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,44 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var designSystem = require('@strapi/design-system');
var constants = require('../../../../constants.js');
var colors = require('../../../../utils/colors.js');
var users = require('../../../../utils/users.js');
const StageColumn = (props)=>{
const { color = constants.STAGE_COLOR_DEFAULT, name } = props.strapi_stage ?? {};
const { themeColorName } = colors.getStageColorByHex(color) ?? {};
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
alignItems: "center",
gap: 2,
maxWidth: "30rem",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
height: 2,
background: color,
borderColor: themeColorName === 'neutral0' ? 'neutral150' : undefined,
hasRadius: true,
shrink: 0,
width: 2
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
fontWeight: "regular",
textColor: "neutral700",
ellipsis: true,
children: name
})
]
});
};
const AssigneeColumn = (props)=>{
const { strapi_assignee: user } = props;
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral800",
children: user ? users.getDisplayName(user) : '-'
});
};
exports.AssigneeColumn = AssigneeColumn;
exports.StageColumn = StageColumn;
//# sourceMappingURL=TableColumns.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TableColumns.js","sources":["../../../../../../admin/src/routes/content-manager/model/components/TableColumns.tsx"],"sourcesContent":["import { SanitizedAdminUser } from '@strapi/admin/strapi-admin';\nimport { Box, Flex, Typography } from '@strapi/design-system';\n\nimport { STAGE_COLOR_DEFAULT } from '../../../../constants';\nimport { getStageColorByHex } from '../../../../utils/colors';\nimport { getDisplayName } from '../../../../utils/users';\n\ninterface StageColumnProps {\n documentId?: string;\n id?: number;\n strapi_stage?: {\n color?: string;\n name: string;\n };\n}\n\nconst StageColumn = (props: StageColumnProps) => {\n const { color = STAGE_COLOR_DEFAULT, name } = props.strapi_stage ?? {};\n const { themeColorName } = getStageColorByHex(color) ?? {};\n\n return (\n <Flex alignItems=\"center\" gap={2} maxWidth=\"30rem\">\n <Box\n height={2}\n background={color}\n borderColor={themeColorName === 'neutral0' ? 'neutral150' : undefined}\n hasRadius\n shrink={0}\n width={2}\n />\n\n <Typography fontWeight=\"regular\" textColor=\"neutral700\" ellipsis>\n {name}\n </Typography>\n </Flex>\n );\n};\n\ninterface AssigneeColumnProps {\n documentId?: string;\n id?: number;\n strapi_assignee?: Pick<\n SanitizedAdminUser,\n 'firstname' | 'lastname' | 'username' | 'email'\n > | null;\n}\n\nconst AssigneeColumn = (props: AssigneeColumnProps) => {\n const { strapi_assignee: user } = props;\n return <Typography textColor=\"neutral800\">{user ? getDisplayName(user) : '-'}</Typography>;\n};\n\nexport { StageColumn, AssigneeColumn };\nexport type { StageColumnProps, AssigneeColumnProps };\n"],"names":["StageColumn","props","color","STAGE_COLOR_DEFAULT","name","strapi_stage","themeColorName","getStageColorByHex","_jsxs","Flex","alignItems","gap","maxWidth","_jsx","Box","height","background","borderColor","undefined","hasRadius","shrink","width","Typography","fontWeight","textColor","ellipsis","AssigneeColumn","strapi_assignee","user","getDisplayName"],"mappings":";;;;;;;;AAgBA,MAAMA,cAAc,CAACC,KAAAA,GAAAA;IACnB,MAAM,EAAEC,KAAQC,GAAAA,6BAAmB,EAAEC,IAAI,EAAE,GAAGH,KAAAA,CAAMI,YAAY,IAAI,EAAC;AACrE,IAAA,MAAM,EAAEC,cAAc,EAAE,GAAGC,yBAAAA,CAAmBL,UAAU,EAAC;AAEzD,IAAA,qBACEM,eAACC,CAAAA,iBAAAA,EAAAA;QAAKC,UAAW,EAAA,QAAA;QAASC,GAAK,EAAA,CAAA;QAAGC,QAAS,EAAA,OAAA;;0BACzCC,cAACC,CAAAA,gBAAAA,EAAAA;gBACCC,MAAQ,EAAA,CAAA;gBACRC,UAAYd,EAAAA,KAAAA;gBACZe,WAAaX,EAAAA,cAAAA,KAAmB,aAAa,YAAeY,GAAAA,SAAAA;gBAC5DC,SAAS,EAAA,IAAA;gBACTC,MAAQ,EAAA,CAAA;gBACRC,KAAO,EAAA;;0BAGTR,cAACS,CAAAA,uBAAAA,EAAAA;gBAAWC,UAAW,EAAA,SAAA;gBAAUC,SAAU,EAAA,YAAA;gBAAaC,QAAQ,EAAA,IAAA;AAC7DrB,gBAAAA,QAAAA,EAAAA;;;;AAIT;AAWA,MAAMsB,iBAAiB,CAACzB,KAAAA,GAAAA;AACtB,IAAA,MAAM,EAAE0B,eAAAA,EAAiBC,IAAI,EAAE,GAAG3B,KAAAA;AAClC,IAAA,qBAAOY,cAACS,CAAAA,uBAAAA,EAAAA;QAAWE,SAAU,EAAA,YAAA;AAAcI,QAAAA,QAAAA,EAAAA,IAAAA,GAAOC,qBAAeD,IAAQ,CAAA,GAAA;;AAC3E;;;;;"}

View File

@@ -0,0 +1,41 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import { Flex, Box, Typography } from '@strapi/design-system';
import { STAGE_COLOR_DEFAULT } from '../../../../constants.mjs';
import { getStageColorByHex } from '../../../../utils/colors.mjs';
import { getDisplayName } from '../../../../utils/users.mjs';
const StageColumn = (props)=>{
const { color = STAGE_COLOR_DEFAULT, name } = props.strapi_stage ?? {};
const { themeColorName } = getStageColorByHex(color) ?? {};
return /*#__PURE__*/ jsxs(Flex, {
alignItems: "center",
gap: 2,
maxWidth: "30rem",
children: [
/*#__PURE__*/ jsx(Box, {
height: 2,
background: color,
borderColor: themeColorName === 'neutral0' ? 'neutral150' : undefined,
hasRadius: true,
shrink: 0,
width: 2
}),
/*#__PURE__*/ jsx(Typography, {
fontWeight: "regular",
textColor: "neutral700",
ellipsis: true,
children: name
})
]
});
};
const AssigneeColumn = (props)=>{
const { strapi_assignee: user } = props;
return /*#__PURE__*/ jsx(Typography, {
textColor: "neutral800",
children: user ? getDisplayName(user) : '-'
});
};
export { AssigneeColumn, StageColumn };
//# sourceMappingURL=TableColumns.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TableColumns.mjs","sources":["../../../../../../admin/src/routes/content-manager/model/components/TableColumns.tsx"],"sourcesContent":["import { SanitizedAdminUser } from '@strapi/admin/strapi-admin';\nimport { Box, Flex, Typography } from '@strapi/design-system';\n\nimport { STAGE_COLOR_DEFAULT } from '../../../../constants';\nimport { getStageColorByHex } from '../../../../utils/colors';\nimport { getDisplayName } from '../../../../utils/users';\n\ninterface StageColumnProps {\n documentId?: string;\n id?: number;\n strapi_stage?: {\n color?: string;\n name: string;\n };\n}\n\nconst StageColumn = (props: StageColumnProps) => {\n const { color = STAGE_COLOR_DEFAULT, name } = props.strapi_stage ?? {};\n const { themeColorName } = getStageColorByHex(color) ?? {};\n\n return (\n <Flex alignItems=\"center\" gap={2} maxWidth=\"30rem\">\n <Box\n height={2}\n background={color}\n borderColor={themeColorName === 'neutral0' ? 'neutral150' : undefined}\n hasRadius\n shrink={0}\n width={2}\n />\n\n <Typography fontWeight=\"regular\" textColor=\"neutral700\" ellipsis>\n {name}\n </Typography>\n </Flex>\n );\n};\n\ninterface AssigneeColumnProps {\n documentId?: string;\n id?: number;\n strapi_assignee?: Pick<\n SanitizedAdminUser,\n 'firstname' | 'lastname' | 'username' | 'email'\n > | null;\n}\n\nconst AssigneeColumn = (props: AssigneeColumnProps) => {\n const { strapi_assignee: user } = props;\n return <Typography textColor=\"neutral800\">{user ? getDisplayName(user) : '-'}</Typography>;\n};\n\nexport { StageColumn, AssigneeColumn };\nexport type { StageColumnProps, AssigneeColumnProps };\n"],"names":["StageColumn","props","color","STAGE_COLOR_DEFAULT","name","strapi_stage","themeColorName","getStageColorByHex","_jsxs","Flex","alignItems","gap","maxWidth","_jsx","Box","height","background","borderColor","undefined","hasRadius","shrink","width","Typography","fontWeight","textColor","ellipsis","AssigneeColumn","strapi_assignee","user","getDisplayName"],"mappings":";;;;;;AAgBA,MAAMA,cAAc,CAACC,KAAAA,GAAAA;IACnB,MAAM,EAAEC,KAAQC,GAAAA,mBAAmB,EAAEC,IAAI,EAAE,GAAGH,KAAAA,CAAMI,YAAY,IAAI,EAAC;AACrE,IAAA,MAAM,EAAEC,cAAc,EAAE,GAAGC,kBAAAA,CAAmBL,UAAU,EAAC;AAEzD,IAAA,qBACEM,IAACC,CAAAA,IAAAA,EAAAA;QAAKC,UAAW,EAAA,QAAA;QAASC,GAAK,EAAA,CAAA;QAAGC,QAAS,EAAA,OAAA;;0BACzCC,GAACC,CAAAA,GAAAA,EAAAA;gBACCC,MAAQ,EAAA,CAAA;gBACRC,UAAYd,EAAAA,KAAAA;gBACZe,WAAaX,EAAAA,cAAAA,KAAmB,aAAa,YAAeY,GAAAA,SAAAA;gBAC5DC,SAAS,EAAA,IAAA;gBACTC,MAAQ,EAAA,CAAA;gBACRC,KAAO,EAAA;;0BAGTR,GAACS,CAAAA,UAAAA,EAAAA;gBAAWC,UAAW,EAAA,SAAA;gBAAUC,SAAU,EAAA,YAAA;gBAAaC,QAAQ,EAAA,IAAA;AAC7DrB,gBAAAA,QAAAA,EAAAA;;;;AAIT;AAWA,MAAMsB,iBAAiB,CAACzB,KAAAA,GAAAA;AACtB,IAAA,MAAM,EAAE0B,eAAAA,EAAiBC,IAAI,EAAE,GAAG3B,KAAAA;AAClC,IAAA,qBAAOY,GAACS,CAAAA,UAAAA,EAAAA;QAAWE,SAAU,EAAA,YAAA;AAAcI,QAAAA,QAAAA,EAAAA,IAAAA,GAAOC,eAAeD,IAAQ,CAAA,GAAA;;AAC3E;;;;"}

View File

@@ -0,0 +1,60 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
require('@strapi/admin/strapi-admin');
require('@strapi/design-system');
require('react-intl');
require('react-router-dom');
require('../../../utils/colors.js');
require('../../../services/settings.js');
var TableColumns = require('./components/TableColumns.js');
var constants = require('./id/components/constants.js');
const REVIEW_WORKFLOW_COLUMNS = [
{
name: constants.STAGE_ATTRIBUTE_NAME,
attribute: {
type: 'relation',
relation: 'oneToMany',
target: 'admin::review-workflow-stage'
},
label: {
id: 'review-workflows.containers.list.table-headers.reviewWorkflows.stage',
defaultMessage: 'Review stage'
},
searchable: false,
sortable: true,
mainField: {
name: 'name',
type: 'string'
},
cellFormatter: (props)=>/*#__PURE__*/ jsxRuntime.jsx(TableColumns.StageColumn, {
...props
})
},
{
name: constants.ASSIGNEE_ATTRIBUTE_NAME,
attribute: {
type: 'relation',
target: 'admin::user',
relation: 'oneToMany'
},
label: {
id: 'review-workflows.containers.list.table-headers.reviewWorkflows.assignee',
defaultMessage: 'Assignee'
},
searchable: false,
sortable: true,
mainField: {
name: 'firstname',
type: 'string'
},
cellFormatter: (props)=>/*#__PURE__*/ jsxRuntime.jsx(TableColumns.AssigneeColumn, {
...props
})
}
];
exports.REVIEW_WORKFLOW_COLUMNS = REVIEW_WORKFLOW_COLUMNS;
//# sourceMappingURL=constants.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.js","sources":["../../../../../admin/src/routes/content-manager/model/constants.tsx"],"sourcesContent":["import { AssigneeFilter } from './components/AssigneeFilter';\nimport { StageFilter } from './components/StageFilter';\nimport { AssigneeColumn, StageColumn } from './components/TableColumns';\nimport { ASSIGNEE_ATTRIBUTE_NAME, STAGE_ATTRIBUTE_NAME } from './id/components/constants';\n\nimport type { Filters } from '@strapi/admin/strapi-admin';\nimport type { ListFieldLayout } from '@strapi/content-manager/strapi-admin';\nimport type { MessageDescriptor } from 'react-intl';\n\nexport const REVIEW_WORKFLOW_COLUMNS = [\n {\n name: STAGE_ATTRIBUTE_NAME,\n attribute: {\n type: 'relation',\n relation: 'oneToMany',\n target: 'admin::review-workflow-stage',\n },\n label: {\n id: 'review-workflows.containers.list.table-headers.reviewWorkflows.stage',\n defaultMessage: 'Review stage',\n },\n searchable: false,\n sortable: true,\n mainField: {\n name: 'name',\n type: 'string',\n },\n cellFormatter: (props) => <StageColumn {...props} />,\n },\n {\n name: ASSIGNEE_ATTRIBUTE_NAME,\n attribute: {\n type: 'relation',\n target: 'admin::user',\n relation: 'oneToMany',\n },\n label: {\n id: 'review-workflows.containers.list.table-headers.reviewWorkflows.assignee',\n defaultMessage: 'Assignee',\n },\n searchable: false,\n sortable: true,\n mainField: {\n name: 'firstname',\n type: 'string',\n },\n cellFormatter: (props) => <AssigneeColumn {...props} />,\n },\n] satisfies Array<Omit<ListFieldLayout, 'label'> & { label: MessageDescriptor }>;\n\nexport const REVIEW_WORKFLOW_FILTERS = [\n {\n mainField: {\n name: 'name',\n type: 'string',\n },\n input: StageFilter,\n label: {\n id: 'review-workflows.containers.list.table-headers.reviewWorkflows.stage',\n defaultMessage: 'Review stage',\n },\n name: 'strapi_stage',\n type: 'relation',\n },\n\n {\n type: 'relation',\n mainField: {\n name: 'id',\n type: 'integer',\n },\n input: AssigneeFilter,\n operators: [\n {\n label: {\n id: 'components.FilterOptions.FILTER_TYPES.$eq',\n defaultMessage: 'is',\n },\n value: '$eq',\n },\n {\n label: {\n id: 'components.FilterOptions.FILTER_TYPES.$ne',\n defaultMessage: 'is not',\n },\n value: '$ne',\n },\n ],\n label: {\n id: 'review-workflows.containers.list.table-headers.reviewWorkflows.assignee.label',\n defaultMessage: 'Assignee',\n },\n name: 'strapi_assignee',\n },\n] satisfies Array<\n Omit<Filters.Filter, 'label' | 'operators'> & {\n label: MessageDescriptor;\n operators?: Array<{ value: string; label: MessageDescriptor }>;\n }\n>;\n"],"names":["REVIEW_WORKFLOW_COLUMNS","name","STAGE_ATTRIBUTE_NAME","attribute","type","relation","target","label","id","defaultMessage","searchable","sortable","mainField","cellFormatter","props","_jsx","StageColumn","ASSIGNEE_ATTRIBUTE_NAME","AssigneeColumn"],"mappings":";;;;;;;;;;;;;MASaA,uBAA0B,GAAA;AACrC,IAAA;QACEC,IAAMC,EAAAA,8BAAAA;QACNC,SAAW,EAAA;YACTC,IAAM,EAAA,UAAA;YACNC,QAAU,EAAA,WAAA;YACVC,MAAQ,EAAA;AACV,SAAA;QACAC,KAAO,EAAA;YACLC,EAAI,EAAA,sEAAA;YACJC,cAAgB,EAAA;AAClB,SAAA;QACAC,UAAY,EAAA,KAAA;QACZC,QAAU,EAAA,IAAA;QACVC,SAAW,EAAA;YACTX,IAAM,EAAA,MAAA;YACNG,IAAM,EAAA;AACR,SAAA;QACAS,aAAe,EAAA,CAACC,sBAAUC,cAACC,CAAAA,wBAAAA,EAAAA;AAAa,gBAAA,GAAGF;;AAC7C,KAAA;AACA,IAAA;QACEb,IAAMgB,EAAAA,iCAAAA;QACNd,SAAW,EAAA;YACTC,IAAM,EAAA,UAAA;YACNE,MAAQ,EAAA,aAAA;YACRD,QAAU,EAAA;AACZ,SAAA;QACAE,KAAO,EAAA;YACLC,EAAI,EAAA,yEAAA;YACJC,cAAgB,EAAA;AAClB,SAAA;QACAC,UAAY,EAAA,KAAA;QACZC,QAAU,EAAA,IAAA;QACVC,SAAW,EAAA;YACTX,IAAM,EAAA,WAAA;YACNG,IAAM,EAAA;AACR,SAAA;QACAS,aAAe,EAAA,CAACC,sBAAUC,cAACG,CAAAA,2BAAAA,EAAAA;AAAgB,gBAAA,GAAGJ;;AAChD;;;;;"}

View File

@@ -0,0 +1,58 @@
import { jsx } from 'react/jsx-runtime';
import 'react';
import '@strapi/admin/strapi-admin';
import '@strapi/design-system';
import 'react-intl';
import 'react-router-dom';
import '../../../utils/colors.mjs';
import '../../../services/settings.mjs';
import { StageColumn, AssigneeColumn } from './components/TableColumns.mjs';
import { STAGE_ATTRIBUTE_NAME, ASSIGNEE_ATTRIBUTE_NAME } from './id/components/constants.mjs';
const REVIEW_WORKFLOW_COLUMNS = [
{
name: STAGE_ATTRIBUTE_NAME,
attribute: {
type: 'relation',
relation: 'oneToMany',
target: 'admin::review-workflow-stage'
},
label: {
id: 'review-workflows.containers.list.table-headers.reviewWorkflows.stage',
defaultMessage: 'Review stage'
},
searchable: false,
sortable: true,
mainField: {
name: 'name',
type: 'string'
},
cellFormatter: (props)=>/*#__PURE__*/ jsx(StageColumn, {
...props
})
},
{
name: ASSIGNEE_ATTRIBUTE_NAME,
attribute: {
type: 'relation',
target: 'admin::user',
relation: 'oneToMany'
},
label: {
id: 'review-workflows.containers.list.table-headers.reviewWorkflows.assignee',
defaultMessage: 'Assignee'
},
searchable: false,
sortable: true,
mainField: {
name: 'firstname',
type: 'string'
},
cellFormatter: (props)=>/*#__PURE__*/ jsx(AssigneeColumn, {
...props
})
}
];
export { REVIEW_WORKFLOW_COLUMNS };
//# sourceMappingURL=constants.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.mjs","sources":["../../../../../admin/src/routes/content-manager/model/constants.tsx"],"sourcesContent":["import { AssigneeFilter } from './components/AssigneeFilter';\nimport { StageFilter } from './components/StageFilter';\nimport { AssigneeColumn, StageColumn } from './components/TableColumns';\nimport { ASSIGNEE_ATTRIBUTE_NAME, STAGE_ATTRIBUTE_NAME } from './id/components/constants';\n\nimport type { Filters } from '@strapi/admin/strapi-admin';\nimport type { ListFieldLayout } from '@strapi/content-manager/strapi-admin';\nimport type { MessageDescriptor } from 'react-intl';\n\nexport const REVIEW_WORKFLOW_COLUMNS = [\n {\n name: STAGE_ATTRIBUTE_NAME,\n attribute: {\n type: 'relation',\n relation: 'oneToMany',\n target: 'admin::review-workflow-stage',\n },\n label: {\n id: 'review-workflows.containers.list.table-headers.reviewWorkflows.stage',\n defaultMessage: 'Review stage',\n },\n searchable: false,\n sortable: true,\n mainField: {\n name: 'name',\n type: 'string',\n },\n cellFormatter: (props) => <StageColumn {...props} />,\n },\n {\n name: ASSIGNEE_ATTRIBUTE_NAME,\n attribute: {\n type: 'relation',\n target: 'admin::user',\n relation: 'oneToMany',\n },\n label: {\n id: 'review-workflows.containers.list.table-headers.reviewWorkflows.assignee',\n defaultMessage: 'Assignee',\n },\n searchable: false,\n sortable: true,\n mainField: {\n name: 'firstname',\n type: 'string',\n },\n cellFormatter: (props) => <AssigneeColumn {...props} />,\n },\n] satisfies Array<Omit<ListFieldLayout, 'label'> & { label: MessageDescriptor }>;\n\nexport const REVIEW_WORKFLOW_FILTERS = [\n {\n mainField: {\n name: 'name',\n type: 'string',\n },\n input: StageFilter,\n label: {\n id: 'review-workflows.containers.list.table-headers.reviewWorkflows.stage',\n defaultMessage: 'Review stage',\n },\n name: 'strapi_stage',\n type: 'relation',\n },\n\n {\n type: 'relation',\n mainField: {\n name: 'id',\n type: 'integer',\n },\n input: AssigneeFilter,\n operators: [\n {\n label: {\n id: 'components.FilterOptions.FILTER_TYPES.$eq',\n defaultMessage: 'is',\n },\n value: '$eq',\n },\n {\n label: {\n id: 'components.FilterOptions.FILTER_TYPES.$ne',\n defaultMessage: 'is not',\n },\n value: '$ne',\n },\n ],\n label: {\n id: 'review-workflows.containers.list.table-headers.reviewWorkflows.assignee.label',\n defaultMessage: 'Assignee',\n },\n name: 'strapi_assignee',\n },\n] satisfies Array<\n Omit<Filters.Filter, 'label' | 'operators'> & {\n label: MessageDescriptor;\n operators?: Array<{ value: string; label: MessageDescriptor }>;\n }\n>;\n"],"names":["REVIEW_WORKFLOW_COLUMNS","name","STAGE_ATTRIBUTE_NAME","attribute","type","relation","target","label","id","defaultMessage","searchable","sortable","mainField","cellFormatter","props","_jsx","StageColumn","ASSIGNEE_ATTRIBUTE_NAME","AssigneeColumn"],"mappings":";;;;;;;;;;;MASaA,uBAA0B,GAAA;AACrC,IAAA;QACEC,IAAMC,EAAAA,oBAAAA;QACNC,SAAW,EAAA;YACTC,IAAM,EAAA,UAAA;YACNC,QAAU,EAAA,WAAA;YACVC,MAAQ,EAAA;AACV,SAAA;QACAC,KAAO,EAAA;YACLC,EAAI,EAAA,sEAAA;YACJC,cAAgB,EAAA;AAClB,SAAA;QACAC,UAAY,EAAA,KAAA;QACZC,QAAU,EAAA,IAAA;QACVC,SAAW,EAAA;YACTX,IAAM,EAAA,MAAA;YACNG,IAAM,EAAA;AACR,SAAA;QACAS,aAAe,EAAA,CAACC,sBAAUC,GAACC,CAAAA,WAAAA,EAAAA;AAAa,gBAAA,GAAGF;;AAC7C,KAAA;AACA,IAAA;QACEb,IAAMgB,EAAAA,uBAAAA;QACNd,SAAW,EAAA;YACTC,IAAM,EAAA,UAAA;YACNE,MAAQ,EAAA,aAAA;YACRD,QAAU,EAAA;AACZ,SAAA;QACAE,KAAO,EAAA;YACLC,EAAI,EAAA,yEAAA;YACJC,cAAgB,EAAA;AAClB,SAAA;QACAC,UAAY,EAAA,KAAA;QACZC,QAAU,EAAA,IAAA;QACVC,SAAW,EAAA;YACTX,IAAM,EAAA,WAAA;YACNG,IAAM,EAAA;AACR,SAAA;QACAS,aAAe,EAAA,CAACC,sBAAUC,GAACG,CAAAA,cAAAA,EAAAA;AAAgB,gBAAA,GAAGJ;;AAChD;;;;;"}

View File

@@ -0,0 +1,169 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var strapiAdmin$1 = require('@strapi/content-manager/strapi-admin');
var designSystem = require('@strapi/design-system');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var hooks = require('../../../../../modules/hooks.js');
var contentManager = require('../../../../../services/content-manager.js');
var api = require('../../../../../utils/api.js');
var users = require('../../../../../utils/users.js');
var constants = require('./constants.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 AssigneeSelect = ({ isCompact })=>{
const { collectionType = '', id, slug: model = '' } = reactRouterDom.useParams();
const permissions = hooks.useTypedSelector((state)=>state.admin_app.permissions);
const { formatMessage } = reactIntl.useIntl();
const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
const { toggleNotification } = strapiAdmin.useNotification();
const { allowedActions: { canRead }, isLoading: isLoadingPermissions } = strapiAdmin.useRBAC(permissions.settings?.users);
const [{ query }] = strapiAdmin.useQueryParams();
const params = React__namespace.useMemo(()=>api.buildValidParams(query), [
query
]);
const { data, isLoading: isLoadingUsers, isError } = strapiAdmin.useAdminUsers(undefined, {
skip: isLoadingPermissions || !canRead
});
const { document } = strapiAdmin$1.unstable_useDocument({
collectionType,
model,
documentId: id
}, {
skip: !id && collectionType !== 'single-types'
});
const users$1 = data?.users || [];
const currentAssignee = document ? document[constants.ASSIGNEE_ATTRIBUTE_NAME] : null;
const [updateAssignee, { error, isLoading: isMutating }] = contentManager.useUpdateAssigneeMutation();
if (!collectionType || !model || !document?.documentId) {
return null;
}
const handleChange = async (assigneeId)=>{
// a simple way to avoid erroneous updates
if (currentAssignee?.id === assigneeId) {
return;
}
const res = await updateAssignee({
slug: collectionType,
model,
id: document.documentId,
params,
data: {
id: assigneeId ? parseInt(assigneeId, 10) : null
}
});
if ('data' in res) {
toggleNotification({
type: 'success',
message: formatMessage({
id: 'content-manager.reviewWorkflows.assignee.notification.saved',
defaultMessage: 'Assignee updated'
})
});
}
if (isCompact && 'error' in res) {
toggleNotification({
type: 'danger',
message: formatAPIError(res.error)
});
}
};
const isDisabled = !isLoadingPermissions && !isLoadingUsers && users$1.length === 0 || !document.documentId;
const isLoading = isLoadingUsers || isLoadingPermissions || isMutating;
const assigneeLabel = formatMessage({
id: 'content-manager.reviewWorkflows.assignee.label',
defaultMessage: 'Assignee'
});
const assigneeClearLabel = formatMessage({
id: 'content-manager.reviewWorkflows.assignee.clear',
defaultMessage: 'Clear assignee'
});
const assigneePlaceholder = formatMessage({
id: 'content-manager.reviewWorkflows.assignee.placeholder',
defaultMessage: 'Select…'
});
if (isCompact) {
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
name: constants.ASSIGNEE_ATTRIBUTE_NAME,
id: constants.ASSIGNEE_ATTRIBUTE_NAME,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.VisuallyHidden, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: assigneeLabel
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Combobox, {
clearLabel: assigneeClearLabel,
disabled: isDisabled,
value: currentAssignee ? currentAssignee.id.toString() : null,
onChange: handleChange,
onClear: ()=>handleChange(null),
placeholder: assigneePlaceholder,
loading: isLoading || isLoadingPermissions || isMutating,
size: "S",
children: users$1.map((user)=>{
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.ComboboxOption, {
value: user.id.toString(),
textValue: users.getDisplayName(user),
children: users.getDisplayName(user)
}, user.id);
})
})
]
});
}
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
name: constants.ASSIGNEE_ATTRIBUTE_NAME,
id: constants.ASSIGNEE_ATTRIBUTE_NAME,
error: (isError && canRead && formatMessage({
id: 'content-manager.reviewWorkflows.assignee.error',
defaultMessage: 'An error occurred while fetching users'
}) || error && formatAPIError(error)) ?? undefined,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: assigneeLabel
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Combobox, {
clearLabel: assigneeClearLabel,
disabled: !isLoadingPermissions && !isLoading && users$1.length === 0 || !document.documentId,
value: currentAssignee ? currentAssignee.id.toString() : null,
onChange: handleChange,
onClear: ()=>handleChange(null),
placeholder: assigneePlaceholder,
loading: isLoading || isLoadingPermissions || isMutating,
children: users$1.map((user)=>{
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.ComboboxOption, {
value: user.id.toString(),
textValue: users.getDisplayName(user),
children: users.getDisplayName(user)
}, user.id);
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
]
});
};
exports.AssigneeSelect = AssigneeSelect;
//# sourceMappingURL=AssigneeSelect.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,148 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import * as React from 'react';
import { useAPIErrorHandler, useNotification, useRBAC, useQueryParams, useAdminUsers } from '@strapi/admin/strapi-admin';
import { unstable_useDocument } from '@strapi/content-manager/strapi-admin';
import { Field, VisuallyHidden, Combobox, ComboboxOption } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import { useParams } from 'react-router-dom';
import { useTypedSelector } from '../../../../../modules/hooks.mjs';
import { useUpdateAssigneeMutation } from '../../../../../services/content-manager.mjs';
import { buildValidParams } from '../../../../../utils/api.mjs';
import { getDisplayName } from '../../../../../utils/users.mjs';
import { ASSIGNEE_ATTRIBUTE_NAME } from './constants.mjs';
const AssigneeSelect = ({ isCompact })=>{
const { collectionType = '', id, slug: model = '' } = useParams();
const permissions = useTypedSelector((state)=>state.admin_app.permissions);
const { formatMessage } = useIntl();
const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
const { toggleNotification } = useNotification();
const { allowedActions: { canRead }, isLoading: isLoadingPermissions } = useRBAC(permissions.settings?.users);
const [{ query }] = useQueryParams();
const params = React.useMemo(()=>buildValidParams(query), [
query
]);
const { data, isLoading: isLoadingUsers, isError } = useAdminUsers(undefined, {
skip: isLoadingPermissions || !canRead
});
const { document } = unstable_useDocument({
collectionType,
model,
documentId: id
}, {
skip: !id && collectionType !== 'single-types'
});
const users = data?.users || [];
const currentAssignee = document ? document[ASSIGNEE_ATTRIBUTE_NAME] : null;
const [updateAssignee, { error, isLoading: isMutating }] = useUpdateAssigneeMutation();
if (!collectionType || !model || !document?.documentId) {
return null;
}
const handleChange = async (assigneeId)=>{
// a simple way to avoid erroneous updates
if (currentAssignee?.id === assigneeId) {
return;
}
const res = await updateAssignee({
slug: collectionType,
model,
id: document.documentId,
params,
data: {
id: assigneeId ? parseInt(assigneeId, 10) : null
}
});
if ('data' in res) {
toggleNotification({
type: 'success',
message: formatMessage({
id: 'content-manager.reviewWorkflows.assignee.notification.saved',
defaultMessage: 'Assignee updated'
})
});
}
if (isCompact && 'error' in res) {
toggleNotification({
type: 'danger',
message: formatAPIError(res.error)
});
}
};
const isDisabled = !isLoadingPermissions && !isLoadingUsers && users.length === 0 || !document.documentId;
const isLoading = isLoadingUsers || isLoadingPermissions || isMutating;
const assigneeLabel = formatMessage({
id: 'content-manager.reviewWorkflows.assignee.label',
defaultMessage: 'Assignee'
});
const assigneeClearLabel = formatMessage({
id: 'content-manager.reviewWorkflows.assignee.clear',
defaultMessage: 'Clear assignee'
});
const assigneePlaceholder = formatMessage({
id: 'content-manager.reviewWorkflows.assignee.placeholder',
defaultMessage: 'Select…'
});
if (isCompact) {
return /*#__PURE__*/ jsxs(Field.Root, {
name: ASSIGNEE_ATTRIBUTE_NAME,
id: ASSIGNEE_ATTRIBUTE_NAME,
children: [
/*#__PURE__*/ jsx(VisuallyHidden, {
children: /*#__PURE__*/ jsx(Field.Label, {
children: assigneeLabel
})
}),
/*#__PURE__*/ jsx(Combobox, {
clearLabel: assigneeClearLabel,
disabled: isDisabled,
value: currentAssignee ? currentAssignee.id.toString() : null,
onChange: handleChange,
onClear: ()=>handleChange(null),
placeholder: assigneePlaceholder,
loading: isLoading || isLoadingPermissions || isMutating,
size: "S",
children: users.map((user)=>{
return /*#__PURE__*/ jsx(ComboboxOption, {
value: user.id.toString(),
textValue: getDisplayName(user),
children: getDisplayName(user)
}, user.id);
})
})
]
});
}
return /*#__PURE__*/ jsxs(Field.Root, {
name: ASSIGNEE_ATTRIBUTE_NAME,
id: ASSIGNEE_ATTRIBUTE_NAME,
error: (isError && canRead && formatMessage({
id: 'content-manager.reviewWorkflows.assignee.error',
defaultMessage: 'An error occurred while fetching users'
}) || error && formatAPIError(error)) ?? undefined,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: assigneeLabel
}),
/*#__PURE__*/ jsx(Combobox, {
clearLabel: assigneeClearLabel,
disabled: !isLoadingPermissions && !isLoading && users.length === 0 || !document.documentId,
value: currentAssignee ? currentAssignee.id.toString() : null,
onChange: handleChange,
onClear: ()=>handleChange(null),
placeholder: assigneePlaceholder,
loading: isLoading || isLoadingPermissions || isMutating,
children: users.map((user)=>{
return /*#__PURE__*/ jsx(ComboboxOption, {
value: user.id.toString(),
textValue: getDisplayName(user),
children: getDisplayName(user)
}, user.id);
})
}),
/*#__PURE__*/ jsx(Field.Error, {})
]
});
};
export { AssigneeSelect };
//# sourceMappingURL=AssigneeSelect.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,31 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var strapiAdmin = require('@strapi/content-manager/strapi-admin');
var designSystem = require('@strapi/design-system');
var reactRouterDom = require('react-router-dom');
var AssigneeSelect = require('./AssigneeSelect.js');
var StageSelect = require('./StageSelect.js');
const Header = ()=>{
const { slug = '', id, collectionType } = reactRouterDom.useParams();
const { edit: { options } } = strapiAdmin.unstable_useDocumentLayout(slug);
if (!window.strapi.isEE || !options?.reviewWorkflows || collectionType !== 'single-types' && !id || id === 'create') {
return null;
}
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(AssigneeSelect.AssigneeSelect, {
isCompact: true
}),
/*#__PURE__*/ jsxRuntime.jsx(StageSelect.StageSelect, {
isCompact: true
})
]
});
};
Header.type = 'preview';
exports.Header = Header;
//# sourceMappingURL=Header.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Header.js","sources":["../../../../../../../admin/src/routes/content-manager/model/id/components/Header.tsx"],"sourcesContent":["import { unstable_useDocumentLayout as useDocumentLayout } from '@strapi/content-manager/strapi-admin';\nimport { Flex } from '@strapi/design-system';\nimport { useParams } from 'react-router-dom';\n\nimport { AssigneeSelect } from './AssigneeSelect';\nimport { StageSelect } from './StageSelect';\n\nconst Header = () => {\n const {\n slug = '',\n id,\n collectionType,\n } = useParams<{\n collectionType: string;\n slug: string;\n id: string;\n }>();\n\n const {\n edit: { options },\n } = useDocumentLayout(slug);\n\n if (\n !window.strapi.isEE ||\n !options?.reviewWorkflows ||\n (collectionType !== 'single-types' && !id) ||\n id === 'create'\n ) {\n return null;\n }\n\n return (\n <Flex gap={2}>\n <AssigneeSelect isCompact />\n <StageSelect isCompact />\n </Flex>\n );\n};\n\nHeader.type = 'preview';\n\nexport { Header };\n"],"names":["Header","slug","id","collectionType","useParams","edit","options","useDocumentLayout","window","strapi","isEE","reviewWorkflows","_jsxs","Flex","gap","_jsx","AssigneeSelect","isCompact","StageSelect","type"],"mappings":";;;;;;;;;AAOA,MAAMA,MAAS,GAAA,IAAA;IACb,MAAM,EACJC,OAAO,EAAE,EACTC,EAAE,EACFC,cAAc,EACf,GAAGC,wBAAAA,EAAAA;AAMJ,IAAA,MAAM,EACJC,IAAM,EAAA,EAAEC,OAAO,EAAE,EAClB,GAAGC,sCAAkBN,CAAAA,IAAAA,CAAAA;AAEtB,IAAA,IACE,CAACO,MAAAA,CAAOC,MAAM,CAACC,IAAI,IACnB,CAACJ,OAASK,EAAAA,eAAAA,IACTR,cAAmB,KAAA,cAAA,IAAkB,CAACD,EAAAA,IACvCA,OAAO,QACP,EAAA;QACA,OAAO,IAAA;AACT;AAEA,IAAA,qBACEU,eAACC,CAAAA,iBAAAA,EAAAA;QAAKC,GAAK,EAAA,CAAA;;0BACTC,cAACC,CAAAA,6BAAAA,EAAAA;gBAAeC,SAAS,EAAA;;0BACzBF,cAACG,CAAAA,uBAAAA,EAAAA;gBAAYD,SAAS,EAAA;;;;AAG5B;AAEAjB,MAAAA,CAAOmB,IAAI,GAAG,SAAA;;;;"}

View File

@@ -0,0 +1,29 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import { unstable_useDocumentLayout } from '@strapi/content-manager/strapi-admin';
import { Flex } from '@strapi/design-system';
import { useParams } from 'react-router-dom';
import { AssigneeSelect } from './AssigneeSelect.mjs';
import { StageSelect } from './StageSelect.mjs';
const Header = ()=>{
const { slug = '', id, collectionType } = useParams();
const { edit: { options } } = unstable_useDocumentLayout(slug);
if (!window.strapi.isEE || !options?.reviewWorkflows || collectionType !== 'single-types' && !id || id === 'create') {
return null;
}
return /*#__PURE__*/ jsxs(Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsx(AssigneeSelect, {
isCompact: true
}),
/*#__PURE__*/ jsx(StageSelect, {
isCompact: true
})
]
});
};
Header.type = 'preview';
export { Header };
//# sourceMappingURL=Header.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Header.mjs","sources":["../../../../../../../admin/src/routes/content-manager/model/id/components/Header.tsx"],"sourcesContent":["import { unstable_useDocumentLayout as useDocumentLayout } from '@strapi/content-manager/strapi-admin';\nimport { Flex } from '@strapi/design-system';\nimport { useParams } from 'react-router-dom';\n\nimport { AssigneeSelect } from './AssigneeSelect';\nimport { StageSelect } from './StageSelect';\n\nconst Header = () => {\n const {\n slug = '',\n id,\n collectionType,\n } = useParams<{\n collectionType: string;\n slug: string;\n id: string;\n }>();\n\n const {\n edit: { options },\n } = useDocumentLayout(slug);\n\n if (\n !window.strapi.isEE ||\n !options?.reviewWorkflows ||\n (collectionType !== 'single-types' && !id) ||\n id === 'create'\n ) {\n return null;\n }\n\n return (\n <Flex gap={2}>\n <AssigneeSelect isCompact />\n <StageSelect isCompact />\n </Flex>\n );\n};\n\nHeader.type = 'preview';\n\nexport { Header };\n"],"names":["Header","slug","id","collectionType","useParams","edit","options","useDocumentLayout","window","strapi","isEE","reviewWorkflows","_jsxs","Flex","gap","_jsx","AssigneeSelect","isCompact","StageSelect","type"],"mappings":";;;;;;;AAOA,MAAMA,MAAS,GAAA,IAAA;IACb,MAAM,EACJC,OAAO,EAAE,EACTC,EAAE,EACFC,cAAc,EACf,GAAGC,SAAAA,EAAAA;AAMJ,IAAA,MAAM,EACJC,IAAM,EAAA,EAAEC,OAAO,EAAE,EAClB,GAAGC,0BAAkBN,CAAAA,IAAAA,CAAAA;AAEtB,IAAA,IACE,CAACO,MAAAA,CAAOC,MAAM,CAACC,IAAI,IACnB,CAACJ,OAASK,EAAAA,eAAAA,IACTR,cAAmB,KAAA,cAAA,IAAkB,CAACD,EAAAA,IACvCA,OAAO,QACP,EAAA;QACA,OAAO,IAAA;AACT;AAEA,IAAA,qBACEU,IAACC,CAAAA,IAAAA,EAAAA;QAAKC,GAAK,EAAA,CAAA;;0BACTC,GAACC,CAAAA,cAAAA,EAAAA;gBAAeC,SAAS,EAAA;;0BACzBF,GAACG,CAAAA,WAAAA,EAAAA;gBAAYD,SAAS,EAAA;;;;AAG5B;AAEAjB,MAAAA,CAAOmB,IAAI,GAAG,SAAA;;;;"}

View File

@@ -0,0 +1,39 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var strapiAdmin = require('@strapi/content-manager/strapi-admin');
var designSystem = require('@strapi/design-system');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var AssigneeSelect = require('./AssigneeSelect.js');
var StageSelect = require('./StageSelect.js');
const Panel = ()=>{
const { slug = '', id, collectionType } = reactRouterDom.useParams();
const { edit: { options } } = strapiAdmin.unstable_useDocumentLayout(slug);
const { formatMessage } = reactIntl.useIntl();
if (!window.strapi.isEE || !options?.reviewWorkflows || collectionType !== 'single-types' && !id || id === 'create') {
return null;
}
return {
title: formatMessage({
id: 'content-manager.containers.edit.panels.review-workflows.title',
defaultMessage: 'Review Workflows'
}),
content: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
gap: 2,
alignItems: "stretch",
width: "100%",
children: [
/*#__PURE__*/ jsxRuntime.jsx(AssigneeSelect.AssigneeSelect, {}),
/*#__PURE__*/ jsxRuntime.jsx(StageSelect.StageSelect, {})
]
})
};
};
// @ts-expect-error this is fine, we like to label the core panels / actions.
Panel.type = 'review-workflows';
exports.Panel = Panel;
//# sourceMappingURL=Panel.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Panel.js","sources":["../../../../../../../admin/src/routes/content-manager/model/id/components/Panel.tsx"],"sourcesContent":["import { unstable_useDocumentLayout as useDocumentLayout } from '@strapi/content-manager/strapi-admin';\nimport { Flex } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\nimport { useParams } from 'react-router-dom';\n\nimport { AssigneeSelect } from './AssigneeSelect';\nimport { StageSelect } from './StageSelect';\n\nimport type { PanelComponent } from '@strapi/content-manager/strapi-admin';\n\nconst Panel: PanelComponent = () => {\n const {\n slug = '',\n id,\n collectionType,\n } = useParams<{\n collectionType: string;\n slug: string;\n id: string;\n }>();\n\n const {\n edit: { options },\n } = useDocumentLayout(slug);\n const { formatMessage } = useIntl();\n\n if (\n !window.strapi.isEE ||\n !options?.reviewWorkflows ||\n (collectionType !== 'single-types' && !id) ||\n id === 'create'\n ) {\n return null;\n }\n\n return {\n title: formatMessage({\n id: 'content-manager.containers.edit.panels.review-workflows.title',\n defaultMessage: 'Review Workflows',\n }),\n content: (\n <Flex direction=\"column\" gap={2} alignItems=\"stretch\" width=\"100%\">\n <AssigneeSelect />\n <StageSelect />\n </Flex>\n ),\n };\n};\n\n// @ts-expect-error this is fine, we like to label the core panels / actions.\nPanel.type = 'review-workflows';\n\nexport { Panel };\n"],"names":["Panel","slug","id","collectionType","useParams","edit","options","useDocumentLayout","formatMessage","useIntl","window","strapi","isEE","reviewWorkflows","title","defaultMessage","content","_jsxs","Flex","direction","gap","alignItems","width","_jsx","AssigneeSelect","StageSelect","type"],"mappings":";;;;;;;;;;AAUA,MAAMA,KAAwB,GAAA,IAAA;IAC5B,MAAM,EACJC,OAAO,EAAE,EACTC,EAAE,EACFC,cAAc,EACf,GAAGC,wBAAAA,EAAAA;AAMJ,IAAA,MAAM,EACJC,IAAM,EAAA,EAAEC,OAAO,EAAE,EAClB,GAAGC,sCAAkBN,CAAAA,IAAAA,CAAAA;IACtB,MAAM,EAAEO,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;AAE1B,IAAA,IACE,CAACC,MAAAA,CAAOC,MAAM,CAACC,IAAI,IACnB,CAACN,OAASO,EAAAA,eAAAA,IACTV,cAAmB,KAAA,cAAA,IAAkB,CAACD,EAAAA,IACvCA,OAAO,QACP,EAAA;QACA,OAAO,IAAA;AACT;IAEA,OAAO;AACLY,QAAAA,KAAAA,EAAON,aAAc,CAAA;YACnBN,EAAI,EAAA,+DAAA;YACJa,cAAgB,EAAA;AAClB,SAAA,CAAA;AACAC,QAAAA,OAAAA,gBACEC,eAACC,CAAAA,iBAAAA,EAAAA;YAAKC,SAAU,EAAA,QAAA;YAASC,GAAK,EAAA,CAAA;YAAGC,UAAW,EAAA,SAAA;YAAUC,KAAM,EAAA,MAAA;;8BAC1DC,cAACC,CAAAA,6BAAAA,EAAAA,EAAAA,CAAAA;8BACDD,cAACE,CAAAA,uBAAAA,EAAAA,EAAAA;;;AAGP,KAAA;AACF;AAEA;AACAzB,KAAAA,CAAM0B,IAAI,GAAG,kBAAA;;;;"}

View File

@@ -0,0 +1,37 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import { unstable_useDocumentLayout } from '@strapi/content-manager/strapi-admin';
import { Flex } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import { useParams } from 'react-router-dom';
import { AssigneeSelect } from './AssigneeSelect.mjs';
import { StageSelect } from './StageSelect.mjs';
const Panel = ()=>{
const { slug = '', id, collectionType } = useParams();
const { edit: { options } } = unstable_useDocumentLayout(slug);
const { formatMessage } = useIntl();
if (!window.strapi.isEE || !options?.reviewWorkflows || collectionType !== 'single-types' && !id || id === 'create') {
return null;
}
return {
title: formatMessage({
id: 'content-manager.containers.edit.panels.review-workflows.title',
defaultMessage: 'Review Workflows'
}),
content: /*#__PURE__*/ jsxs(Flex, {
direction: "column",
gap: 2,
alignItems: "stretch",
width: "100%",
children: [
/*#__PURE__*/ jsx(AssigneeSelect, {}),
/*#__PURE__*/ jsx(StageSelect, {})
]
})
};
};
// @ts-expect-error this is fine, we like to label the core panels / actions.
Panel.type = 'review-workflows';
export { Panel };
//# sourceMappingURL=Panel.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Panel.mjs","sources":["../../../../../../../admin/src/routes/content-manager/model/id/components/Panel.tsx"],"sourcesContent":["import { unstable_useDocumentLayout as useDocumentLayout } from '@strapi/content-manager/strapi-admin';\nimport { Flex } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\nimport { useParams } from 'react-router-dom';\n\nimport { AssigneeSelect } from './AssigneeSelect';\nimport { StageSelect } from './StageSelect';\n\nimport type { PanelComponent } from '@strapi/content-manager/strapi-admin';\n\nconst Panel: PanelComponent = () => {\n const {\n slug = '',\n id,\n collectionType,\n } = useParams<{\n collectionType: string;\n slug: string;\n id: string;\n }>();\n\n const {\n edit: { options },\n } = useDocumentLayout(slug);\n const { formatMessage } = useIntl();\n\n if (\n !window.strapi.isEE ||\n !options?.reviewWorkflows ||\n (collectionType !== 'single-types' && !id) ||\n id === 'create'\n ) {\n return null;\n }\n\n return {\n title: formatMessage({\n id: 'content-manager.containers.edit.panels.review-workflows.title',\n defaultMessage: 'Review Workflows',\n }),\n content: (\n <Flex direction=\"column\" gap={2} alignItems=\"stretch\" width=\"100%\">\n <AssigneeSelect />\n <StageSelect />\n </Flex>\n ),\n };\n};\n\n// @ts-expect-error this is fine, we like to label the core panels / actions.\nPanel.type = 'review-workflows';\n\nexport { Panel };\n"],"names":["Panel","slug","id","collectionType","useParams","edit","options","useDocumentLayout","formatMessage","useIntl","window","strapi","isEE","reviewWorkflows","title","defaultMessage","content","_jsxs","Flex","direction","gap","alignItems","width","_jsx","AssigneeSelect","StageSelect","type"],"mappings":";;;;;;;;AAUA,MAAMA,KAAwB,GAAA,IAAA;IAC5B,MAAM,EACJC,OAAO,EAAE,EACTC,EAAE,EACFC,cAAc,EACf,GAAGC,SAAAA,EAAAA;AAMJ,IAAA,MAAM,EACJC,IAAM,EAAA,EAAEC,OAAO,EAAE,EAClB,GAAGC,0BAAkBN,CAAAA,IAAAA,CAAAA;IACtB,MAAM,EAAEO,aAAa,EAAE,GAAGC,OAAAA,EAAAA;AAE1B,IAAA,IACE,CAACC,MAAAA,CAAOC,MAAM,CAACC,IAAI,IACnB,CAACN,OAASO,EAAAA,eAAAA,IACTV,cAAmB,KAAA,cAAA,IAAkB,CAACD,EAAAA,IACvCA,OAAO,QACP,EAAA;QACA,OAAO,IAAA;AACT;IAEA,OAAO;AACLY,QAAAA,KAAAA,EAAON,aAAc,CAAA;YACnBN,EAAI,EAAA,+DAAA;YACJa,cAAgB,EAAA;AAClB,SAAA,CAAA;AACAC,QAAAA,OAAAA,gBACEC,IAACC,CAAAA,IAAAA,EAAAA;YAAKC,SAAU,EAAA,QAAA;YAASC,GAAK,EAAA,CAAA;YAAGC,UAAW,EAAA,SAAA;YAAUC,KAAM,EAAA,MAAA;;8BAC1DC,GAACC,CAAAA,cAAAA,EAAAA,EAAAA,CAAAA;8BACDD,GAACE,CAAAA,WAAAA,EAAAA,EAAAA;;;AAGP,KAAA;AACF;AAEA;AACAzB,KAAAA,CAAM0B,IAAI,GAAG,kBAAA;;;;"}

View File

@@ -0,0 +1,329 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var ee = require('@strapi/admin/strapi-admin/ee');
var strapiAdmin$1 = require('@strapi/content-manager/strapi-admin');
var designSystem = require('@strapi/design-system');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var LimitsModal = require('../../../../../components/LimitsModal.js');
var constants$1 = require('../../../../../constants.js');
var contentManager = require('../../../../../services/content-manager.js');
var api = require('../../../../../utils/api.js');
var colors = require('../../../../../utils/colors.js');
var constants = require('./constants.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);
/* -------------------------------------------------------------------------------------------------
* LimitModals
* -----------------------------------------------------------------------------------------------*/ const WorkflowLimitModal = ({ open, onOpenChange })=>{
const { formatMessage } = reactIntl.useIntl();
return /*#__PURE__*/ jsxRuntime.jsxs(LimitsModal.LimitsModal.Root, {
open: open,
onOpenChange: onOpenChange,
children: [
/*#__PURE__*/ jsxRuntime.jsx(LimitsModal.LimitsModal.Title, {
children: formatMessage({
id: 'content-manager.reviewWorkflows.workflows.limit.title',
defaultMessage: 'Youve reached the limit of workflows in your plan'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(LimitsModal.LimitsModal.Body, {
children: formatMessage({
id: 'content-manager.reviewWorkflows.workflows.limit.body',
defaultMessage: 'Delete a workflow or contact Sales to enable more workflows.'
})
})
]
});
};
const StageLimitModal = ({ open, onOpenChange })=>{
const { formatMessage } = reactIntl.useIntl();
return /*#__PURE__*/ jsxRuntime.jsxs(LimitsModal.LimitsModal.Root, {
open: open,
onOpenChange: onOpenChange,
children: [
/*#__PURE__*/ jsxRuntime.jsx(LimitsModal.LimitsModal.Title, {
children: formatMessage({
id: 'content-manager.reviewWorkflows.stages.limit.title',
defaultMessage: 'You have reached the limit of stages for this workflow in your plan'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(LimitsModal.LimitsModal.Body, {
children: formatMessage({
id: 'content-manager.reviewWorkflows.stages.limit.body',
defaultMessage: 'Try deleting some stages or contact Sales to enable more stages.'
})
})
]
});
};
/* -------------------------------------------------------------------------------------------------
* StageSelect
* -----------------------------------------------------------------------------------------------*/ const Select = ({ stages, activeWorkflowStage, isLoading, ...props })=>{
const { formatMessage } = reactIntl.useIntl();
const { themeColorName } = colors.getStageColorByHex(activeWorkflowStage?.color) ?? {};
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelect, {
disabled: stages.length === 0,
placeholder: formatMessage({
id: 'content-manager.reviewWorkflows.assignee.placeholder',
defaultMessage: 'Select…'
}),
startIcon: activeWorkflowStage && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
tag: "span",
height: 2,
background: activeWorkflowStage?.color,
borderColor: themeColorName === 'neutral0' ? 'neutral150' : undefined,
hasRadius: true,
shrink: 0,
width: 2,
marginRight: "-3px"
}),
// @ts-expect-error `customizeContent` is not correctly typed in the DS.
customizeContent: ()=>{
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
tag: "span",
justifyContent: "space-between",
alignItems: "center",
width: "100%",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral800",
ellipsis: true,
lineHeight: "inherit",
children: activeWorkflowStage?.name ?? ''
}),
isLoading ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Loader, {
small: true,
style: {
display: 'flex'
},
"data-testid": "loader"
}) : null
]
});
},
...props,
children: stages.map(({ id, color, name })=>{
const { themeColorName } = colors.getStageColorByHex(color) ?? {};
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
startIcon: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
height: 2,
background: color,
borderColor: themeColorName === 'neutral0' ? 'neutral150' : undefined,
hasRadius: true,
shrink: 0,
width: 2
}),
value: id,
textValue: name,
children: name
}, id);
})
});
};
const StageSelect = ({ isCompact })=>{
const { collectionType = '', slug: model = '', id = '' } = reactRouterDom.useParams();
const { formatMessage } = reactIntl.useIntl();
const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
const { toggleNotification } = strapiAdmin.useNotification();
const [{ query }] = strapiAdmin.useQueryParams();
const params = React__namespace.useMemo(()=>api.buildValidParams(query), [
query
]);
const { document, isLoading: isLoadingDocument } = strapiAdmin$1.unstable_useDocument({
collectionType,
model,
documentId: id
}, {
skip: !id && collectionType !== 'single-types'
});
const { data, isLoading: isLoadingStages } = contentManager.useGetStagesQuery({
slug: collectionType,
model: model,
// @ts-expect-error `id` is not correctly typed in the DS.
id: document?.documentId,
params
}, {
skip: !document?.documentId
});
const { meta, stages = [] } = data ?? {};
const { getFeature } = ee.useLicenseLimits();
const [showLimitModal, setShowLimitModal] = React__namespace.useState(null);
const limits = getFeature('review-workflows') ?? {};
const activeWorkflowStage = document ? document[constants.STAGE_ATTRIBUTE_NAME] : null;
const [updateStage, { error }] = contentManager.useUpdateStageMutation();
const handleChange = async (stageId)=>{
try {
/**
* If the current license has a limit:
* check if the total count of workflows exceeds that limit and display
* the limits modal.
*
* If the current license does not have a limit (e.g. offline license):
* do nothing (for now).
*
*/ if (limits?.[constants$1.CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME] && parseInt(limits[constants$1.CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME], 10) < (meta?.workflowCount ?? 0)) {
setShowLimitModal('workflow');
/**
* If the current license has a limit:
* check if the total count of stages exceeds that limit and display
* the limits modal.
*
* If the current license does not have a limit (e.g. offline license):
* do nothing (for now).
*
*/ } else if (limits?.[constants$1.CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME] && parseInt(limits[constants$1.CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME], 10) < stages.length) {
setShowLimitModal('stage');
} else {
if (document?.documentId) {
const res = await updateStage({
model,
id: document.documentId,
slug: collectionType,
params,
data: {
id: stageId
}
});
if ('data' in res) {
toggleNotification({
type: 'success',
message: formatMessage({
id: 'content-manager.reviewWorkflows.stage.notification.saved',
defaultMessage: 'Review stage updated'
})
});
}
if (isCompact && 'error' in res) {
toggleNotification({
type: 'danger',
message: formatAPIError(res.error)
});
}
}
}
} catch (error) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'content-manager.reviewWorkflows.stage.notification.error',
defaultMessage: 'An error occurred while updating the review stage'
})
});
}
};
const isLoading = isLoadingStages || isLoadingDocument;
const reviewStageLabel = formatMessage({
id: 'content-manager.reviewWorkflows.stage.label',
defaultMessage: 'Review stage'
});
const reviewStageHint = !isLoading && stages.length === 0 && // TODO: Handle errors and hints
formatMessage({
id: 'content-manager.reviewWorkflows.stages.no-transition',
defaultMessage: 'You dont have the permission to update this stage.'
});
if (isCompact) {
return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Tooltip, {
label: reviewStageHint,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Root, {
name: constants.STAGE_ATTRIBUTE_NAME,
id: constants.STAGE_ATTRIBUTE_NAME,
children: /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.VisuallyHidden, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: reviewStageLabel
})
}),
/*#__PURE__*/ jsxRuntime.jsx(Select, {
stages: stages,
activeWorkflowStage: activeWorkflowStage,
isLoading: isLoading,
size: "S",
disabled: stages.length === 0,
value: activeWorkflowStage?.id,
onChange: handleChange,
placeholder: formatMessage({
id: 'content-manager.reviewWorkflows.assignee.placeholder',
defaultMessage: 'Select…'
})
})
]
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(WorkflowLimitModal, {
open: showLimitModal === 'workflow',
onOpenChange: ()=>setShowLimitModal(null)
}),
/*#__PURE__*/ jsxRuntime.jsx(StageLimitModal, {
open: showLimitModal === 'stage',
onOpenChange: ()=>setShowLimitModal(null)
})
]
});
}
return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
hint: reviewStageHint,
error: error && formatAPIError(error) || undefined,
name: constants.STAGE_ATTRIBUTE_NAME,
id: constants.STAGE_ATTRIBUTE_NAME,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: reviewStageLabel
}),
/*#__PURE__*/ jsxRuntime.jsx(Select, {
stages: stages,
activeWorkflowStage: activeWorkflowStage,
isLoading: isLoading,
disabled: stages.length === 0,
value: activeWorkflowStage?.id,
onChange: handleChange,
placeholder: formatMessage({
id: 'content-manager.reviewWorkflows.assignee.placeholder',
defaultMessage: 'Select…'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Hint, {}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
]
}),
/*#__PURE__*/ jsxRuntime.jsx(WorkflowLimitModal, {
open: showLimitModal === 'workflow',
onOpenChange: ()=>setShowLimitModal(null)
}),
/*#__PURE__*/ jsxRuntime.jsx(StageLimitModal, {
open: showLimitModal === 'stage',
onOpenChange: ()=>setShowLimitModal(null)
})
]
});
};
exports.StageSelect = StageSelect;
//# sourceMappingURL=StageSelect.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,308 @@
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
import * as React from 'react';
import { useAPIErrorHandler, useNotification, useQueryParams } from '@strapi/admin/strapi-admin';
import { useLicenseLimits } from '@strapi/admin/strapi-admin/ee';
import { unstable_useDocument } from '@strapi/content-manager/strapi-admin';
import { Tooltip, Field, VisuallyHidden, SingleSelect, Flex, Typography, Loader, SingleSelectOption } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import { useParams } from 'react-router-dom';
import { LimitsModal } from '../../../../../components/LimitsModal.mjs';
import { CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME, CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME } from '../../../../../constants.mjs';
import { useGetStagesQuery, useUpdateStageMutation } from '../../../../../services/content-manager.mjs';
import { buildValidParams } from '../../../../../utils/api.mjs';
import { getStageColorByHex } from '../../../../../utils/colors.mjs';
import { STAGE_ATTRIBUTE_NAME } from './constants.mjs';
/* -------------------------------------------------------------------------------------------------
* LimitModals
* -----------------------------------------------------------------------------------------------*/ const WorkflowLimitModal = ({ open, onOpenChange })=>{
const { formatMessage } = useIntl();
return /*#__PURE__*/ jsxs(LimitsModal.Root, {
open: open,
onOpenChange: onOpenChange,
children: [
/*#__PURE__*/ jsx(LimitsModal.Title, {
children: formatMessage({
id: 'content-manager.reviewWorkflows.workflows.limit.title',
defaultMessage: 'Youve reached the limit of workflows in your plan'
})
}),
/*#__PURE__*/ jsx(LimitsModal.Body, {
children: formatMessage({
id: 'content-manager.reviewWorkflows.workflows.limit.body',
defaultMessage: 'Delete a workflow or contact Sales to enable more workflows.'
})
})
]
});
};
const StageLimitModal = ({ open, onOpenChange })=>{
const { formatMessage } = useIntl();
return /*#__PURE__*/ jsxs(LimitsModal.Root, {
open: open,
onOpenChange: onOpenChange,
children: [
/*#__PURE__*/ jsx(LimitsModal.Title, {
children: formatMessage({
id: 'content-manager.reviewWorkflows.stages.limit.title',
defaultMessage: 'You have reached the limit of stages for this workflow in your plan'
})
}),
/*#__PURE__*/ jsx(LimitsModal.Body, {
children: formatMessage({
id: 'content-manager.reviewWorkflows.stages.limit.body',
defaultMessage: 'Try deleting some stages or contact Sales to enable more stages.'
})
})
]
});
};
/* -------------------------------------------------------------------------------------------------
* StageSelect
* -----------------------------------------------------------------------------------------------*/ const Select = ({ stages, activeWorkflowStage, isLoading, ...props })=>{
const { formatMessage } = useIntl();
const { themeColorName } = getStageColorByHex(activeWorkflowStage?.color) ?? {};
return /*#__PURE__*/ jsx(SingleSelect, {
disabled: stages.length === 0,
placeholder: formatMessage({
id: 'content-manager.reviewWorkflows.assignee.placeholder',
defaultMessage: 'Select…'
}),
startIcon: activeWorkflowStage && /*#__PURE__*/ jsx(Flex, {
tag: "span",
height: 2,
background: activeWorkflowStage?.color,
borderColor: themeColorName === 'neutral0' ? 'neutral150' : undefined,
hasRadius: true,
shrink: 0,
width: 2,
marginRight: "-3px"
}),
// @ts-expect-error `customizeContent` is not correctly typed in the DS.
customizeContent: ()=>{
return /*#__PURE__*/ jsxs(Flex, {
tag: "span",
justifyContent: "space-between",
alignItems: "center",
width: "100%",
children: [
/*#__PURE__*/ jsx(Typography, {
textColor: "neutral800",
ellipsis: true,
lineHeight: "inherit",
children: activeWorkflowStage?.name ?? ''
}),
isLoading ? /*#__PURE__*/ jsx(Loader, {
small: true,
style: {
display: 'flex'
},
"data-testid": "loader"
}) : null
]
});
},
...props,
children: stages.map(({ id, color, name })=>{
const { themeColorName } = getStageColorByHex(color) ?? {};
return /*#__PURE__*/ jsx(SingleSelectOption, {
startIcon: /*#__PURE__*/ jsx(Flex, {
height: 2,
background: color,
borderColor: themeColorName === 'neutral0' ? 'neutral150' : undefined,
hasRadius: true,
shrink: 0,
width: 2
}),
value: id,
textValue: name,
children: name
}, id);
})
});
};
const StageSelect = ({ isCompact })=>{
const { collectionType = '', slug: model = '', id = '' } = useParams();
const { formatMessage } = useIntl();
const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
const { toggleNotification } = useNotification();
const [{ query }] = useQueryParams();
const params = React.useMemo(()=>buildValidParams(query), [
query
]);
const { document, isLoading: isLoadingDocument } = unstable_useDocument({
collectionType,
model,
documentId: id
}, {
skip: !id && collectionType !== 'single-types'
});
const { data, isLoading: isLoadingStages } = useGetStagesQuery({
slug: collectionType,
model: model,
// @ts-expect-error `id` is not correctly typed in the DS.
id: document?.documentId,
params
}, {
skip: !document?.documentId
});
const { meta, stages = [] } = data ?? {};
const { getFeature } = useLicenseLimits();
const [showLimitModal, setShowLimitModal] = React.useState(null);
const limits = getFeature('review-workflows') ?? {};
const activeWorkflowStage = document ? document[STAGE_ATTRIBUTE_NAME] : null;
const [updateStage, { error }] = useUpdateStageMutation();
const handleChange = async (stageId)=>{
try {
/**
* If the current license has a limit:
* check if the total count of workflows exceeds that limit and display
* the limits modal.
*
* If the current license does not have a limit (e.g. offline license):
* do nothing (for now).
*
*/ if (limits?.[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME] && parseInt(limits[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME], 10) < (meta?.workflowCount ?? 0)) {
setShowLimitModal('workflow');
/**
* If the current license has a limit:
* check if the total count of stages exceeds that limit and display
* the limits modal.
*
* If the current license does not have a limit (e.g. offline license):
* do nothing (for now).
*
*/ } else if (limits?.[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME] && parseInt(limits[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME], 10) < stages.length) {
setShowLimitModal('stage');
} else {
if (document?.documentId) {
const res = await updateStage({
model,
id: document.documentId,
slug: collectionType,
params,
data: {
id: stageId
}
});
if ('data' in res) {
toggleNotification({
type: 'success',
message: formatMessage({
id: 'content-manager.reviewWorkflows.stage.notification.saved',
defaultMessage: 'Review stage updated'
})
});
}
if (isCompact && 'error' in res) {
toggleNotification({
type: 'danger',
message: formatAPIError(res.error)
});
}
}
}
} catch (error) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'content-manager.reviewWorkflows.stage.notification.error',
defaultMessage: 'An error occurred while updating the review stage'
})
});
}
};
const isLoading = isLoadingStages || isLoadingDocument;
const reviewStageLabel = formatMessage({
id: 'content-manager.reviewWorkflows.stage.label',
defaultMessage: 'Review stage'
});
const reviewStageHint = !isLoading && stages.length === 0 && // TODO: Handle errors and hints
formatMessage({
id: 'content-manager.reviewWorkflows.stages.no-transition',
defaultMessage: 'You dont have the permission to update this stage.'
});
if (isCompact) {
return /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsx(Tooltip, {
label: reviewStageHint,
children: /*#__PURE__*/ jsx(Field.Root, {
name: STAGE_ATTRIBUTE_NAME,
id: STAGE_ATTRIBUTE_NAME,
children: /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsx(VisuallyHidden, {
children: /*#__PURE__*/ jsx(Field.Label, {
children: reviewStageLabel
})
}),
/*#__PURE__*/ jsx(Select, {
stages: stages,
activeWorkflowStage: activeWorkflowStage,
isLoading: isLoading,
size: "S",
disabled: stages.length === 0,
value: activeWorkflowStage?.id,
onChange: handleChange,
placeholder: formatMessage({
id: 'content-manager.reviewWorkflows.assignee.placeholder',
defaultMessage: 'Select…'
})
})
]
})
})
}),
/*#__PURE__*/ jsx(WorkflowLimitModal, {
open: showLimitModal === 'workflow',
onOpenChange: ()=>setShowLimitModal(null)
}),
/*#__PURE__*/ jsx(StageLimitModal, {
open: showLimitModal === 'stage',
onOpenChange: ()=>setShowLimitModal(null)
})
]
});
}
return /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsxs(Field.Root, {
hint: reviewStageHint,
error: error && formatAPIError(error) || undefined,
name: STAGE_ATTRIBUTE_NAME,
id: STAGE_ATTRIBUTE_NAME,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: reviewStageLabel
}),
/*#__PURE__*/ jsx(Select, {
stages: stages,
activeWorkflowStage: activeWorkflowStage,
isLoading: isLoading,
disabled: stages.length === 0,
value: activeWorkflowStage?.id,
onChange: handleChange,
placeholder: formatMessage({
id: 'content-manager.reviewWorkflows.assignee.placeholder',
defaultMessage: 'Select…'
})
}),
/*#__PURE__*/ jsx(Field.Hint, {}),
/*#__PURE__*/ jsx(Field.Error, {})
]
}),
/*#__PURE__*/ jsx(WorkflowLimitModal, {
open: showLimitModal === 'workflow',
onOpenChange: ()=>setShowLimitModal(null)
}),
/*#__PURE__*/ jsx(StageLimitModal, {
open: showLimitModal === 'stage',
onOpenChange: ()=>setShowLimitModal(null)
})
]
});
};
export { StageSelect };
//# sourceMappingURL=StageSelect.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
'use strict';
const STAGE_ATTRIBUTE_NAME = 'strapi_stage';
const ASSIGNEE_ATTRIBUTE_NAME = 'strapi_assignee';
exports.ASSIGNEE_ATTRIBUTE_NAME = ASSIGNEE_ATTRIBUTE_NAME;
exports.STAGE_ATTRIBUTE_NAME = STAGE_ATTRIBUTE_NAME;
//# sourceMappingURL=constants.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.js","sources":["../../../../../../../admin/src/routes/content-manager/model/id/components/constants.ts"],"sourcesContent":["export const STAGE_ATTRIBUTE_NAME = 'strapi_stage';\nexport const ASSIGNEE_ATTRIBUTE_NAME = 'strapi_assignee';\n"],"names":["STAGE_ATTRIBUTE_NAME","ASSIGNEE_ATTRIBUTE_NAME"],"mappings":";;AAAO,MAAMA,uBAAuB;AAC7B,MAAMC,0BAA0B;;;;;"}

View File

@@ -0,0 +1,5 @@
const STAGE_ATTRIBUTE_NAME = 'strapi_stage';
const ASSIGNEE_ATTRIBUTE_NAME = 'strapi_assignee';
export { ASSIGNEE_ATTRIBUTE_NAME, STAGE_ATTRIBUTE_NAME };
//# sourceMappingURL=constants.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.mjs","sources":["../../../../../../../admin/src/routes/content-manager/model/id/components/constants.ts"],"sourcesContent":["export const STAGE_ATTRIBUTE_NAME = 'strapi_stage';\nexport const ASSIGNEE_ATTRIBUTE_NAME = 'strapi_assignee';\n"],"names":["STAGE_ATTRIBUTE_NAME","ASSIGNEE_ATTRIBUTE_NAME"],"mappings":"AAAO,MAAMA,uBAAuB;AAC7B,MAAMC,0BAA0B;;;;"}

View File

@@ -0,0 +1,194 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var icons = require('@strapi/icons');
var reactIntl = require('react-intl');
var purchasePageIllustrationDark = require('../assets/purchase-page-illustration-dark.svg.js');
var purchasePageIllustrationLight = require('../assets/purchase-page-illustration-light.svg.js');
var hooks = require('../modules/hooks.js');
const PurchaseReviewWorkflows = ()=>{
const { formatMessage } = reactIntl.useIntl();
const currentTheme = hooks.useTypedSelector((state)=>state.admin_app.theme.currentTheme);
const illustration = currentTheme === 'light' ? purchasePageIllustrationLight : purchasePageIllustrationDark;
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.Root, {
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Main, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.Header, {
title: formatMessage({
id: 'Settings.review-workflows.list.page.title',
defaultMessage: 'Review Workflows'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
marginLeft: 10,
marginRight: 10,
shadow: "filterShadow",
hasRadius: true,
background: "neutral0",
borderColor: 'neutral150',
overflow: 'hidden',
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Grid.Root, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: 6,
s: 12,
alignItems: 'flex-start',
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "flex-start",
padding: 7,
width: '100%',
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
children: /*#__PURE__*/ jsxRuntime.jsx(icons.SealCheck, {
fill: "primary600",
width: `24px`,
height: `24px`
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
paddingTop: 3,
paddingBottom: 4,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "beta",
fontWeight: "bold",
children: formatMessage({
id: 'settings.page.purchase.description',
defaultMessage: 'Manage your content review process'
})
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: 'flex-start',
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(icons.Check, {
fill: "success500",
width: `16px`,
height: `16px`,
style: {
flexShrink: 0
}
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral700",
children: formatMessage({
id: 'settings.page.purchase.perks1',
defaultMessage: 'Customizable review stages'
})
})
]
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(icons.Check, {
fill: "success500",
width: `16px`,
height: `16px`,
style: {
flexShrink: 0
}
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral700",
children: formatMessage({
id: 'settings.page.purchase.perks2',
defaultMessage: 'Manage user permissions'
})
})
]
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(icons.Check, {
fill: "success500",
width: `16px`,
height: `16px`,
style: {
flexShrink: 0
}
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral700",
children: formatMessage({
id: 'settings.page.purchase.perks3',
defaultMessage: 'Support for webhooks'
})
})
]
})
]
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 2,
marginTop: 7,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.LinkButton, {
variant: "default",
href: "https://strapi.io/pricing-self-hosted?utm_campaign=In-Product-CTA&utm_source=Review%20Workflows",
children: formatMessage({
id: 'Settings.page.purchase.upgrade.cta',
defaultMessage: 'Upgrade'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.LinkButton, {
variant: "tertiary",
endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.ExternalLink, {}),
href: "https://strapi.io/features/review-workflow?utm_campaign=In-Product-CTA&utm_source=Review%20Workflows",
children: formatMessage({
id: 'Settings.page.purchase.learn-more.cta',
defaultMessage: 'Learn more'
})
})
]
})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: 6,
s: 12,
background: "primary100",
minHeight: '280px',
children: /*#__PURE__*/ jsxRuntime.jsx("div", {
style: {
position: 'relative',
width: '100%',
height: '100%'
},
children: /*#__PURE__*/ jsxRuntime.jsx("img", {
src: illustration,
alt: "purchase-page-review-workflows-illustration",
width: "100%",
height: "100%",
style: {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
objectFit: 'cover',
objectPosition: 'bottom left'
}
})
})
})
]
})
})
]
})
});
};
exports.PurchaseReviewWorkflows = PurchaseReviewWorkflows;
//# sourceMappingURL=purchase-review-workflows.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,192 @@
import { jsx, jsxs } from 'react/jsx-runtime';
import { Layouts } from '@strapi/admin/strapi-admin';
import { Main, Box, Grid, Flex, Typography, LinkButton } from '@strapi/design-system';
import { SealCheck, Check, ExternalLink } from '@strapi/icons';
import { useIntl } from 'react-intl';
import img$1 from '../assets/purchase-page-illustration-dark.svg.mjs';
import img from '../assets/purchase-page-illustration-light.svg.mjs';
import { useTypedSelector } from '../modules/hooks.mjs';
const PurchaseReviewWorkflows = ()=>{
const { formatMessage } = useIntl();
const currentTheme = useTypedSelector((state)=>state.admin_app.theme.currentTheme);
const illustration = currentTheme === 'light' ? img : img$1;
return /*#__PURE__*/ jsx(Layouts.Root, {
children: /*#__PURE__*/ jsxs(Main, {
children: [
/*#__PURE__*/ jsx(Layouts.Header, {
title: formatMessage({
id: 'Settings.review-workflows.list.page.title',
defaultMessage: 'Review Workflows'
})
}),
/*#__PURE__*/ jsx(Box, {
marginLeft: 10,
marginRight: 10,
shadow: "filterShadow",
hasRadius: true,
background: "neutral0",
borderColor: 'neutral150',
overflow: 'hidden',
children: /*#__PURE__*/ jsxs(Grid.Root, {
children: [
/*#__PURE__*/ jsx(Grid.Item, {
col: 6,
s: 12,
alignItems: 'flex-start',
children: /*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "flex-start",
padding: 7,
width: '100%',
children: [
/*#__PURE__*/ jsx(Flex, {
children: /*#__PURE__*/ jsx(SealCheck, {
fill: "primary600",
width: `24px`,
height: `24px`
})
}),
/*#__PURE__*/ jsx(Flex, {
paddingTop: 3,
paddingBottom: 4,
children: /*#__PURE__*/ jsx(Typography, {
variant: "beta",
fontWeight: "bold",
children: formatMessage({
id: 'settings.page.purchase.description',
defaultMessage: 'Manage your content review process'
})
})
}),
/*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: 'flex-start',
gap: 2,
children: [
/*#__PURE__*/ jsxs(Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsx(Check, {
fill: "success500",
width: `16px`,
height: `16px`,
style: {
flexShrink: 0
}
}),
/*#__PURE__*/ jsx(Typography, {
textColor: "neutral700",
children: formatMessage({
id: 'settings.page.purchase.perks1',
defaultMessage: 'Customizable review stages'
})
})
]
}),
/*#__PURE__*/ jsxs(Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsx(Check, {
fill: "success500",
width: `16px`,
height: `16px`,
style: {
flexShrink: 0
}
}),
/*#__PURE__*/ jsx(Typography, {
textColor: "neutral700",
children: formatMessage({
id: 'settings.page.purchase.perks2',
defaultMessage: 'Manage user permissions'
})
})
]
}),
/*#__PURE__*/ jsxs(Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsx(Check, {
fill: "success500",
width: `16px`,
height: `16px`,
style: {
flexShrink: 0
}
}),
/*#__PURE__*/ jsx(Typography, {
textColor: "neutral700",
children: formatMessage({
id: 'settings.page.purchase.perks3',
defaultMessage: 'Support for webhooks'
})
})
]
})
]
}),
/*#__PURE__*/ jsxs(Flex, {
gap: 2,
marginTop: 7,
children: [
/*#__PURE__*/ jsx(LinkButton, {
variant: "default",
href: "https://strapi.io/pricing-self-hosted?utm_campaign=In-Product-CTA&utm_source=Review%20Workflows",
children: formatMessage({
id: 'Settings.page.purchase.upgrade.cta',
defaultMessage: 'Upgrade'
})
}),
/*#__PURE__*/ jsx(LinkButton, {
variant: "tertiary",
endIcon: /*#__PURE__*/ jsx(ExternalLink, {}),
href: "https://strapi.io/features/review-workflow?utm_campaign=In-Product-CTA&utm_source=Review%20Workflows",
children: formatMessage({
id: 'Settings.page.purchase.learn-more.cta',
defaultMessage: 'Learn more'
})
})
]
})
]
})
}),
/*#__PURE__*/ jsx(Grid.Item, {
col: 6,
s: 12,
background: "primary100",
minHeight: '280px',
children: /*#__PURE__*/ jsx("div", {
style: {
position: 'relative',
width: '100%',
height: '100%'
},
children: /*#__PURE__*/ jsx("img", {
src: illustration,
alt: "purchase-page-review-workflows-illustration",
width: "100%",
height: "100%",
style: {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
objectFit: 'cover',
objectPosition: 'bottom left'
}
})
})
})
]
})
})
]
})
});
};
export { PurchaseReviewWorkflows };
//# sourceMappingURL=purchase-review-workflows.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,51 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var designSystem = require('@strapi/design-system');
var icons = require('@strapi/icons');
var styledComponents = require('styled-components');
const AddStage = ({ children, ...props })=>{
return /*#__PURE__*/ jsxRuntime.jsx(StyledButton, {
tag: "button",
background: "neutral0",
borderColor: "neutral150",
paddingBottom: 3,
paddingLeft: 4,
paddingRight: 4,
paddingTop: 3,
shadow: "filterShadow",
...props,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "pi",
fontWeight: "bold",
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
tag: "span",
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(icons.PlusCircle, {
width: "2.4rem",
height: "2.4rem",
"aria-hidden": true
}),
children
]
})
})
});
};
const StyledButton = styledComponents.styled(designSystem.Box)`
border-radius: 26px;
color: ${({ theme })=>theme.colors.neutral500};
&:hover {
color: ${({ theme })=>theme.colors.primary600};
}
&:active {
color: ${({ theme })=>theme.colors.primary600};
}
`;
exports.AddStage = AddStage;
//# sourceMappingURL=AddStage.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"AddStage.js","sources":["../../../../../admin/src/routes/settings/components/AddStage.tsx"],"sourcesContent":["import { Box, BoxComponent, ButtonProps, Flex, Typography } from '@strapi/design-system';\nimport { PlusCircle } from '@strapi/icons';\nimport { styled } from 'styled-components';\n\nexport const AddStage = ({ children, ...props }: ButtonProps) => {\n return (\n <StyledButton\n tag=\"button\"\n background=\"neutral0\"\n borderColor=\"neutral150\"\n paddingBottom={3}\n paddingLeft={4}\n paddingRight={4}\n paddingTop={3}\n shadow=\"filterShadow\"\n {...props}\n >\n <Typography variant=\"pi\" fontWeight=\"bold\">\n <Flex tag=\"span\" gap={2}>\n <PlusCircle width=\"2.4rem\" height=\"2.4rem\" aria-hidden />\n {children}\n </Flex>\n </Typography>\n </StyledButton>\n );\n};\n\nconst StyledButton = styled<BoxComponent<'button'>>(Box)`\n border-radius: 26px;\n color: ${({ theme }) => theme.colors.neutral500};\n\n &:hover {\n color: ${({ theme }) => theme.colors.primary600};\n }\n\n &:active {\n color: ${({ theme }) => theme.colors.primary600};\n }\n`;\n"],"names":["AddStage","children","props","_jsx","StyledButton","tag","background","borderColor","paddingBottom","paddingLeft","paddingRight","paddingTop","shadow","Typography","variant","fontWeight","_jsxs","Flex","gap","PlusCircle","width","height","aria-hidden","styled","Box","theme","colors","neutral500","primary600"],"mappings":";;;;;;;MAIaA,QAAW,GAAA,CAAC,EAAEC,QAAQ,EAAE,GAAGC,KAAoB,EAAA,GAAA;AAC1D,IAAA,qBACEC,cAACC,CAAAA,YAAAA,EAAAA;QACCC,GAAI,EAAA,QAAA;QACJC,UAAW,EAAA,UAAA;QACXC,WAAY,EAAA,YAAA;QACZC,aAAe,EAAA,CAAA;QACfC,WAAa,EAAA,CAAA;QACbC,YAAc,EAAA,CAAA;QACdC,UAAY,EAAA,CAAA;QACZC,MAAO,EAAA,cAAA;AACN,QAAA,GAAGV,KAAK;AAET,QAAA,QAAA,gBAAAC,cAACU,CAAAA,uBAAAA,EAAAA;YAAWC,OAAQ,EAAA,IAAA;YAAKC,UAAW,EAAA,MAAA;AAClC,YAAA,QAAA,gBAAAC,eAACC,CAAAA,iBAAAA,EAAAA;gBAAKZ,GAAI,EAAA,MAAA;gBAAOa,GAAK,EAAA,CAAA;;kCACpBf,cAACgB,CAAAA,gBAAAA,EAAAA;wBAAWC,KAAM,EAAA,QAAA;wBAASC,MAAO,EAAA,QAAA;wBAASC,aAAW,EAAA;;AACrDrB,oBAAAA;;;;;AAKX;AAEA,MAAMG,YAAAA,GAAemB,uBAA+BC,CAAAA,gBAAAA,CAAI;;SAE/C,EAAE,CAAC,EAAEC,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAACC,UAAU,CAAC;;;WAGvC,EAAE,CAAC,EAAEF,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAACE,UAAU,CAAC;;;;WAIzC,EAAE,CAAC,EAAEH,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAACE,UAAU,CAAC;;AAEpD,CAAC;;;;"}

View File

@@ -0,0 +1,49 @@
import { jsx, jsxs } from 'react/jsx-runtime';
import { Box, Typography, Flex } from '@strapi/design-system';
import { PlusCircle } from '@strapi/icons';
import { styled } from 'styled-components';
const AddStage = ({ children, ...props })=>{
return /*#__PURE__*/ jsx(StyledButton, {
tag: "button",
background: "neutral0",
borderColor: "neutral150",
paddingBottom: 3,
paddingLeft: 4,
paddingRight: 4,
paddingTop: 3,
shadow: "filterShadow",
...props,
children: /*#__PURE__*/ jsx(Typography, {
variant: "pi",
fontWeight: "bold",
children: /*#__PURE__*/ jsxs(Flex, {
tag: "span",
gap: 2,
children: [
/*#__PURE__*/ jsx(PlusCircle, {
width: "2.4rem",
height: "2.4rem",
"aria-hidden": true
}),
children
]
})
})
});
};
const StyledButton = styled(Box)`
border-radius: 26px;
color: ${({ theme })=>theme.colors.neutral500};
&:hover {
color: ${({ theme })=>theme.colors.primary600};
}
&:active {
color: ${({ theme })=>theme.colors.primary600};
}
`;
export { AddStage };
//# sourceMappingURL=AddStage.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"AddStage.mjs","sources":["../../../../../admin/src/routes/settings/components/AddStage.tsx"],"sourcesContent":["import { Box, BoxComponent, ButtonProps, Flex, Typography } from '@strapi/design-system';\nimport { PlusCircle } from '@strapi/icons';\nimport { styled } from 'styled-components';\n\nexport const AddStage = ({ children, ...props }: ButtonProps) => {\n return (\n <StyledButton\n tag=\"button\"\n background=\"neutral0\"\n borderColor=\"neutral150\"\n paddingBottom={3}\n paddingLeft={4}\n paddingRight={4}\n paddingTop={3}\n shadow=\"filterShadow\"\n {...props}\n >\n <Typography variant=\"pi\" fontWeight=\"bold\">\n <Flex tag=\"span\" gap={2}>\n <PlusCircle width=\"2.4rem\" height=\"2.4rem\" aria-hidden />\n {children}\n </Flex>\n </Typography>\n </StyledButton>\n );\n};\n\nconst StyledButton = styled<BoxComponent<'button'>>(Box)`\n border-radius: 26px;\n color: ${({ theme }) => theme.colors.neutral500};\n\n &:hover {\n color: ${({ theme }) => theme.colors.primary600};\n }\n\n &:active {\n color: ${({ theme }) => theme.colors.primary600};\n }\n`;\n"],"names":["AddStage","children","props","_jsx","StyledButton","tag","background","borderColor","paddingBottom","paddingLeft","paddingRight","paddingTop","shadow","Typography","variant","fontWeight","_jsxs","Flex","gap","PlusCircle","width","height","aria-hidden","styled","Box","theme","colors","neutral500","primary600"],"mappings":";;;;;MAIaA,QAAW,GAAA,CAAC,EAAEC,QAAQ,EAAE,GAAGC,KAAoB,EAAA,GAAA;AAC1D,IAAA,qBACEC,GAACC,CAAAA,YAAAA,EAAAA;QACCC,GAAI,EAAA,QAAA;QACJC,UAAW,EAAA,UAAA;QACXC,WAAY,EAAA,YAAA;QACZC,aAAe,EAAA,CAAA;QACfC,WAAa,EAAA,CAAA;QACbC,YAAc,EAAA,CAAA;QACdC,UAAY,EAAA,CAAA;QACZC,MAAO,EAAA,cAAA;AACN,QAAA,GAAGV,KAAK;AAET,QAAA,QAAA,gBAAAC,GAACU,CAAAA,UAAAA,EAAAA;YAAWC,OAAQ,EAAA,IAAA;YAAKC,UAAW,EAAA,MAAA;AAClC,YAAA,QAAA,gBAAAC,IAACC,CAAAA,IAAAA,EAAAA;gBAAKZ,GAAI,EAAA,MAAA;gBAAOa,GAAK,EAAA,CAAA;;kCACpBf,GAACgB,CAAAA,UAAAA,EAAAA;wBAAWC,KAAM,EAAA,QAAA;wBAASC,MAAO,EAAA,QAAA;wBAASC,aAAW,EAAA;;AACrDrB,oBAAAA;;;;;AAKX;AAEA,MAAMG,YAAAA,GAAemB,MAA+BC,CAAAA,GAAAA,CAAI;;SAE/C,EAAE,CAAC,EAAEC,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAACC,UAAU,CAAC;;;WAGvC,EAAE,CAAC,EAAEF,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAACE,UAAU,CAAC;;;;WAIzC,EAAE,CAAC,EAAEH,KAAK,EAAE,GAAKA,KAAMC,CAAAA,MAAM,CAACE,UAAU,CAAC;;AAEpD,CAAC;;;;"}

View File

@@ -0,0 +1,86 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var reactDnd = require('react-dnd');
var reactIntl = require('react-intl');
var constants = require('../constants.js');
var StageDragPreview = require('./StageDragPreview.js');
function getStyle(initialOffset, currentOffset, mouseOffset) {
if (!initialOffset || !currentOffset || !mouseOffset) {
return {
display: 'none'
};
}
const { x, y } = mouseOffset;
return {
transform: `translate(${x}px, ${y}px)`
};
}
const DragLayerRendered = ()=>{
const { itemType, isDragging, item, initialOffset, currentOffset, mouseOffset } = reactDnd.useDragLayer((monitor)=>({
item: monitor.getItem(),
itemType: monitor.getItemType(),
initialOffset: monitor.getInitialSourceClientOffset(),
currentOffset: monitor.getSourceClientOffset(),
isDragging: monitor.isDragging(),
mouseOffset: monitor.getClientOffset()
}));
if (!isDragging || itemType !== constants.DRAG_DROP_TYPES.STAGE) {
return null;
}
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
height: "100%",
left: 0,
position: "fixed",
pointerEvents: "none",
top: 0,
zIndex: 100,
width: "100%",
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Box, {
style: getStyle(initialOffset, currentOffset, mouseOffset),
children: [
/*#__PURE__*/ jsxRuntime.jsx(StageDragPreview.StageDragPreview, {
name: typeof item.item === 'string' ? item.item : null
}),
";"
]
})
});
};
const Root = ({ children })=>{
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Main, {
children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.Content, {
children: children
})
});
};
const Header = ({ title, subtitle, navigationAction, primaryAction })=>{
const { formatMessage } = reactIntl.useIntl();
return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Title, {
children: formatMessage({
id: 'Settings.PageTitle',
defaultMessage: 'Settings - {name}'
}, {
name: title
})
}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.BaseHeader, {
navigationAction: navigationAction,
primaryAction: primaryAction,
title: title,
subtitle: subtitle
})
]
});
};
exports.DragLayerRendered = DragLayerRendered;
exports.Header = Header;
exports.Root = Root;
//# sourceMappingURL=Layout.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,82 @@
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import 'react';
import { Page, Layouts } from '@strapi/admin/strapi-admin';
import { Box } from '@strapi/design-system';
import { useDragLayer } from 'react-dnd';
import { useIntl } from 'react-intl';
import { DRAG_DROP_TYPES } from '../constants.mjs';
import { StageDragPreview } from './StageDragPreview.mjs';
function getStyle(initialOffset, currentOffset, mouseOffset) {
if (!initialOffset || !currentOffset || !mouseOffset) {
return {
display: 'none'
};
}
const { x, y } = mouseOffset;
return {
transform: `translate(${x}px, ${y}px)`
};
}
const DragLayerRendered = ()=>{
const { itemType, isDragging, item, initialOffset, currentOffset, mouseOffset } = useDragLayer((monitor)=>({
item: monitor.getItem(),
itemType: monitor.getItemType(),
initialOffset: monitor.getInitialSourceClientOffset(),
currentOffset: monitor.getSourceClientOffset(),
isDragging: monitor.isDragging(),
mouseOffset: monitor.getClientOffset()
}));
if (!isDragging || itemType !== DRAG_DROP_TYPES.STAGE) {
return null;
}
return /*#__PURE__*/ jsx(Box, {
height: "100%",
left: 0,
position: "fixed",
pointerEvents: "none",
top: 0,
zIndex: 100,
width: "100%",
children: /*#__PURE__*/ jsxs(Box, {
style: getStyle(initialOffset, currentOffset, mouseOffset),
children: [
/*#__PURE__*/ jsx(StageDragPreview, {
name: typeof item.item === 'string' ? item.item : null
}),
";"
]
})
});
};
const Root = ({ children })=>{
return /*#__PURE__*/ jsx(Page.Main, {
children: /*#__PURE__*/ jsx(Layouts.Content, {
children: children
})
});
};
const Header = ({ title, subtitle, navigationAction, primaryAction })=>{
const { formatMessage } = useIntl();
return /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsx(Page.Title, {
children: formatMessage({
id: 'Settings.PageTitle',
defaultMessage: 'Settings - {name}'
}, {
name: title
})
}),
/*#__PURE__*/ jsx(Layouts.BaseHeader, {
navigationAction: navigationAction,
primaryAction: primaryAction,
title: title,
subtitle: subtitle
})
]
});
};
export { DragLayerRendered, Header, Root };
//# sourceMappingURL=Layout.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,40 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var designSystem = require('@strapi/design-system');
var icons = require('@strapi/icons');
const StageDragPreview = ({ name })=>{
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
background: "primary100",
borderStyle: "dashed",
borderColor: "primary600",
borderWidth: "1px",
gap: 3,
hasRadius: true,
padding: 3,
shadow: "tableShadow",
width: "30rem",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
alignItems: "center",
background: "neutral200",
borderRadius: "50%",
height: 6,
justifyContent: "center",
width: 6,
children: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {
width: "0.8rem",
fill: "neutral600"
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
fontWeight: "bold",
children: name
})
]
});
};
exports.StageDragPreview = StageDragPreview;
//# sourceMappingURL=StageDragPreview.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StageDragPreview.js","sources":["../../../../../admin/src/routes/settings/components/StageDragPreview.tsx"],"sourcesContent":["import { Flex, Typography } from '@strapi/design-system';\nimport { CaretDown } from '@strapi/icons';\n\ninterface StageDragPreviewType {\n name: string | null;\n}\n\nconst StageDragPreview = ({ name }: StageDragPreviewType) => {\n return (\n <Flex\n background=\"primary100\"\n borderStyle=\"dashed\"\n borderColor=\"primary600\"\n borderWidth=\"1px\"\n gap={3}\n hasRadius\n padding={3}\n shadow=\"tableShadow\"\n width=\"30rem\"\n >\n <Flex\n alignItems=\"center\"\n background=\"neutral200\"\n borderRadius=\"50%\"\n height={6}\n justifyContent=\"center\"\n width={6}\n >\n <CaretDown width=\"0.8rem\" fill=\"neutral600\" />\n </Flex>\n\n <Typography fontWeight=\"bold\">{name}</Typography>\n </Flex>\n );\n};\n\nexport { StageDragPreview };\nexport type { StageDragPreviewType };\n"],"names":["StageDragPreview","name","_jsxs","Flex","background","borderStyle","borderColor","borderWidth","gap","hasRadius","padding","shadow","width","_jsx","alignItems","borderRadius","height","justifyContent","CaretDown","fill","Typography","fontWeight"],"mappings":";;;;;;AAOA,MAAMA,gBAAmB,GAAA,CAAC,EAAEC,IAAI,EAAwB,GAAA;AACtD,IAAA,qBACEC,eAACC,CAAAA,iBAAAA,EAAAA;QACCC,UAAW,EAAA,YAAA;QACXC,WAAY,EAAA,QAAA;QACZC,WAAY,EAAA,YAAA;QACZC,WAAY,EAAA,KAAA;QACZC,GAAK,EAAA,CAAA;QACLC,SAAS,EAAA,IAAA;QACTC,OAAS,EAAA,CAAA;QACTC,MAAO,EAAA,aAAA;QACPC,KAAM,EAAA,OAAA;;0BAENC,cAACV,CAAAA,iBAAAA,EAAAA;gBACCW,UAAW,EAAA,QAAA;gBACXV,UAAW,EAAA,YAAA;gBACXW,YAAa,EAAA,KAAA;gBACbC,MAAQ,EAAA,CAAA;gBACRC,cAAe,EAAA,QAAA;gBACfL,KAAO,EAAA,CAAA;AAEP,gBAAA,QAAA,gBAAAC,cAACK,CAAAA,eAAAA,EAAAA;oBAAUN,KAAM,EAAA,QAAA;oBAASO,IAAK,EAAA;;;0BAGjCN,cAACO,CAAAA,uBAAAA,EAAAA;gBAAWC,UAAW,EAAA,MAAA;AAAQpB,gBAAAA,QAAAA,EAAAA;;;;AAGrC;;;;"}

View File

@@ -0,0 +1,38 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import { Flex, Typography } from '@strapi/design-system';
import { CaretDown } from '@strapi/icons';
const StageDragPreview = ({ name })=>{
return /*#__PURE__*/ jsxs(Flex, {
background: "primary100",
borderStyle: "dashed",
borderColor: "primary600",
borderWidth: "1px",
gap: 3,
hasRadius: true,
padding: 3,
shadow: "tableShadow",
width: "30rem",
children: [
/*#__PURE__*/ jsx(Flex, {
alignItems: "center",
background: "neutral200",
borderRadius: "50%",
height: 6,
justifyContent: "center",
width: 6,
children: /*#__PURE__*/ jsx(CaretDown, {
width: "0.8rem",
fill: "neutral600"
})
}),
/*#__PURE__*/ jsx(Typography, {
fontWeight: "bold",
children: name
})
]
});
};
export { StageDragPreview };
//# sourceMappingURL=StageDragPreview.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StageDragPreview.mjs","sources":["../../../../../admin/src/routes/settings/components/StageDragPreview.tsx"],"sourcesContent":["import { Flex, Typography } from '@strapi/design-system';\nimport { CaretDown } from '@strapi/icons';\n\ninterface StageDragPreviewType {\n name: string | null;\n}\n\nconst StageDragPreview = ({ name }: StageDragPreviewType) => {\n return (\n <Flex\n background=\"primary100\"\n borderStyle=\"dashed\"\n borderColor=\"primary600\"\n borderWidth=\"1px\"\n gap={3}\n hasRadius\n padding={3}\n shadow=\"tableShadow\"\n width=\"30rem\"\n >\n <Flex\n alignItems=\"center\"\n background=\"neutral200\"\n borderRadius=\"50%\"\n height={6}\n justifyContent=\"center\"\n width={6}\n >\n <CaretDown width=\"0.8rem\" fill=\"neutral600\" />\n </Flex>\n\n <Typography fontWeight=\"bold\">{name}</Typography>\n </Flex>\n );\n};\n\nexport { StageDragPreview };\nexport type { StageDragPreviewType };\n"],"names":["StageDragPreview","name","_jsxs","Flex","background","borderStyle","borderColor","borderWidth","gap","hasRadius","padding","shadow","width","_jsx","alignItems","borderRadius","height","justifyContent","CaretDown","fill","Typography","fontWeight"],"mappings":";;;;AAOA,MAAMA,gBAAmB,GAAA,CAAC,EAAEC,IAAI,EAAwB,GAAA;AACtD,IAAA,qBACEC,IAACC,CAAAA,IAAAA,EAAAA;QACCC,UAAW,EAAA,YAAA;QACXC,WAAY,EAAA,QAAA;QACZC,WAAY,EAAA,YAAA;QACZC,WAAY,EAAA,KAAA;QACZC,GAAK,EAAA,CAAA;QACLC,SAAS,EAAA,IAAA;QACTC,OAAS,EAAA,CAAA;QACTC,MAAO,EAAA,aAAA;QACPC,KAAM,EAAA,OAAA;;0BAENC,GAACV,CAAAA,IAAAA,EAAAA;gBACCW,UAAW,EAAA,QAAA;gBACXV,UAAW,EAAA,YAAA;gBACXW,YAAa,EAAA,KAAA;gBACbC,MAAQ,EAAA,CAAA;gBACRC,cAAe,EAAA,QAAA;gBACfL,KAAO,EAAA,CAAA;AAEP,gBAAA,QAAA,gBAAAC,GAACK,CAAAA,SAAAA,EAAAA;oBAAUN,KAAM,EAAA,QAAA;oBAASO,IAAK,EAAA;;;0BAGjCN,GAACO,CAAAA,UAAAA,EAAAA;gBAAWC,UAAW,EAAA,MAAA;AAAQpB,gBAAAA,QAAAA,EAAAA;;;;AAGrC;;;;"}

View File

@@ -0,0 +1,593 @@
'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 reactDndHtml5Backend = require('react-dnd-html5-backend');
var reactIntl = require('react-intl');
var styledComponents = require('styled-components');
var admin = require('../../../services/admin.js');
var colors = require('../../../utils/colors.js');
var constants = require('../constants.js');
var useDragAndDrop = require('../hooks/useDragAndDrop.js');
var AddStage = require('./AddStage.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 Stages = ({ canDelete = true, canUpdate = true, isCreating })=>{
const { formatMessage } = reactIntl.useIntl();
const { trackUsage } = strapiAdmin.useTracking();
const addFieldRow = strapiAdmin.useForm('Stages', (state)=>state.addFieldRow);
const { value: stages = [] } = strapiAdmin.useField('stages');
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
gap: 6,
width: "100%",
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Box, {
position: "relative",
width: "100%",
children: [
/*#__PURE__*/ jsxRuntime.jsx(Background, {
background: "neutral200",
height: "100%",
left: "50%",
position: "absolute",
top: "0",
width: 2
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
position: "relative",
tag: "ol",
children: stages.map((stage, index)=>{
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
tag: "li",
children: /*#__PURE__*/ jsxRuntime.jsx(Stage, {
index: index,
canDelete: stages.length > 1 && canDelete,
canReorder: stages.length > 1,
canUpdate: canUpdate,
stagesCount: stages.length,
defaultOpen: !stage.id,
...stage
})
}, stage.__temp_key__);
})
})
]
}),
canUpdate && /*#__PURE__*/ jsxRuntime.jsx(AddStage.AddStage, {
type: "button",
onClick: ()=>{
addFieldRow('stages', {
name: ''
});
trackUsage('willCreateStage');
},
children: formatMessage({
id: 'Settings.review-workflows.stage.add',
defaultMessage: 'Add new stage'
})
})
]
});
};
const Background = styledComponents.styled(designSystem.Box)`
transform: translateX(-50%);
`;
const Stage = ({ index, canDelete = false, canReorder = false, canUpdate = false, stagesCount, name, permissions, color, defaultOpen })=>{
const [liveText, setLiveText] = React__namespace.useState();
const { formatMessage } = reactIntl.useIntl();
const { trackUsage } = strapiAdmin.useTracking();
const stageErrors = strapiAdmin.useForm('Stages', (state)=>state.errors.stages);
const error = stageErrors?.[index];
const addFieldRow = strapiAdmin.useForm('Stage', (state)=>state.addFieldRow);
const moveFieldRow = strapiAdmin.useForm('Stage', (state)=>state.moveFieldRow);
const removeFieldRow = strapiAdmin.useForm('Stage', (state)=>state.removeFieldRow);
const getItemPos = (index)=>`${index + 1} of ${stagesCount}`;
const handleGrabStage = (index)=>{
setLiveText(formatMessage({
id: 'dnd.grab-item',
defaultMessage: `{item}, grabbed. Current position in list: {position}. Press up and down arrow to change position, Spacebar to drop, Escape to cancel.`
}, {
item: name,
position: getItemPos(index)
}));
};
const handleDropStage = (index)=>{
setLiveText(formatMessage({
id: 'dnd.drop-item',
defaultMessage: `{item}, dropped. Final position in list: {position}.`
}, {
item: name,
position: getItemPos(index)
}));
};
const handleCancelDragStage = ()=>{
setLiveText(formatMessage({
id: 'dnd.cancel-item',
defaultMessage: '{item}, dropped. Re-order cancelled.'
}, {
item: name
}));
};
const handleMoveStage = (newIndex, oldIndex)=>{
setLiveText(formatMessage({
id: 'dnd.reorder',
defaultMessage: '{item}, moved. New position in list: {position}.'
}, {
item: name,
position: getItemPos(newIndex)
}));
moveFieldRow('stages', oldIndex, newIndex);
};
const [{ handlerId, isDragging, handleKeyDown }, stageRef, dropRef, dragRef, dragPreviewRef] = useDragAndDrop.useDragAndDrop(canReorder, {
index,
item: {
index,
name
},
onGrabItem: handleGrabStage,
onDropItem: handleDropStage,
onMoveItem: handleMoveStage,
onCancel: handleCancelDragStage,
type: constants.DRAG_DROP_TYPES.STAGE
});
// @ts-expect-error the stageRef is incorrectly typed.
const composedRef = designSystem.useComposedRefs(stageRef, dropRef);
React__namespace.useEffect(()=>{
dragPreviewRef(reactDndHtml5Backend.getEmptyImage(), {
captureDraggingState: false
});
}, [
dragPreviewRef,
index
]);
const handleCloneClick = ()=>{
addFieldRow('stages', {
name,
color,
permissions
});
};
const id = React__namespace.useId();
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Box, {
ref: composedRef,
shadow: "tableShadow",
children: [
liveText && /*#__PURE__*/ jsxRuntime.jsx(designSystem.VisuallyHidden, {
"aria-live": "assertive",
children: liveText
}),
isDragging ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
background: "primary100",
borderStyle: "dashed",
borderColor: "primary600",
borderWidth: "1px",
display: "block",
hasRadius: true,
padding: 6
}) : /*#__PURE__*/ jsxRuntime.jsx(AccordionRoot, {
onValueChange: (value)=>{
if (value) {
trackUsage('willEditStage');
}
},
defaultValue: defaultOpen ? id : undefined,
$error: Object.values(error ?? {}).length > 0,
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Accordion.Item, {
value: id,
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Accordion.Header, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Accordion.Trigger, {
children: name
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Accordion.Actions, {
children: canDelete || canUpdate ? /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Menu.Root, {
children: [
/*#__PURE__*/ jsxRuntime.jsxs(ContextMenuTrigger, {
size: "S",
endIcon: null,
paddingLeft: 2,
paddingRight: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(icons.More, {
"aria-hidden": true,
focusable: false
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.VisuallyHidden, {
tag: "span",
children: formatMessage({
id: '[tbdb].components.DynamicZone.more-actions',
defaultMessage: 'More actions'
})
})
]
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Menu.Content, {
popoverPlacement: "bottom-end",
zIndex: 2,
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Menu.SubRoot, {
children: [
canUpdate && /*#__PURE__*/ jsxRuntime.jsx(designSystem.MenuItem, {
onClick: handleCloneClick,
children: formatMessage({
id: 'Settings.review-workflows.stage.delete',
defaultMessage: 'Duplicate stage'
})
}),
canDelete && /*#__PURE__*/ jsxRuntime.jsx(DeleteMenuItem, {
onClick: ()=>removeFieldRow('stages', index),
children: formatMessage({
id: 'Settings.review-workflows.stage.delete',
defaultMessage: 'Delete'
})
})
]
})
})
]
}),
canUpdate && /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
background: "transparent",
hasRadius: true,
variant: "ghost",
"data-handler-id": handlerId,
ref: dragRef,
label: formatMessage({
id: 'Settings.review-workflows.stage.drag',
defaultMessage: 'Drag'
}),
onClick: (e)=>e.stopPropagation(),
onKeyDown: handleKeyDown,
children: /*#__PURE__*/ jsxRuntime.jsx(icons.Drag, {})
})
]
}) : null
})
]
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Accordion.Content, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Root, {
gap: 4,
padding: 6,
children: [
{
disabled: !canUpdate,
label: formatMessage({
id: 'Settings.review-workflows.stage.name.label',
defaultMessage: 'Stage name'
}),
name: `stages.${index}.name`,
required: true,
size: 6,
type: 'string'
},
{
disabled: !canUpdate,
label: formatMessage({
id: 'content-manager.reviewWorkflows.stage.color',
defaultMessage: 'Color'
}),
name: `stages.${index}.color`,
required: true,
size: 6,
type: 'color'
},
{
disabled: !canUpdate,
label: formatMessage({
id: 'Settings.review-workflows.stage.permissions.label',
defaultMessage: 'Roles that can change this stage'
}),
name: `stages.${index}.permissions`,
placeholder: formatMessage({
id: 'Settings.review-workflows.stage.permissions.placeholder',
defaultMessage: 'Select a role'
}),
required: true,
size: 6,
type: 'permissions'
}
].map(({ size, ...field })=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: size,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(InputRenderer, {
...field
})
}, field.name))
})
})
]
})
})
]
});
};
const AccordionRoot = styledComponents.styled(designSystem.Accordion.Root)`
border: 1px solid
${({ theme, $error })=>$error ? theme.colors.danger600 : theme.colors.neutral200};
`;
const DeleteMenuItem = styledComponents.styled(designSystem.MenuItem)`
color: ${({ theme })=>theme.colors.danger600};
`;
// Removing the font-size from the child-span aligns the
// more icon vertically
const ContextMenuTrigger = styledComponents.styled(designSystem.Menu.Trigger)`
:hover,
:focus {
background-color: ${({ theme })=>theme.colors.neutral100};
}
> span {
font-size: 0;
}
`;
const InputRenderer = (props)=>{
switch(props.type){
case 'color':
return /*#__PURE__*/ jsxRuntime.jsx(ColorSelector, {
...props
});
case 'permissions':
return /*#__PURE__*/ jsxRuntime.jsx(PermissionsField, {
...props
});
default:
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.InputRenderer, {
...props
});
}
};
const ColorSelector = ({ disabled, label, name, required })=>{
const { formatMessage } = reactIntl.useIntl();
const { value, error, onChange } = strapiAdmin.useField(name);
const colorOptions = colors.AVAILABLE_COLORS.map(({ hex, name })=>({
value: hex,
label: formatMessage({
id: 'Settings.review-workflows.stage.color.name',
defaultMessage: '{name}'
}, {
name
}),
color: hex
}));
const { themeColorName } = colors.getStageColorByHex(value) ?? {};
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
error: error,
name: name,
required: required,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: label
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelect, {
disabled: disabled,
onChange: (v)=>{
onChange(name, v.toString());
},
value: value?.toUpperCase(),
startIcon: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
tag: "span",
height: 2,
background: value,
borderColor: themeColorName === 'neutral0' ? 'neutral150' : 'transparent',
hasRadius: true,
shrink: 0,
width: 2
}),
children: colorOptions.map(({ value, label, color })=>{
const { themeColorName } = colors.getStageColorByHex(color) || {};
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
value: value,
startIcon: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
tag: "span",
height: 2,
background: color,
borderColor: themeColorName === 'neutral0' ? 'neutral150' : 'transparent',
hasRadius: true,
shrink: 0,
width: 2
}),
children: label
}, value);
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
]
});
};
const PermissionsField = ({ disabled, name, placeholder, required })=>{
const { formatMessage } = reactIntl.useIntl();
const { toggleNotification } = strapiAdmin.useNotification();
const [isApplyAllConfirmationOpen, setIsApplyAllConfirmationOpen] = React__namespace.useState(false);
const { value = [], error, onChange } = strapiAdmin.useField(name);
const allStages = strapiAdmin.useForm('PermissionsField', (state)=>state.values.stages);
const onFormValueChange = strapiAdmin.useForm('PermissionsField', (state)=>state.onChange);
const rolesErrorCount = React__namespace.useRef(0);
const { data: roles = [], isLoading, error: getRolesError } = admin.useGetAdminRolesQuery();
// Super admins always have permissions to do everything and therefore
// there is no point for this role to show up in the role combobox
const filteredRoles = roles?.filter((role)=>role.code !== 'strapi-super-admin') ?? [];
React__namespace.useEffect(()=>{
if (!isLoading && getRolesError && 'status' in getRolesError && getRolesError.status == 403 && rolesErrorCount.current === 0) {
rolesErrorCount.current = 1;
toggleNotification({
blockTransition: true,
type: 'danger',
message: formatMessage({
id: 'review-workflows.stage.permissions.noPermissions.description',
defaultMessage: 'You dont have the permission to see roles. Contact your administrator.'
})
});
}
}, [
formatMessage,
isLoading,
roles,
toggleNotification,
getRolesError
]);
if (!isLoading && filteredRoles.length === 0) {
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
name: name,
hint: formatMessage({
id: 'Settings.review-workflows.stage.permissions.noPermissions.description',
defaultMessage: 'You dont have the permission to see roles'
}),
required: required,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'Settings.review-workflows.stage.permissions.label',
defaultMessage: 'Roles that can change this stage'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.TextInput, {
disabled: true,
placeholder: formatMessage({
id: 'components.NotAllowedInput.text',
defaultMessage: 'No permissions to see this field'
}),
startAction: /*#__PURE__*/ jsxRuntime.jsx(icons.EyeStriked, {
fill: "neutral600"
}),
type: "text",
value: ""
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Hint, {})
]
});
}
return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
alignItems: "flex-end",
gap: 3,
children: [
/*#__PURE__*/ jsxRuntime.jsx(PermissionWrapper, {
grow: 1,
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
error: error,
name: name,
required: true,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'Settings.review-workflows.stage.permissions.label',
defaultMessage: 'Roles that can change this stage'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.MultiSelect, {
disabled: disabled,
onChange: (values)=>{
// Because the select components expects strings for values, but
// the yup schema validates we are sending full permission objects to the API,
// we must coerce the string value back to an object
const permissions = values.map((value)=>({
role: parseInt(value, 10),
action: 'admin::review-workflows.stage.transition'
}));
onChange(name, permissions);
},
placeholder: placeholder,
// The Select component expects strings for values
value: value.map((permission)=>`${permission.role}`),
withTags: true,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.MultiSelectGroup, {
label: formatMessage({
id: 'Settings.review-workflows.stage.permissions.allRoles.label',
defaultMessage: 'All roles'
}),
values: filteredRoles.map((r)=>`${r.id}`),
children: filteredRoles.map((role)=>{
return /*#__PURE__*/ jsxRuntime.jsx(NestedOption, {
value: `${role.id}`,
children: role.name
}, role.id);
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Dialog.Root, {
open: isApplyAllConfirmationOpen,
onOpenChange: setIsApplyAllConfirmationOpen,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Trigger, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
disabled: disabled,
label: formatMessage({
id: 'Settings.review-workflows.stage.permissions.apply.label',
defaultMessage: 'Apply to all stages'
}),
size: "L",
children: /*#__PURE__*/ jsxRuntime.jsx(icons.Duplicate, {})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.ConfirmDialog, {
onConfirm: ()=>{
onFormValueChange('stages', allStages.map((stage)=>({
...stage,
permissions: value
})));
setIsApplyAllConfirmationOpen(false);
toggleNotification({
type: 'success',
message: formatMessage({
id: 'Settings.review-workflows.page.edit.confirm.stages.permissions.copy.success',
defaultMessage: 'Applied roles to all other stages of the workflow'
})
});
},
variant: "default",
children: formatMessage({
id: 'Settings.review-workflows.page.edit.confirm.stages.permissions.copy',
defaultMessage: 'Roles that can change that stage will be applied to all the other stages.'
})
})
]
})
]
})
});
};
const NestedOption = styledComponents.styled(designSystem.MultiSelectOption)`
padding-left: ${({ theme })=>theme.spaces[7]};
`;
// Grow the size of the permission Select
const PermissionWrapper = styledComponents.styled(designSystem.Flex)`
> * {
flex-grow: 1;
}
`;
exports.Stages = Stages;
//# sourceMappingURL=Stages.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,572 @@
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import * as React from 'react';
import { useTracking, useForm, useField, InputRenderer as InputRenderer$1, useNotification, ConfirmDialog } from '@strapi/admin/strapi-admin';
import { Box, Accordion, MenuItem, Menu, MultiSelectOption, Flex, useComposedRefs, VisuallyHidden, IconButton, Grid, Field, SingleSelect, SingleSelectOption, TextInput, MultiSelect, MultiSelectGroup, Dialog } from '@strapi/design-system';
import { More, Drag, EyeStriked, Duplicate } from '@strapi/icons';
import { getEmptyImage } from 'react-dnd-html5-backend';
import { useIntl } from 'react-intl';
import { styled } from 'styled-components';
import { useGetAdminRolesQuery } from '../../../services/admin.mjs';
import { AVAILABLE_COLORS, getStageColorByHex } from '../../../utils/colors.mjs';
import { DRAG_DROP_TYPES } from '../constants.mjs';
import { useDragAndDrop } from '../hooks/useDragAndDrop.mjs';
import { AddStage } from './AddStage.mjs';
const Stages = ({ canDelete = true, canUpdate = true, isCreating })=>{
const { formatMessage } = useIntl();
const { trackUsage } = useTracking();
const addFieldRow = useForm('Stages', (state)=>state.addFieldRow);
const { value: stages = [] } = useField('stages');
return /*#__PURE__*/ jsxs(Flex, {
direction: "column",
gap: 6,
width: "100%",
children: [
/*#__PURE__*/ jsxs(Box, {
position: "relative",
width: "100%",
children: [
/*#__PURE__*/ jsx(Background, {
background: "neutral200",
height: "100%",
left: "50%",
position: "absolute",
top: "0",
width: 2
}),
/*#__PURE__*/ jsx(Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
position: "relative",
tag: "ol",
children: stages.map((stage, index)=>{
return /*#__PURE__*/ jsx(Box, {
tag: "li",
children: /*#__PURE__*/ jsx(Stage, {
index: index,
canDelete: stages.length > 1 && canDelete,
canReorder: stages.length > 1,
canUpdate: canUpdate,
stagesCount: stages.length,
defaultOpen: !stage.id,
...stage
})
}, stage.__temp_key__);
})
})
]
}),
canUpdate && /*#__PURE__*/ jsx(AddStage, {
type: "button",
onClick: ()=>{
addFieldRow('stages', {
name: ''
});
trackUsage('willCreateStage');
},
children: formatMessage({
id: 'Settings.review-workflows.stage.add',
defaultMessage: 'Add new stage'
})
})
]
});
};
const Background = styled(Box)`
transform: translateX(-50%);
`;
const Stage = ({ index, canDelete = false, canReorder = false, canUpdate = false, stagesCount, name, permissions, color, defaultOpen })=>{
const [liveText, setLiveText] = React.useState();
const { formatMessage } = useIntl();
const { trackUsage } = useTracking();
const stageErrors = useForm('Stages', (state)=>state.errors.stages);
const error = stageErrors?.[index];
const addFieldRow = useForm('Stage', (state)=>state.addFieldRow);
const moveFieldRow = useForm('Stage', (state)=>state.moveFieldRow);
const removeFieldRow = useForm('Stage', (state)=>state.removeFieldRow);
const getItemPos = (index)=>`${index + 1} of ${stagesCount}`;
const handleGrabStage = (index)=>{
setLiveText(formatMessage({
id: 'dnd.grab-item',
defaultMessage: `{item}, grabbed. Current position in list: {position}. Press up and down arrow to change position, Spacebar to drop, Escape to cancel.`
}, {
item: name,
position: getItemPos(index)
}));
};
const handleDropStage = (index)=>{
setLiveText(formatMessage({
id: 'dnd.drop-item',
defaultMessage: `{item}, dropped. Final position in list: {position}.`
}, {
item: name,
position: getItemPos(index)
}));
};
const handleCancelDragStage = ()=>{
setLiveText(formatMessage({
id: 'dnd.cancel-item',
defaultMessage: '{item}, dropped. Re-order cancelled.'
}, {
item: name
}));
};
const handleMoveStage = (newIndex, oldIndex)=>{
setLiveText(formatMessage({
id: 'dnd.reorder',
defaultMessage: '{item}, moved. New position in list: {position}.'
}, {
item: name,
position: getItemPos(newIndex)
}));
moveFieldRow('stages', oldIndex, newIndex);
};
const [{ handlerId, isDragging, handleKeyDown }, stageRef, dropRef, dragRef, dragPreviewRef] = useDragAndDrop(canReorder, {
index,
item: {
index,
name
},
onGrabItem: handleGrabStage,
onDropItem: handleDropStage,
onMoveItem: handleMoveStage,
onCancel: handleCancelDragStage,
type: DRAG_DROP_TYPES.STAGE
});
// @ts-expect-error the stageRef is incorrectly typed.
const composedRef = useComposedRefs(stageRef, dropRef);
React.useEffect(()=>{
dragPreviewRef(getEmptyImage(), {
captureDraggingState: false
});
}, [
dragPreviewRef,
index
]);
const handleCloneClick = ()=>{
addFieldRow('stages', {
name,
color,
permissions
});
};
const id = React.useId();
return /*#__PURE__*/ jsxs(Box, {
ref: composedRef,
shadow: "tableShadow",
children: [
liveText && /*#__PURE__*/ jsx(VisuallyHidden, {
"aria-live": "assertive",
children: liveText
}),
isDragging ? /*#__PURE__*/ jsx(Box, {
background: "primary100",
borderStyle: "dashed",
borderColor: "primary600",
borderWidth: "1px",
display: "block",
hasRadius: true,
padding: 6
}) : /*#__PURE__*/ jsx(AccordionRoot, {
onValueChange: (value)=>{
if (value) {
trackUsage('willEditStage');
}
},
defaultValue: defaultOpen ? id : undefined,
$error: Object.values(error ?? {}).length > 0,
children: /*#__PURE__*/ jsxs(Accordion.Item, {
value: id,
children: [
/*#__PURE__*/ jsxs(Accordion.Header, {
children: [
/*#__PURE__*/ jsx(Accordion.Trigger, {
children: name
}),
/*#__PURE__*/ jsx(Accordion.Actions, {
children: canDelete || canUpdate ? /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsxs(Menu.Root, {
children: [
/*#__PURE__*/ jsxs(ContextMenuTrigger, {
size: "S",
endIcon: null,
paddingLeft: 2,
paddingRight: 2,
children: [
/*#__PURE__*/ jsx(More, {
"aria-hidden": true,
focusable: false
}),
/*#__PURE__*/ jsx(VisuallyHidden, {
tag: "span",
children: formatMessage({
id: '[tbdb].components.DynamicZone.more-actions',
defaultMessage: 'More actions'
})
})
]
}),
/*#__PURE__*/ jsx(Menu.Content, {
popoverPlacement: "bottom-end",
zIndex: 2,
children: /*#__PURE__*/ jsxs(Menu.SubRoot, {
children: [
canUpdate && /*#__PURE__*/ jsx(MenuItem, {
onClick: handleCloneClick,
children: formatMessage({
id: 'Settings.review-workflows.stage.delete',
defaultMessage: 'Duplicate stage'
})
}),
canDelete && /*#__PURE__*/ jsx(DeleteMenuItem, {
onClick: ()=>removeFieldRow('stages', index),
children: formatMessage({
id: 'Settings.review-workflows.stage.delete',
defaultMessage: 'Delete'
})
})
]
})
})
]
}),
canUpdate && /*#__PURE__*/ jsx(IconButton, {
background: "transparent",
hasRadius: true,
variant: "ghost",
"data-handler-id": handlerId,
ref: dragRef,
label: formatMessage({
id: 'Settings.review-workflows.stage.drag',
defaultMessage: 'Drag'
}),
onClick: (e)=>e.stopPropagation(),
onKeyDown: handleKeyDown,
children: /*#__PURE__*/ jsx(Drag, {})
})
]
}) : null
})
]
}),
/*#__PURE__*/ jsx(Accordion.Content, {
children: /*#__PURE__*/ jsx(Grid.Root, {
gap: 4,
padding: 6,
children: [
{
disabled: !canUpdate,
label: formatMessage({
id: 'Settings.review-workflows.stage.name.label',
defaultMessage: 'Stage name'
}),
name: `stages.${index}.name`,
required: true,
size: 6,
type: 'string'
},
{
disabled: !canUpdate,
label: formatMessage({
id: 'content-manager.reviewWorkflows.stage.color',
defaultMessage: 'Color'
}),
name: `stages.${index}.color`,
required: true,
size: 6,
type: 'color'
},
{
disabled: !canUpdate,
label: formatMessage({
id: 'Settings.review-workflows.stage.permissions.label',
defaultMessage: 'Roles that can change this stage'
}),
name: `stages.${index}.permissions`,
placeholder: formatMessage({
id: 'Settings.review-workflows.stage.permissions.placeholder',
defaultMessage: 'Select a role'
}),
required: true,
size: 6,
type: 'permissions'
}
].map(({ size, ...field })=>/*#__PURE__*/ jsx(Grid.Item, {
col: size,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(InputRenderer, {
...field
})
}, field.name))
})
})
]
})
})
]
});
};
const AccordionRoot = styled(Accordion.Root)`
border: 1px solid
${({ theme, $error })=>$error ? theme.colors.danger600 : theme.colors.neutral200};
`;
const DeleteMenuItem = styled(MenuItem)`
color: ${({ theme })=>theme.colors.danger600};
`;
// Removing the font-size from the child-span aligns the
// more icon vertically
const ContextMenuTrigger = styled(Menu.Trigger)`
:hover,
:focus {
background-color: ${({ theme })=>theme.colors.neutral100};
}
> span {
font-size: 0;
}
`;
const InputRenderer = (props)=>{
switch(props.type){
case 'color':
return /*#__PURE__*/ jsx(ColorSelector, {
...props
});
case 'permissions':
return /*#__PURE__*/ jsx(PermissionsField, {
...props
});
default:
return /*#__PURE__*/ jsx(InputRenderer$1, {
...props
});
}
};
const ColorSelector = ({ disabled, label, name, required })=>{
const { formatMessage } = useIntl();
const { value, error, onChange } = useField(name);
const colorOptions = AVAILABLE_COLORS.map(({ hex, name })=>({
value: hex,
label: formatMessage({
id: 'Settings.review-workflows.stage.color.name',
defaultMessage: '{name}'
}, {
name
}),
color: hex
}));
const { themeColorName } = getStageColorByHex(value) ?? {};
return /*#__PURE__*/ jsxs(Field.Root, {
error: error,
name: name,
required: required,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: label
}),
/*#__PURE__*/ jsx(SingleSelect, {
disabled: disabled,
onChange: (v)=>{
onChange(name, v.toString());
},
value: value?.toUpperCase(),
startIcon: /*#__PURE__*/ jsx(Flex, {
tag: "span",
height: 2,
background: value,
borderColor: themeColorName === 'neutral0' ? 'neutral150' : 'transparent',
hasRadius: true,
shrink: 0,
width: 2
}),
children: colorOptions.map(({ value, label, color })=>{
const { themeColorName } = getStageColorByHex(color) || {};
return /*#__PURE__*/ jsx(SingleSelectOption, {
value: value,
startIcon: /*#__PURE__*/ jsx(Flex, {
tag: "span",
height: 2,
background: color,
borderColor: themeColorName === 'neutral0' ? 'neutral150' : 'transparent',
hasRadius: true,
shrink: 0,
width: 2
}),
children: label
}, value);
})
}),
/*#__PURE__*/ jsx(Field.Error, {})
]
});
};
const PermissionsField = ({ disabled, name, placeholder, required })=>{
const { formatMessage } = useIntl();
const { toggleNotification } = useNotification();
const [isApplyAllConfirmationOpen, setIsApplyAllConfirmationOpen] = React.useState(false);
const { value = [], error, onChange } = useField(name);
const allStages = useForm('PermissionsField', (state)=>state.values.stages);
const onFormValueChange = useForm('PermissionsField', (state)=>state.onChange);
const rolesErrorCount = React.useRef(0);
const { data: roles = [], isLoading, error: getRolesError } = useGetAdminRolesQuery();
// Super admins always have permissions to do everything and therefore
// there is no point for this role to show up in the role combobox
const filteredRoles = roles?.filter((role)=>role.code !== 'strapi-super-admin') ?? [];
React.useEffect(()=>{
if (!isLoading && getRolesError && 'status' in getRolesError && getRolesError.status == 403 && rolesErrorCount.current === 0) {
rolesErrorCount.current = 1;
toggleNotification({
blockTransition: true,
type: 'danger',
message: formatMessage({
id: 'review-workflows.stage.permissions.noPermissions.description',
defaultMessage: 'You dont have the permission to see roles. Contact your administrator.'
})
});
}
}, [
formatMessage,
isLoading,
roles,
toggleNotification,
getRolesError
]);
if (!isLoading && filteredRoles.length === 0) {
return /*#__PURE__*/ jsxs(Field.Root, {
name: name,
hint: formatMessage({
id: 'Settings.review-workflows.stage.permissions.noPermissions.description',
defaultMessage: 'You dont have the permission to see roles'
}),
required: required,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'Settings.review-workflows.stage.permissions.label',
defaultMessage: 'Roles that can change this stage'
})
}),
/*#__PURE__*/ jsx(TextInput, {
disabled: true,
placeholder: formatMessage({
id: 'components.NotAllowedInput.text',
defaultMessage: 'No permissions to see this field'
}),
startAction: /*#__PURE__*/ jsx(EyeStriked, {
fill: "neutral600"
}),
type: "text",
value: ""
}),
/*#__PURE__*/ jsx(Field.Hint, {})
]
});
}
return /*#__PURE__*/ jsx(Fragment, {
children: /*#__PURE__*/ jsxs(Flex, {
alignItems: "flex-end",
gap: 3,
children: [
/*#__PURE__*/ jsx(PermissionWrapper, {
grow: 1,
children: /*#__PURE__*/ jsxs(Field.Root, {
error: error,
name: name,
required: true,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'Settings.review-workflows.stage.permissions.label',
defaultMessage: 'Roles that can change this stage'
})
}),
/*#__PURE__*/ jsx(MultiSelect, {
disabled: disabled,
onChange: (values)=>{
// Because the select components expects strings for values, but
// the yup schema validates we are sending full permission objects to the API,
// we must coerce the string value back to an object
const permissions = values.map((value)=>({
role: parseInt(value, 10),
action: 'admin::review-workflows.stage.transition'
}));
onChange(name, permissions);
},
placeholder: placeholder,
// The Select component expects strings for values
value: value.map((permission)=>`${permission.role}`),
withTags: true,
children: /*#__PURE__*/ jsx(MultiSelectGroup, {
label: formatMessage({
id: 'Settings.review-workflows.stage.permissions.allRoles.label',
defaultMessage: 'All roles'
}),
values: filteredRoles.map((r)=>`${r.id}`),
children: filteredRoles.map((role)=>{
return /*#__PURE__*/ jsx(NestedOption, {
value: `${role.id}`,
children: role.name
}, role.id);
})
})
}),
/*#__PURE__*/ jsx(Field.Error, {})
]
})
}),
/*#__PURE__*/ jsxs(Dialog.Root, {
open: isApplyAllConfirmationOpen,
onOpenChange: setIsApplyAllConfirmationOpen,
children: [
/*#__PURE__*/ jsx(Dialog.Trigger, {
children: /*#__PURE__*/ jsx(IconButton, {
disabled: disabled,
label: formatMessage({
id: 'Settings.review-workflows.stage.permissions.apply.label',
defaultMessage: 'Apply to all stages'
}),
size: "L",
children: /*#__PURE__*/ jsx(Duplicate, {})
})
}),
/*#__PURE__*/ jsx(ConfirmDialog, {
onConfirm: ()=>{
onFormValueChange('stages', allStages.map((stage)=>({
...stage,
permissions: value
})));
setIsApplyAllConfirmationOpen(false);
toggleNotification({
type: 'success',
message: formatMessage({
id: 'Settings.review-workflows.page.edit.confirm.stages.permissions.copy.success',
defaultMessage: 'Applied roles to all other stages of the workflow'
})
});
},
variant: "default",
children: formatMessage({
id: 'Settings.review-workflows.page.edit.confirm.stages.permissions.copy',
defaultMessage: 'Roles that can change that stage will be applied to all the other stages.'
})
})
]
})
]
})
});
};
const NestedOption = styled(MultiSelectOption)`
padding-left: ${({ theme })=>theme.spaces[7]};
`;
// Grow the size of the permission Select
const PermissionWrapper = styled(Flex)`
> * {
flex-grow: 1;
}
`;
export { Stages };
//# sourceMappingURL=Stages.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,203 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var reactIntl = require('react-intl');
var styledComponents = require('styled-components');
var contentManager = require('../../../services/content-manager.js');
var useReviewWorkflows = require('../hooks/useReviewWorkflows.js');
const WorkflowAttributes = ({ canUpdate = true })=>{
const { formatMessage } = reactIntl.useIntl();
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Grid.Root, {
background: "neutral0",
hasRadius: true,
gap: 4,
padding: 6,
shadow: "tableShadow",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: 6,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.InputRenderer, {
disabled: !canUpdate,
label: formatMessage({
id: 'Settings.review-workflows.workflow.name.label',
defaultMessage: 'Workflow Name'
}),
name: "name",
required: true,
type: "string"
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: 6,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(ContentTypesSelector, {
disabled: !canUpdate
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: 6,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(StageSelector, {
disabled: !canUpdate
})
})
]
});
};
const ContentTypesSelector = ({ disabled })=>{
const { formatMessage, locale } = reactIntl.useIntl();
const { data: contentTypes, isLoading } = contentManager.useGetContentTypesQuery();
const { workflows } = useReviewWorkflows.useReviewWorkflows();
const currentWorkflow = strapiAdmin.useForm('ContentTypesSelector', (state)=>state.values);
const { error, value, onChange } = strapiAdmin.useField('contentTypes');
const formatter = designSystem.useCollator(locale, {
sensitivity: 'base'
});
const isDisabled = disabled || isLoading || !contentTypes || contentTypes.collectionType.length === 0 && contentTypes.singleType.length === 0;
const collectionTypes = (contentTypes?.collectionType ?? []).toSorted((a, b)=>formatter.compare(a.info.displayName, b.info.displayName)).map((contentType)=>({
label: contentType.info.displayName,
value: contentType.uid
}));
const singleTypes = (contentTypes?.singleType ?? []).map((contentType)=>({
label: contentType.info.displayName,
value: contentType.uid
}));
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
error: error,
name: 'contentTypes',
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.label',
defaultMessage: 'Associated to'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.MultiSelect, {
customizeContent: (value)=>formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.displayValue',
defaultMessage: '{count} {count, plural, one {content type} other {content types}} selected'
}, {
count: value?.length
}),
disabled: isDisabled,
onChange: (values)=>{
onChange('contentTypes', values);
},
value: value,
placeholder: formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.placeholder',
defaultMessage: 'Select'
}),
children: [
...collectionTypes.length > 0 ? [
{
label: formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.collectionTypes.label',
defaultMessage: 'Collection Types'
}),
children: collectionTypes
}
] : [],
...singleTypes.length > 0 ? [
{
label: formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.singleTypes.label',
defaultMessage: 'Single Types'
}),
children: singleTypes
}
] : []
].map((opt)=>{
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.MultiSelectGroup, {
label: opt.label,
values: opt.children.map((child)=>child.value.toString()),
children: opt.children.map((child)=>{
const { name: assignedWorkflowName } = workflows?.find((workflow)=>(currentWorkflow && workflow.id !== currentWorkflow.id || !currentWorkflow) && workflow.contentTypes.includes(child.value)) ?? {};
return /*#__PURE__*/ jsxRuntime.jsx(NestedOption, {
value: child.value,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
children: // @ts-expect-error - formatMessage options doesn't expect to be a React component but that's what we need actually for the <i> and <em> components
formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.assigned.notice',
defaultMessage: '{label} {name, select, undefined {} other {<i>(assigned to <em>{name}</em> workflow)</i>}}'
}, {
label: child.label,
name: assignedWorkflowName,
em: (...children)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
tag: "em",
fontWeight: "bold",
children: children
}),
i: (...children)=>/*#__PURE__*/ jsxRuntime.jsx(ContentTypeTakeNotice, {
children: children
})
})
})
}, child.value);
})
}, opt.label);
})
})
]
});
};
const NestedOption = styledComponents.styled(designSystem.MultiSelectOption)`
padding-left: ${({ theme })=>theme.spaces[7]};
`;
const ContentTypeTakeNotice = styledComponents.styled(designSystem.Typography)`
font-style: italic;
`;
const StageSelector = ({ disabled })=>{
const { value: stages = [] } = strapiAdmin.useField('stages');
const { formatMessage } = reactIntl.useIntl();
const { error, value, onChange } = strapiAdmin.useField('stageRequiredToPublish');
// stages with empty names are not valid, so we avoid them from being used to avoid errors
const validStages = stages.filter((stage)=>stage.name);
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
error: error,
name: "stageRequiredToPublish",
hint: formatMessage({
id: 'settings.review-workflows.workflow.stageRequiredToPublish.hint',
defaultMessage: 'Prevents entries from being published if they are not at the required stage.'
}),
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'settings.review-workflows.workflow.stageRequiredToPublish.label',
defaultMessage: 'Required stage for publishing'
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.SingleSelect, {
disabled: disabled,
onChange: (value)=>{
onChange('stageRequiredToPublish', value);
},
value: value,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
value: '',
children: formatMessage({
id: 'settings.review-workflows.workflow.stageRequiredToPublish.any',
defaultMessage: 'Any stage'
})
}),
validStages.map((stage, i)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
value: stage.id?.toString() || stage.__temp_key__,
children: stage.name
}, `requiredToPublishStage-${stage.id || stage.__temp_key__}`))
]
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Hint, {})
]
});
};
exports.WorkflowAttributes = WorkflowAttributes;
//# sourceMappingURL=WorkflowAttributes.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,201 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import { InputRenderer, useForm, useField } from '@strapi/admin/strapi-admin';
import { MultiSelectOption, Typography, Grid, useCollator, Field, MultiSelect, MultiSelectGroup, SingleSelect, SingleSelectOption } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import { styled } from 'styled-components';
import { useGetContentTypesQuery } from '../../../services/content-manager.mjs';
import { useReviewWorkflows } from '../hooks/useReviewWorkflows.mjs';
const WorkflowAttributes = ({ canUpdate = true })=>{
const { formatMessage } = useIntl();
return /*#__PURE__*/ jsxs(Grid.Root, {
background: "neutral0",
hasRadius: true,
gap: 4,
padding: 6,
shadow: "tableShadow",
children: [
/*#__PURE__*/ jsx(Grid.Item, {
col: 6,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(InputRenderer, {
disabled: !canUpdate,
label: formatMessage({
id: 'Settings.review-workflows.workflow.name.label',
defaultMessage: 'Workflow Name'
}),
name: "name",
required: true,
type: "string"
})
}),
/*#__PURE__*/ jsx(Grid.Item, {
col: 6,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(ContentTypesSelector, {
disabled: !canUpdate
})
}),
/*#__PURE__*/ jsx(Grid.Item, {
col: 6,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(StageSelector, {
disabled: !canUpdate
})
})
]
});
};
const ContentTypesSelector = ({ disabled })=>{
const { formatMessage, locale } = useIntl();
const { data: contentTypes, isLoading } = useGetContentTypesQuery();
const { workflows } = useReviewWorkflows();
const currentWorkflow = useForm('ContentTypesSelector', (state)=>state.values);
const { error, value, onChange } = useField('contentTypes');
const formatter = useCollator(locale, {
sensitivity: 'base'
});
const isDisabled = disabled || isLoading || !contentTypes || contentTypes.collectionType.length === 0 && contentTypes.singleType.length === 0;
const collectionTypes = (contentTypes?.collectionType ?? []).toSorted((a, b)=>formatter.compare(a.info.displayName, b.info.displayName)).map((contentType)=>({
label: contentType.info.displayName,
value: contentType.uid
}));
const singleTypes = (contentTypes?.singleType ?? []).map((contentType)=>({
label: contentType.info.displayName,
value: contentType.uid
}));
return /*#__PURE__*/ jsxs(Field.Root, {
error: error,
name: 'contentTypes',
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.label',
defaultMessage: 'Associated to'
})
}),
/*#__PURE__*/ jsx(MultiSelect, {
customizeContent: (value)=>formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.displayValue',
defaultMessage: '{count} {count, plural, one {content type} other {content types}} selected'
}, {
count: value?.length
}),
disabled: isDisabled,
onChange: (values)=>{
onChange('contentTypes', values);
},
value: value,
placeholder: formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.placeholder',
defaultMessage: 'Select'
}),
children: [
...collectionTypes.length > 0 ? [
{
label: formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.collectionTypes.label',
defaultMessage: 'Collection Types'
}),
children: collectionTypes
}
] : [],
...singleTypes.length > 0 ? [
{
label: formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.singleTypes.label',
defaultMessage: 'Single Types'
}),
children: singleTypes
}
] : []
].map((opt)=>{
return /*#__PURE__*/ jsx(MultiSelectGroup, {
label: opt.label,
values: opt.children.map((child)=>child.value.toString()),
children: opt.children.map((child)=>{
const { name: assignedWorkflowName } = workflows?.find((workflow)=>(currentWorkflow && workflow.id !== currentWorkflow.id || !currentWorkflow) && workflow.contentTypes.includes(child.value)) ?? {};
return /*#__PURE__*/ jsx(NestedOption, {
value: child.value,
children: /*#__PURE__*/ jsx(Typography, {
children: // @ts-expect-error - formatMessage options doesn't expect to be a React component but that's what we need actually for the <i> and <em> components
formatMessage({
id: 'Settings.review-workflows.workflow.contentTypes.assigned.notice',
defaultMessage: '{label} {name, select, undefined {} other {<i>(assigned to <em>{name}</em> workflow)</i>}}'
}, {
label: child.label,
name: assignedWorkflowName,
em: (...children)=>/*#__PURE__*/ jsx(Typography, {
tag: "em",
fontWeight: "bold",
children: children
}),
i: (...children)=>/*#__PURE__*/ jsx(ContentTypeTakeNotice, {
children: children
})
})
})
}, child.value);
})
}, opt.label);
})
})
]
});
};
const NestedOption = styled(MultiSelectOption)`
padding-left: ${({ theme })=>theme.spaces[7]};
`;
const ContentTypeTakeNotice = styled(Typography)`
font-style: italic;
`;
const StageSelector = ({ disabled })=>{
const { value: stages = [] } = useField('stages');
const { formatMessage } = useIntl();
const { error, value, onChange } = useField('stageRequiredToPublish');
// stages with empty names are not valid, so we avoid them from being used to avoid errors
const validStages = stages.filter((stage)=>stage.name);
return /*#__PURE__*/ jsxs(Field.Root, {
error: error,
name: "stageRequiredToPublish",
hint: formatMessage({
id: 'settings.review-workflows.workflow.stageRequiredToPublish.hint',
defaultMessage: 'Prevents entries from being published if they are not at the required stage.'
}),
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'settings.review-workflows.workflow.stageRequiredToPublish.label',
defaultMessage: 'Required stage for publishing'
})
}),
/*#__PURE__*/ jsxs(SingleSelect, {
disabled: disabled,
onChange: (value)=>{
onChange('stageRequiredToPublish', value);
},
value: value,
children: [
/*#__PURE__*/ jsx(SingleSelectOption, {
value: '',
children: formatMessage({
id: 'settings.review-workflows.workflow.stageRequiredToPublish.any',
defaultMessage: 'Any stage'
})
}),
validStages.map((stage, i)=>/*#__PURE__*/ jsx(SingleSelectOption, {
value: stage.id?.toString() || stage.__temp_key__,
children: stage.name
}, `requiredToPublishStage-${stage.id || stage.__temp_key__}`))
]
}),
/*#__PURE__*/ jsx(Field.Hint, {})
]
});
};
export { WorkflowAttributes };
//# sourceMappingURL=WorkflowAttributes.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
'use strict';
const DRAG_DROP_TYPES = {
STAGE: 'stage'
};
exports.DRAG_DROP_TYPES = DRAG_DROP_TYPES;
//# sourceMappingURL=constants.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.js","sources":["../../../../admin/src/routes/settings/constants.ts"],"sourcesContent":["export type DragDropTypes = 'stage';\n\nexport const DRAG_DROP_TYPES: Record<Uppercase<DragDropTypes>, DragDropTypes> = {\n STAGE: 'stage',\n};\n"],"names":["DRAG_DROP_TYPES","STAGE"],"mappings":";;MAEaA,eAAmE,GAAA;IAC9EC,KAAO,EAAA;AACT;;;;"}

View File

@@ -0,0 +1,6 @@
const DRAG_DROP_TYPES = {
STAGE: 'stage'
};
export { DRAG_DROP_TYPES };
//# sourceMappingURL=constants.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.mjs","sources":["../../../../admin/src/routes/settings/constants.ts"],"sourcesContent":["export type DragDropTypes = 'stage';\n\nexport const DRAG_DROP_TYPES: Record<Uppercase<DragDropTypes>, DragDropTypes> = {\n STAGE: 'stage',\n};\n"],"names":["DRAG_DROP_TYPES","STAGE"],"mappings":"MAEaA,eAAmE,GAAA;IAC9EC,KAAO,EAAA;AACT;;;;"}

View File

@@ -0,0 +1,193 @@
'use strict';
var React = require('react');
var reactDnd = require('react-dnd');
var useKeyboardDragAndDrop = require('./useKeyboardDragAndDrop.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 DIRECTIONS = {
UPWARD: 'upward',
DOWNWARD: 'downward'
};
const DROP_SENSITIVITY = {
REGULAR: 'regular',
IMMEDIATE: 'immediate'
};
/**
* A utility hook abstracting the general drag and drop hooks from react-dnd.
* Centralising the same behaviours and by default offering keyboard support.
*/ const useDragAndDrop = (active, { type = 'STRAPI_DND', index, item, onStart, onEnd, onGrabItem, onDropItem, onCancel, onMoveItem, dropSensitivity = DROP_SENSITIVITY.REGULAR })=>{
const objectRef = React__namespace.useRef(null);
const [{ handlerId, isOver }, dropRef] = reactDnd.useDrop({
accept: type,
collect (monitor) {
return {
handlerId: monitor.getHandlerId(),
isOver: monitor.isOver({
shallow: true
})
};
},
drop (item) {
const draggedIndex = item.index;
const newIndex = index;
if (isOver && onDropItem) {
onDropItem(draggedIndex, newIndex);
}
},
hover (item, monitor) {
if (!objectRef.current || !onMoveItem) {
return;
}
const dragIndex = item.index;
const newIndex = index;
const hoverBoundingRect = objectRef.current?.getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
const clientOffset = monitor.getClientOffset();
if (!clientOffset) return;
const hoverClientY = clientOffset && clientOffset.y - hoverBoundingRect.top;
if (typeof dragIndex === 'number' && typeof newIndex === 'number') {
if (dragIndex === newIndex) {
// Don't replace items with themselves
return;
}
if (dropSensitivity === DROP_SENSITIVITY.REGULAR) {
// Dragging downwards
if (dragIndex < newIndex && hoverClientY < hoverMiddleY) {
return;
}
// Dragging upwards
if (dragIndex > newIndex && hoverClientY > hoverMiddleY) {
return;
}
}
// Time to actually perform the action
onMoveItem(newIndex, dragIndex);
item.index = newIndex;
} else {
// Using numbers as indices doesn't work for nested list items with path like [1, 1, 0]
if (Array.isArray(dragIndex) && Array.isArray(newIndex)) {
// Indices comparison to find item position in nested list
const minLength = Math.min(dragIndex.length, newIndex.length);
let areEqual = true;
let isLessThan = false;
let isGreaterThan = false;
for(let i = 0; i < minLength; i++){
if (dragIndex[i] < newIndex[i]) {
isLessThan = true;
areEqual = false;
break;
} else if (dragIndex[i] > newIndex[i]) {
isGreaterThan = true;
areEqual = false;
break;
}
}
// Don't replace items with themselves
if (areEqual && dragIndex.length === newIndex.length) {
return;
}
if (dropSensitivity === DROP_SENSITIVITY.REGULAR) {
// Dragging downwards
if (isLessThan && !isGreaterThan && hoverClientY < hoverMiddleY) {
return;
}
// Dragging upwards
if (isGreaterThan && !isLessThan && hoverClientY > hoverMiddleY) {
return;
}
}
}
onMoveItem(newIndex, dragIndex);
item.index = newIndex;
}
}
});
const getDragDirection = (monitor)=>{
if (monitor && monitor.isDragging() && !monitor.didDrop() && monitor.getInitialClientOffset() && monitor.getClientOffset()) {
const deltaY = monitor.getInitialClientOffset().y - monitor.getClientOffset().y;
if (deltaY > 0) return DIRECTIONS.UPWARD;
if (deltaY < 0) return DIRECTIONS.DOWNWARD;
return null;
}
return null;
};
const [{ isDragging, direction }, dragRef, dragPreviewRef] = reactDnd.useDrag({
type,
item () {
if (onStart) {
onStart();
}
/**
* This will be attached and it helps define the preview sizes
* when a component is flexy e.g. Relations
*/ const { width } = objectRef.current?.getBoundingClientRect() ?? {};
return {
index,
width,
...item
};
},
end () {
if (onEnd) {
onEnd();
}
},
canDrag: active,
/**
* This is useful when the item is in a virtualized list.
* However, if we don't have an ID then we want the libraries
* defaults to take care of this.
*/ isDragging: item?.id ? (monitor)=>{
return item.id === monitor.getItem().id;
} : undefined,
collect: (monitor)=>({
isDragging: monitor.isDragging(),
initialOffset: monitor.getInitialClientOffset(),
currentOffset: monitor.getClientOffset(),
direction: getDragDirection(monitor)
})
});
const handleKeyDown = useKeyboardDragAndDrop.useKeyboardDragAndDrop(active, index, {
onGrabItem,
onDropItem,
onCancel,
onMoveItem
});
return [
{
handlerId,
isDragging,
handleKeyDown,
isOverDropTarget: isOver,
direction
},
objectRef,
dropRef,
dragRef,
dragPreviewRef
];
};
exports.DIRECTIONS = DIRECTIONS;
exports.DROP_SENSITIVITY = DROP_SENSITIVITY;
exports.useDragAndDrop = useDragAndDrop;
//# sourceMappingURL=useDragAndDrop.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,170 @@
import * as React from 'react';
import { useDrop, useDrag } from 'react-dnd';
import { useKeyboardDragAndDrop } from './useKeyboardDragAndDrop.mjs';
const DIRECTIONS = {
UPWARD: 'upward',
DOWNWARD: 'downward'
};
const DROP_SENSITIVITY = {
REGULAR: 'regular',
IMMEDIATE: 'immediate'
};
/**
* A utility hook abstracting the general drag and drop hooks from react-dnd.
* Centralising the same behaviours and by default offering keyboard support.
*/ const useDragAndDrop = (active, { type = 'STRAPI_DND', index, item, onStart, onEnd, onGrabItem, onDropItem, onCancel, onMoveItem, dropSensitivity = DROP_SENSITIVITY.REGULAR })=>{
const objectRef = React.useRef(null);
const [{ handlerId, isOver }, dropRef] = useDrop({
accept: type,
collect (monitor) {
return {
handlerId: monitor.getHandlerId(),
isOver: monitor.isOver({
shallow: true
})
};
},
drop (item) {
const draggedIndex = item.index;
const newIndex = index;
if (isOver && onDropItem) {
onDropItem(draggedIndex, newIndex);
}
},
hover (item, monitor) {
if (!objectRef.current || !onMoveItem) {
return;
}
const dragIndex = item.index;
const newIndex = index;
const hoverBoundingRect = objectRef.current?.getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
const clientOffset = monitor.getClientOffset();
if (!clientOffset) return;
const hoverClientY = clientOffset && clientOffset.y - hoverBoundingRect.top;
if (typeof dragIndex === 'number' && typeof newIndex === 'number') {
if (dragIndex === newIndex) {
// Don't replace items with themselves
return;
}
if (dropSensitivity === DROP_SENSITIVITY.REGULAR) {
// Dragging downwards
if (dragIndex < newIndex && hoverClientY < hoverMiddleY) {
return;
}
// Dragging upwards
if (dragIndex > newIndex && hoverClientY > hoverMiddleY) {
return;
}
}
// Time to actually perform the action
onMoveItem(newIndex, dragIndex);
item.index = newIndex;
} else {
// Using numbers as indices doesn't work for nested list items with path like [1, 1, 0]
if (Array.isArray(dragIndex) && Array.isArray(newIndex)) {
// Indices comparison to find item position in nested list
const minLength = Math.min(dragIndex.length, newIndex.length);
let areEqual = true;
let isLessThan = false;
let isGreaterThan = false;
for(let i = 0; i < minLength; i++){
if (dragIndex[i] < newIndex[i]) {
isLessThan = true;
areEqual = false;
break;
} else if (dragIndex[i] > newIndex[i]) {
isGreaterThan = true;
areEqual = false;
break;
}
}
// Don't replace items with themselves
if (areEqual && dragIndex.length === newIndex.length) {
return;
}
if (dropSensitivity === DROP_SENSITIVITY.REGULAR) {
// Dragging downwards
if (isLessThan && !isGreaterThan && hoverClientY < hoverMiddleY) {
return;
}
// Dragging upwards
if (isGreaterThan && !isLessThan && hoverClientY > hoverMiddleY) {
return;
}
}
}
onMoveItem(newIndex, dragIndex);
item.index = newIndex;
}
}
});
const getDragDirection = (monitor)=>{
if (monitor && monitor.isDragging() && !monitor.didDrop() && monitor.getInitialClientOffset() && monitor.getClientOffset()) {
const deltaY = monitor.getInitialClientOffset().y - monitor.getClientOffset().y;
if (deltaY > 0) return DIRECTIONS.UPWARD;
if (deltaY < 0) return DIRECTIONS.DOWNWARD;
return null;
}
return null;
};
const [{ isDragging, direction }, dragRef, dragPreviewRef] = useDrag({
type,
item () {
if (onStart) {
onStart();
}
/**
* This will be attached and it helps define the preview sizes
* when a component is flexy e.g. Relations
*/ const { width } = objectRef.current?.getBoundingClientRect() ?? {};
return {
index,
width,
...item
};
},
end () {
if (onEnd) {
onEnd();
}
},
canDrag: active,
/**
* This is useful when the item is in a virtualized list.
* However, if we don't have an ID then we want the libraries
* defaults to take care of this.
*/ isDragging: item?.id ? (monitor)=>{
return item.id === monitor.getItem().id;
} : undefined,
collect: (monitor)=>({
isDragging: monitor.isDragging(),
initialOffset: monitor.getInitialClientOffset(),
currentOffset: monitor.getClientOffset(),
direction: getDragDirection(monitor)
})
});
const handleKeyDown = useKeyboardDragAndDrop(active, index, {
onGrabItem,
onDropItem,
onCancel,
onMoveItem
});
return [
{
handlerId,
isDragging,
handleKeyDown,
isOverDropTarget: isOver,
direction
},
objectRef,
dropRef,
dragRef,
dragPreviewRef
];
};
export { DIRECTIONS, DROP_SENSITIVITY, useDragAndDrop };
//# sourceMappingURL=useDragAndDrop.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,94 @@
'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);
/**
* Utility hook designed to implement keyboard accessibile drag and drop by
* returning an onKeyDown handler to be passed to the drag icon button.
*
* @internal - You should use `useDragAndDrop` instead.
*/ const useKeyboardDragAndDrop = (active, index, { onCancel, onDropItem, onGrabItem, onMoveItem })=>{
const [isSelected, setIsSelected] = React__namespace.useState(false);
const handleMove = (movement)=>{
if (!isSelected) {
return;
}
if (typeof index === 'number' && onMoveItem) {
if (movement === 'UP') {
onMoveItem(index - 1, index);
} else if (movement === 'DOWN') {
onMoveItem(index + 1, index);
}
}
};
const handleDragClick = ()=>{
if (isSelected) {
if (onDropItem) {
onDropItem(index);
}
setIsSelected(false);
} else {
if (onGrabItem) {
onGrabItem(index);
}
setIsSelected(true);
}
};
const handleCancel = ()=>{
if (isSelected) {
setIsSelected(false);
if (onCancel) {
onCancel(index);
}
}
};
const handleKeyDown = (e)=>{
if (!active) {
return;
}
if (e.key === 'Tab' && !isSelected) {
return;
}
e.preventDefault();
switch(e.key){
case ' ':
case 'Enter':
handleDragClick();
break;
case 'Escape':
handleCancel();
break;
case 'ArrowDown':
case 'ArrowRight':
handleMove('DOWN');
break;
case 'ArrowUp':
case 'ArrowLeft':
handleMove('UP');
break;
}
};
return handleKeyDown;
};
exports.useKeyboardDragAndDrop = useKeyboardDragAndDrop;
//# sourceMappingURL=useKeyboardDragAndDrop.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useKeyboardDragAndDrop.js","sources":["../../../../../admin/src/routes/settings/hooks/useKeyboardDragAndDrop.ts"],"sourcesContent":["import * as React from 'react';\n\nexport type UseKeyboardDragAndDropCallbacks<TIndex extends number | Array<number> = number> = {\n onCancel?: (index: TIndex) => void;\n onDropItem?: (currentIndex: TIndex, newIndex?: TIndex) => void;\n onGrabItem?: (index: TIndex) => void;\n onMoveItem?: (newIndex: TIndex, currentIndex: TIndex) => void;\n};\n\n/**\n * Utility hook designed to implement keyboard accessibile drag and drop by\n * returning an onKeyDown handler to be passed to the drag icon button.\n *\n * @internal - You should use `useDragAndDrop` instead.\n */\nexport const useKeyboardDragAndDrop = <TIndex extends number | Array<number> = number>(\n active: boolean,\n index: TIndex,\n { onCancel, onDropItem, onGrabItem, onMoveItem }: UseKeyboardDragAndDropCallbacks<TIndex>\n) => {\n const [isSelected, setIsSelected] = React.useState(false);\n\n const handleMove = (movement: 'UP' | 'DOWN') => {\n if (!isSelected) {\n return;\n }\n if (typeof index === 'number' && onMoveItem) {\n if (movement === 'UP') {\n onMoveItem((index - 1) as TIndex, index);\n } else if (movement === 'DOWN') {\n onMoveItem((index + 1) as TIndex, index);\n }\n }\n };\n\n const handleDragClick = () => {\n if (isSelected) {\n if (onDropItem) {\n onDropItem(index);\n }\n setIsSelected(false);\n } else {\n if (onGrabItem) {\n onGrabItem(index);\n }\n setIsSelected(true);\n }\n };\n\n const handleCancel = () => {\n if (isSelected) {\n setIsSelected(false);\n\n if (onCancel) {\n onCancel(index);\n }\n }\n };\n\n const handleKeyDown = <E extends Element>(e: React.KeyboardEvent<E>) => {\n if (!active) {\n return;\n }\n\n if (e.key === 'Tab' && !isSelected) {\n return;\n }\n\n e.preventDefault();\n\n switch (e.key) {\n case ' ':\n case 'Enter':\n handleDragClick();\n break;\n\n case 'Escape':\n handleCancel();\n break;\n\n case 'ArrowDown':\n case 'ArrowRight':\n handleMove('DOWN');\n break;\n\n case 'ArrowUp':\n case 'ArrowLeft':\n handleMove('UP');\n break;\n\n default:\n }\n };\n\n return handleKeyDown;\n};\n"],"names":["useKeyboardDragAndDrop","active","index","onCancel","onDropItem","onGrabItem","onMoveItem","isSelected","setIsSelected","React","useState","handleMove","movement","handleDragClick","handleCancel","handleKeyDown","e","key","preventDefault"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AASA;;;;;AAKC,IACM,MAAMA,sBAAyB,GAAA,CACpCC,QACAC,KACA,EAAA,EAAEC,QAAQ,EAAEC,UAAU,EAAEC,UAAU,EAAEC,UAAU,EAA2C,GAAA;AAEzF,IAAA,MAAM,CAACC,UAAYC,EAAAA,aAAAA,CAAc,GAAGC,gBAAAA,CAAMC,QAAQ,CAAC,KAAA,CAAA;AAEnD,IAAA,MAAMC,aAAa,CAACC,QAAAA,GAAAA;AAClB,QAAA,IAAI,CAACL,UAAY,EAAA;AACf,YAAA;AACF;QACA,IAAI,OAAOL,KAAU,KAAA,QAAA,IAAYI,UAAY,EAAA;AAC3C,YAAA,IAAIM,aAAa,IAAM,EAAA;AACrBN,gBAAAA,UAAAA,CAAYJ,QAAQ,CAAcA,EAAAA,KAAAA,CAAAA;aAC7B,MAAA,IAAIU,aAAa,MAAQ,EAAA;AAC9BN,gBAAAA,UAAAA,CAAYJ,QAAQ,CAAcA,EAAAA,KAAAA,CAAAA;AACpC;AACF;AACF,KAAA;AAEA,IAAA,MAAMW,eAAkB,GAAA,IAAA;AACtB,QAAA,IAAIN,UAAY,EAAA;AACd,YAAA,IAAIH,UAAY,EAAA;gBACdA,UAAWF,CAAAA,KAAAA,CAAAA;AACb;YACAM,aAAc,CAAA,KAAA,CAAA;SACT,MAAA;AACL,YAAA,IAAIH,UAAY,EAAA;gBACdA,UAAWH,CAAAA,KAAAA,CAAAA;AACb;YACAM,aAAc,CAAA,IAAA,CAAA;AAChB;AACF,KAAA;AAEA,IAAA,MAAMM,YAAe,GAAA,IAAA;AACnB,QAAA,IAAIP,UAAY,EAAA;YACdC,aAAc,CAAA,KAAA,CAAA;AAEd,YAAA,IAAIL,QAAU,EAAA;gBACZA,QAASD,CAAAA,KAAAA,CAAAA;AACX;AACF;AACF,KAAA;AAEA,IAAA,MAAMa,gBAAgB,CAAoBC,CAAAA,GAAAA;AACxC,QAAA,IAAI,CAACf,MAAQ,EAAA;AACX,YAAA;AACF;AAEA,QAAA,IAAIe,CAAEC,CAAAA,GAAG,KAAK,KAAA,IAAS,CAACV,UAAY,EAAA;AAClC,YAAA;AACF;AAEAS,QAAAA,CAAAA,CAAEE,cAAc,EAAA;AAEhB,QAAA,OAAQF,EAAEC,GAAG;YACX,KAAK,GAAA;YACL,KAAK,OAAA;AACHJ,gBAAAA,eAAAA,EAAAA;AACA,gBAAA;YAEF,KAAK,QAAA;AACHC,gBAAAA,YAAAA,EAAAA;AACA,gBAAA;YAEF,KAAK,WAAA;YACL,KAAK,YAAA;gBACHH,UAAW,CAAA,MAAA,CAAA;AACX,gBAAA;YAEF,KAAK,SAAA;YACL,KAAK,WAAA;gBACHA,UAAW,CAAA,IAAA,CAAA;AACX,gBAAA;AAGJ;AACF,KAAA;IAEA,OAAOI,aAAAA;AACT;;;;"}

View File

@@ -0,0 +1,73 @@
import * as React from 'react';
/**
* Utility hook designed to implement keyboard accessibile drag and drop by
* returning an onKeyDown handler to be passed to the drag icon button.
*
* @internal - You should use `useDragAndDrop` instead.
*/ const useKeyboardDragAndDrop = (active, index, { onCancel, onDropItem, onGrabItem, onMoveItem })=>{
const [isSelected, setIsSelected] = React.useState(false);
const handleMove = (movement)=>{
if (!isSelected) {
return;
}
if (typeof index === 'number' && onMoveItem) {
if (movement === 'UP') {
onMoveItem(index - 1, index);
} else if (movement === 'DOWN') {
onMoveItem(index + 1, index);
}
}
};
const handleDragClick = ()=>{
if (isSelected) {
if (onDropItem) {
onDropItem(index);
}
setIsSelected(false);
} else {
if (onGrabItem) {
onGrabItem(index);
}
setIsSelected(true);
}
};
const handleCancel = ()=>{
if (isSelected) {
setIsSelected(false);
if (onCancel) {
onCancel(index);
}
}
};
const handleKeyDown = (e)=>{
if (!active) {
return;
}
if (e.key === 'Tab' && !isSelected) {
return;
}
e.preventDefault();
switch(e.key){
case ' ':
case 'Enter':
handleDragClick();
break;
case 'Escape':
handleCancel();
break;
case 'ArrowDown':
case 'ArrowRight':
handleMove('DOWN');
break;
case 'ArrowUp':
case 'ArrowLeft':
handleMove('UP');
break;
}
};
return handleKeyDown;
};
export { useKeyboardDragAndDrop };
//# sourceMappingURL=useKeyboardDragAndDrop.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useKeyboardDragAndDrop.mjs","sources":["../../../../../admin/src/routes/settings/hooks/useKeyboardDragAndDrop.ts"],"sourcesContent":["import * as React from 'react';\n\nexport type UseKeyboardDragAndDropCallbacks<TIndex extends number | Array<number> = number> = {\n onCancel?: (index: TIndex) => void;\n onDropItem?: (currentIndex: TIndex, newIndex?: TIndex) => void;\n onGrabItem?: (index: TIndex) => void;\n onMoveItem?: (newIndex: TIndex, currentIndex: TIndex) => void;\n};\n\n/**\n * Utility hook designed to implement keyboard accessibile drag and drop by\n * returning an onKeyDown handler to be passed to the drag icon button.\n *\n * @internal - You should use `useDragAndDrop` instead.\n */\nexport const useKeyboardDragAndDrop = <TIndex extends number | Array<number> = number>(\n active: boolean,\n index: TIndex,\n { onCancel, onDropItem, onGrabItem, onMoveItem }: UseKeyboardDragAndDropCallbacks<TIndex>\n) => {\n const [isSelected, setIsSelected] = React.useState(false);\n\n const handleMove = (movement: 'UP' | 'DOWN') => {\n if (!isSelected) {\n return;\n }\n if (typeof index === 'number' && onMoveItem) {\n if (movement === 'UP') {\n onMoveItem((index - 1) as TIndex, index);\n } else if (movement === 'DOWN') {\n onMoveItem((index + 1) as TIndex, index);\n }\n }\n };\n\n const handleDragClick = () => {\n if (isSelected) {\n if (onDropItem) {\n onDropItem(index);\n }\n setIsSelected(false);\n } else {\n if (onGrabItem) {\n onGrabItem(index);\n }\n setIsSelected(true);\n }\n };\n\n const handleCancel = () => {\n if (isSelected) {\n setIsSelected(false);\n\n if (onCancel) {\n onCancel(index);\n }\n }\n };\n\n const handleKeyDown = <E extends Element>(e: React.KeyboardEvent<E>) => {\n if (!active) {\n return;\n }\n\n if (e.key === 'Tab' && !isSelected) {\n return;\n }\n\n e.preventDefault();\n\n switch (e.key) {\n case ' ':\n case 'Enter':\n handleDragClick();\n break;\n\n case 'Escape':\n handleCancel();\n break;\n\n case 'ArrowDown':\n case 'ArrowRight':\n handleMove('DOWN');\n break;\n\n case 'ArrowUp':\n case 'ArrowLeft':\n handleMove('UP');\n break;\n\n default:\n }\n };\n\n return handleKeyDown;\n};\n"],"names":["useKeyboardDragAndDrop","active","index","onCancel","onDropItem","onGrabItem","onMoveItem","isSelected","setIsSelected","React","useState","handleMove","movement","handleDragClick","handleCancel","handleKeyDown","e","key","preventDefault"],"mappings":";;AASA;;;;;AAKC,IACM,MAAMA,sBAAyB,GAAA,CACpCC,QACAC,KACA,EAAA,EAAEC,QAAQ,EAAEC,UAAU,EAAEC,UAAU,EAAEC,UAAU,EAA2C,GAAA;AAEzF,IAAA,MAAM,CAACC,UAAYC,EAAAA,aAAAA,CAAc,GAAGC,KAAAA,CAAMC,QAAQ,CAAC,KAAA,CAAA;AAEnD,IAAA,MAAMC,aAAa,CAACC,QAAAA,GAAAA;AAClB,QAAA,IAAI,CAACL,UAAY,EAAA;AACf,YAAA;AACF;QACA,IAAI,OAAOL,KAAU,KAAA,QAAA,IAAYI,UAAY,EAAA;AAC3C,YAAA,IAAIM,aAAa,IAAM,EAAA;AACrBN,gBAAAA,UAAAA,CAAYJ,QAAQ,CAAcA,EAAAA,KAAAA,CAAAA;aAC7B,MAAA,IAAIU,aAAa,MAAQ,EAAA;AAC9BN,gBAAAA,UAAAA,CAAYJ,QAAQ,CAAcA,EAAAA,KAAAA,CAAAA;AACpC;AACF;AACF,KAAA;AAEA,IAAA,MAAMW,eAAkB,GAAA,IAAA;AACtB,QAAA,IAAIN,UAAY,EAAA;AACd,YAAA,IAAIH,UAAY,EAAA;gBACdA,UAAWF,CAAAA,KAAAA,CAAAA;AACb;YACAM,aAAc,CAAA,KAAA,CAAA;SACT,MAAA;AACL,YAAA,IAAIH,UAAY,EAAA;gBACdA,UAAWH,CAAAA,KAAAA,CAAAA;AACb;YACAM,aAAc,CAAA,IAAA,CAAA;AAChB;AACF,KAAA;AAEA,IAAA,MAAMM,YAAe,GAAA,IAAA;AACnB,QAAA,IAAIP,UAAY,EAAA;YACdC,aAAc,CAAA,KAAA,CAAA;AAEd,YAAA,IAAIL,QAAU,EAAA;gBACZA,QAASD,CAAAA,KAAAA,CAAAA;AACX;AACF;AACF,KAAA;AAEA,IAAA,MAAMa,gBAAgB,CAAoBC,CAAAA,GAAAA;AACxC,QAAA,IAAI,CAACf,MAAQ,EAAA;AACX,YAAA;AACF;AAEA,QAAA,IAAIe,CAAEC,CAAAA,GAAG,KAAK,KAAA,IAAS,CAACV,UAAY,EAAA;AAClC,YAAA;AACF;AAEAS,QAAAA,CAAAA,CAAEE,cAAc,EAAA;AAEhB,QAAA,OAAQF,EAAEC,GAAG;YACX,KAAK,GAAA;YACL,KAAK,OAAA;AACHJ,gBAAAA,eAAAA,EAAAA;AACA,gBAAA;YAEF,KAAK,QAAA;AACHC,gBAAAA,YAAAA,EAAAA;AACA,gBAAA;YAEF,KAAK,WAAA;YACL,KAAK,YAAA;gBACHH,UAAW,CAAA,MAAA,CAAA;AACX,gBAAA;YAEF,KAAK,SAAA;YACL,KAAK,WAAA;gBACHA,UAAW,CAAA,IAAA,CAAA;AACX,gBAAA;AAGJ;AACF,KAAA;IAEA,OAAOI,aAAAA;AACT;;;;"}

View File

@@ -0,0 +1,171 @@
'use strict';
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var reactIntl = require('react-intl');
var settings = require('../../../services/settings.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 DEFAULT_UNEXPECTED_ERROR_MSG = {
id: 'notification.error',
defaultMessage: 'An error occurred, please try again'
};
const useReviewWorkflows = (params = {})=>{
const { toggleNotification } = strapiAdmin.useNotification();
const { formatMessage } = reactIntl.useIntl();
const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
const { skip = false, ...queryParams } = params;
const { data, isLoading, error } = settings.useGetWorkflowsQuery({
...queryParams
}, {
skip
});
React__namespace.useEffect(()=>{
if (error) {
toggleNotification({
type: 'danger',
message: formatAPIError(error)
});
}
}, [
error,
formatAPIError,
toggleNotification
]);
const [createWorkflow] = settings.useCreateWorkflowMutation();
const create = React__namespace.useCallback(async (data)=>{
try {
const res = await createWorkflow({
data
});
if ('error' in res) {
toggleNotification({
type: 'danger',
message: formatAPIError(res.error)
});
return res;
}
toggleNotification({
type: 'success',
message: formatMessage({
id: 'actions.created',
defaultMessage: 'Created workflow'
})
});
return res;
} catch (err) {
toggleNotification({
type: 'danger',
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
});
throw err;
}
}, [
createWorkflow,
formatAPIError,
formatMessage,
toggleNotification
]);
const [updateWorkflow] = settings.useUpdateWorkflowMutation();
const update = React__namespace.useCallback(async (id, data)=>{
try {
const res = await updateWorkflow({
id,
data
});
if ('error' in res) {
toggleNotification({
type: 'danger',
message: formatAPIError(res.error)
});
return res;
}
toggleNotification({
type: 'success',
message: formatMessage({
id: 'actions.updated',
defaultMessage: 'Updated workflow'
})
});
return res;
} catch (err) {
toggleNotification({
type: 'danger',
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
});
throw err;
}
}, [
formatAPIError,
formatMessage,
toggleNotification,
updateWorkflow
]);
const [deleteWorkflow] = settings.useDeleteWorkflowMutation();
const deleteAction = React__namespace.useCallback(async (id)=>{
try {
const res = await deleteWorkflow({
id
});
if ('error' in res) {
toggleNotification({
type: 'danger',
message: formatAPIError(res.error)
});
return;
}
toggleNotification({
type: 'success',
message: formatMessage({
id: 'actions.deleted',
defaultMessage: 'Deleted workflow'
})
});
return res.data;
} catch (err) {
toggleNotification({
type: 'danger',
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
});
throw err;
}
}, [
deleteWorkflow,
formatAPIError,
formatMessage,
toggleNotification
]);
const { workflows = [], meta } = data ?? {};
return {
// meta contains e.g. the total of all workflows. we can not use
// the pagination object here, because the list is not paginated.
meta,
workflows,
isLoading,
error,
create,
delete: deleteAction,
update
};
};
exports.useReviewWorkflows = useReviewWorkflows;
//# sourceMappingURL=useReviewWorkflows.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,150 @@
import * as React from 'react';
import { useNotification, useAPIErrorHandler } from '@strapi/admin/strapi-admin';
import { useIntl } from 'react-intl';
import { useGetWorkflowsQuery, useCreateWorkflowMutation, useUpdateWorkflowMutation, useDeleteWorkflowMutation } from '../../../services/settings.mjs';
const DEFAULT_UNEXPECTED_ERROR_MSG = {
id: 'notification.error',
defaultMessage: 'An error occurred, please try again'
};
const useReviewWorkflows = (params = {})=>{
const { toggleNotification } = useNotification();
const { formatMessage } = useIntl();
const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
const { skip = false, ...queryParams } = params;
const { data, isLoading, error } = useGetWorkflowsQuery({
...queryParams
}, {
skip
});
React.useEffect(()=>{
if (error) {
toggleNotification({
type: 'danger',
message: formatAPIError(error)
});
}
}, [
error,
formatAPIError,
toggleNotification
]);
const [createWorkflow] = useCreateWorkflowMutation();
const create = React.useCallback(async (data)=>{
try {
const res = await createWorkflow({
data
});
if ('error' in res) {
toggleNotification({
type: 'danger',
message: formatAPIError(res.error)
});
return res;
}
toggleNotification({
type: 'success',
message: formatMessage({
id: 'actions.created',
defaultMessage: 'Created workflow'
})
});
return res;
} catch (err) {
toggleNotification({
type: 'danger',
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
});
throw err;
}
}, [
createWorkflow,
formatAPIError,
formatMessage,
toggleNotification
]);
const [updateWorkflow] = useUpdateWorkflowMutation();
const update = React.useCallback(async (id, data)=>{
try {
const res = await updateWorkflow({
id,
data
});
if ('error' in res) {
toggleNotification({
type: 'danger',
message: formatAPIError(res.error)
});
return res;
}
toggleNotification({
type: 'success',
message: formatMessage({
id: 'actions.updated',
defaultMessage: 'Updated workflow'
})
});
return res;
} catch (err) {
toggleNotification({
type: 'danger',
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
});
throw err;
}
}, [
formatAPIError,
formatMessage,
toggleNotification,
updateWorkflow
]);
const [deleteWorkflow] = useDeleteWorkflowMutation();
const deleteAction = React.useCallback(async (id)=>{
try {
const res = await deleteWorkflow({
id
});
if ('error' in res) {
toggleNotification({
type: 'danger',
message: formatAPIError(res.error)
});
return;
}
toggleNotification({
type: 'success',
message: formatMessage({
id: 'actions.deleted',
defaultMessage: 'Deleted workflow'
})
});
return res.data;
} catch (err) {
toggleNotification({
type: 'danger',
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
});
throw err;
}
}, [
deleteWorkflow,
formatAPIError,
formatMessage,
toggleNotification
]);
const { workflows = [], meta } = data ?? {};
return {
// meta contains e.g. the total of all workflows. we can not use
// the pagination object here, because the list is not paginated.
meta,
workflows,
isLoading,
error,
create,
delete: deleteAction,
update
};
};
export { useReviewWorkflows };
//# sourceMappingURL=useReviewWorkflows.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,404 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var ee = require('@strapi/admin/strapi-admin/ee');
var designSystem = require('@strapi/design-system');
var icons = require('@strapi/icons');
var fractionalIndexing = require('fractional-indexing');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var yup = require('yup');
var LimitsModal = require('../../components/LimitsModal.js');
var constants = require('../../constants.js');
var hooks = require('../../modules/hooks.js');
var api = require('../../utils/api.js');
var Layout = require('./components/Layout.js');
var Stages = require('./components/Stages.js');
var WorkflowAttributes = require('./components/WorkflowAttributes.js');
var useReviewWorkflows = require('./hooks/useReviewWorkflows.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);
var yup__namespace = /*#__PURE__*/_interopNamespaceDefault(yup);
/* -------------------------------------------------------------------------------------------------
* EditPage
* -----------------------------------------------------------------------------------------------*/ const WORKFLOW_SCHEMA = yup__namespace.object({
contentTypes: yup__namespace.array().of(yup__namespace.string()),
name: yup__namespace.string().max(255, {
id: 'review-workflows.validation.name.max-length',
defaultMessage: 'Name can not be longer than 255 characters'
}).required().nullable(),
stages: yup__namespace.array().of(yup__namespace.object().shape({
name: yup__namespace.string().nullable().required({
id: 'review-workflows.validation.stage.name',
defaultMessage: 'Name is required'
}).max(255, {
id: 'review-workflows.validation.stage.max-length',
defaultMessage: 'Name can not be longer than 255 characters'
}).test('unique-name', {
id: 'review-workflows.validation.stage.duplicate',
defaultMessage: 'Stage name must be unique'
}, (stageName, context)=>{
// @ts-expect-error it does exist.
const { stages } = context.from[1].value;
return stages.filter((stage)=>stage.name === stageName).length === 1;
}),
color: yup__namespace.string().nullable().required({
id: 'review-workflows.validation.stage.color',
defaultMessage: 'Color is required'
}).matches(/^#(?:[0-9a-fA-F]{3}){1,2}$/i),
permissions: yup__namespace.array(yup__namespace.object({
role: yup__namespace.number().strict().typeError({
id: 'review-workflows.validation.stage.permissions.role.number',
defaultMessage: 'Role must be of type number'
}).required(),
action: yup__namespace.string().required({
id: 'review-workflows.validation.stage.permissions.action.required',
defaultMessage: 'Action is a required argument'
})
})).strict()
})).min(1),
stageRequiredToPublish: yup__namespace.string().nullable()
});
const EditPage = ()=>{
const { id = '' } = reactRouterDom.useParams();
const isCreatingWorkflow = id === 'create';
const { formatMessage } = reactIntl.useIntl();
const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
const navigate = reactRouterDom.useNavigate();
const { toggleNotification } = strapiAdmin.useNotification();
const { isLoading: isLoadingWorkflow, meta, workflows, error, update, create } = useReviewWorkflows.useReviewWorkflows();
const permissions = hooks.useTypedSelector((state)=>state.admin_app.permissions['settings']?.['review-workflows']);
const { allowedActions: { canDelete, canUpdate, canCreate } } = strapiAdmin.useRBAC(permissions);
const [savePrompts, setSavePrompts] = React__namespace.useState({});
const { getFeature, isLoading: isLicenseLoading } = ee.useLicenseLimits();
const [showLimitModal, setShowLimitModal] = React__namespace.useState(null);
const currentWorkflow = workflows?.find((workflow)=>workflow.id === parseInt(id, 10));
const contentTypesFromOtherWorkflows = workflows?.filter((workflow)=>workflow.id !== parseInt(id, 10)).flatMap((workflow)=>workflow.contentTypes);
const limits = getFeature('review-workflows');
const numberOfWorkflows = limits?.[constants.CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME];
const stagesPerWorkflow = limits?.[constants.CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME];
const submitForm = async (data, helpers)=>{
try {
const { stageRequiredToPublish, ...rest } = data;
const stageRequiredToPublishName = stageRequiredToPublish === '' ? null : rest.stages.find((stage)=>stage.id === Number(stageRequiredToPublish) || stage.__temp_key__ === stageRequiredToPublish)?.name;
if (!isCreatingWorkflow) {
const res = await update(id, {
...rest,
// compare permissions of stages and only submit them if at least one has
// changed; this enables partial updates e.g. for users who don't have
// permissions to see roles
stages: rest.stages.map((stage)=>{
let hasUpdatedPermissions = true;
const serverStage = currentWorkflow?.stages?.find((serverStage)=>serverStage.id === stage?.id);
if (serverStage) {
hasUpdatedPermissions = serverStage.permissions?.length !== stage.permissions?.length || !serverStage.permissions?.every((serverPermission)=>!!stage.permissions?.find((permission)=>permission.role === serverPermission.role));
}
return {
...stage,
permissions: hasUpdatedPermissions ? stage.permissions : undefined
};
}),
stageRequiredToPublishName
});
if ('error' in res && api.isBaseQueryError(res.error) && res.error.name === 'ValidationError') {
helpers.setErrors(formatValidationErrors(res.error));
}
} else {
const res = await create({
...rest,
stageRequiredToPublishName
});
if ('error' in res && api.isBaseQueryError(res.error) && res.error.name === 'ValidationError') {
helpers.setErrors(formatValidationErrors(res.error));
} else if ('data' in res) {
navigate(`../${res.data.id}`, {
replace: true
});
}
}
} catch (error) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
setSavePrompts({});
};
const handleConfirmDeleteDialog = (data, helpers)=>async ()=>{
await submitForm(data, helpers);
};
const handleConfirmClose = ()=>{
setSavePrompts({});
};
const handleSubmit = async (data, helpers)=>{
const isContentTypeReassignment = data.contentTypes.some((contentType)=>contentTypesFromOtherWorkflows?.includes(contentType));
const hasDeletedServerStages = !isCreatingWorkflow && !currentWorkflow?.stages.every((stage)=>data.stages.some((newStage)=>newStage.id === stage.id));
if (meta && numberOfWorkflows && meta?.workflowCount > parseInt(numberOfWorkflows, 10)) {
/**
* If the current license has a limit, check if the total count of workflows
* exceeds that limit and display the limits modal instead of sending the
* update, because it would throw an API error.
*/ setShowLimitModal('workflow');
/**
* If the current license has a limit, check if the total count of stages
* exceeds that limit and display the limits modal instead of sending the
* update, because it would throw an API error.
*/ } else if (data.stages && stagesPerWorkflow && data.stages.length > parseInt(stagesPerWorkflow, 10)) {
setShowLimitModal('stage');
} else if (hasDeletedServerStages || isContentTypeReassignment) {
if (hasDeletedServerStages) {
setSavePrompts((prev)=>({
...prev,
hasDeletedServerStages: true
}));
}
if (isContentTypeReassignment) {
setSavePrompts((prev)=>({
...prev,
hasReassignedContentTypes: true
}));
}
} else {
await submitForm(data, helpers);
}
};
/**
* If the current license has a limit:
* check if the total count of workflows or stages exceeds that limit and display
* the limits modal on page load. It can be closed by the user, but the
* API will throw an error in case they try to create a new workflow or update the
* stages.
*
* If the current license does not have a limit (e.g. offline license):
* do nothing (for now). In case they are trying to create the 201st workflow/ stage
* the API will throw an error.
*
*/ React__namespace.useEffect(()=>{
if (!isLoadingWorkflow && !isLicenseLoading) {
if (meta && numberOfWorkflows && meta?.workflowCount > parseInt(numberOfWorkflows, 10)) {
setShowLimitModal('workflow');
} else if (currentWorkflow && currentWorkflow.stages && stagesPerWorkflow && currentWorkflow.stages.length > parseInt(stagesPerWorkflow, 10)) {
setShowLimitModal('stage');
}
}
}, [
currentWorkflow,
isLicenseLoading,
isLoadingWorkflow,
limits,
meta,
numberOfWorkflows,
stagesPerWorkflow
]);
const initialValues = React__namespace.useMemo(()=>{
if (isCreatingWorkflow || !currentWorkflow) {
return {
name: '',
stages: [],
contentTypes: [],
stageRequiredToPublish: ''
};
} else {
return {
name: currentWorkflow.name,
stages: addTmpKeysToStages(currentWorkflow.stages),
contentTypes: currentWorkflow.contentTypes,
stageRequiredToPublish: currentWorkflow.stageRequiredToPublish?.id.toString() ?? ''
};
}
}, [
currentWorkflow,
isCreatingWorkflow
]);
if (isLoadingWorkflow) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Loading, {});
}
if (error) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Error, {});
}
return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(Layout.DragLayerRendered, {}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Form, {
method: isCreatingWorkflow ? 'POST' : 'PUT',
initialValues: initialValues,
validationSchema: WORKFLOW_SCHEMA,
onSubmit: handleSubmit,
children: ({ modified, isSubmitting, values, setErrors })=>/*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(Layout.Header, {
navigationAction: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.BackButton, {
fallback: ".."
}),
primaryAction: canUpdate || canCreate ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.Check, {}),
type: "submit",
disabled: !modified || isSubmitting || values.stages.length === 0,
// if the confirm dialog is open the loading state is on
// the confirm button already
loading: !Boolean(Object.keys(savePrompts).length > 0) && isSubmitting,
children: formatMessage({
id: 'global.save',
defaultMessage: 'Save'
})
}) : null,
subtitle: formatMessage({
id: 'review-workflows.page.subtitle',
defaultMessage: '{count, plural, one {# stage} other {# stages}}'
}, {
count: currentWorkflow?.stages?.length ?? 0
}),
title: currentWorkflow?.name || formatMessage({
id: 'Settings.review-workflows.create.page.title',
defaultMessage: 'Create Review Workflow'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(Layout.Root, {
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
alignItems: "stretch",
direction: "column",
gap: 7,
children: [
/*#__PURE__*/ jsxRuntime.jsx(WorkflowAttributes.WorkflowAttributes, {
canUpdate: canUpdate || canCreate
}),
/*#__PURE__*/ jsxRuntime.jsx(Stages.Stages, {
canDelete: canDelete,
canUpdate: canUpdate || canCreate,
isCreating: isCreatingWorkflow
})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Root, {
open: Object.keys(savePrompts).length > 0,
onOpenChange: handleConfirmClose,
children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.ConfirmDialog, {
onConfirm: handleConfirmDeleteDialog(values, {
setErrors
}),
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
gap: 5,
children: [
savePrompts.hasDeletedServerStages && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textAlign: "center",
variant: "omega",
children: formatMessage({
id: 'review-workflows.page.delete.confirm.stages.body',
defaultMessage: 'All entries assigned to deleted stages will be moved to the previous stage.'
})
}),
savePrompts.hasReassignedContentTypes && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textAlign: "center",
variant: "omega",
children: formatMessage({
id: 'review-workflows.page.delete.confirm.contentType.body',
defaultMessage: '{count} {count, plural, one {content-type} other {content-types}} {count, plural, one {is} other {are}} already mapped to {count, plural, one {another workflow} other {other workflows}}. If you save changes, {count, plural, one {this} other {these}} {count, plural, one {content-type} other {{count} content-types}} will no more be mapped to the {count, plural, one {another workflow} other {other workflows}} and all corresponding information will be removed.'
}, {
count: contentTypesFromOtherWorkflows?.filter((contentType)=>values.contentTypes.includes(contentType)).length ?? 0
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textAlign: "center",
variant: "omega",
children: formatMessage({
id: 'review-workflows.page.delete.confirm.confirm',
defaultMessage: 'Are you sure you want to save?'
})
})
]
})
})
})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(LimitsModal.LimitsModal.Root, {
open: showLimitModal === 'workflow',
onOpenChange: ()=>setShowLimitModal(null),
children: [
/*#__PURE__*/ jsxRuntime.jsx(LimitsModal.LimitsModal.Title, {
children: formatMessage({
id: 'review-workflows.edit.page.workflows.limit.title',
defaultMessage: 'Youve reached the limit of workflows in your plan'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(LimitsModal.LimitsModal.Body, {
children: formatMessage({
id: 'review-workflows.edit.page.workflows.limit.body',
defaultMessage: 'Delete a workflow or contact Sales to enable more workflows.'
})
})
]
}),
/*#__PURE__*/ jsxRuntime.jsxs(LimitsModal.LimitsModal.Root, {
open: showLimitModal === 'stage',
onOpenChange: ()=>setShowLimitModal(null),
children: [
/*#__PURE__*/ jsxRuntime.jsx(LimitsModal.LimitsModal.Title, {
children: formatMessage({
id: 'review-workflows.edit.page.stages.limit.title',
defaultMessage: 'You have reached the limit of stages for this workflow in your plan'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(LimitsModal.LimitsModal.Body, {
children: formatMessage({
id: 'review-workflows.edit.page.stages.limit.body',
defaultMessage: 'Try deleting some stages or contact Sales to enable more stages.'
})
})
]
})
]
});
};
const addTmpKeysToStages = (data)=>{
const keys = fractionalIndexing.generateNKeysBetween(undefined, undefined, data.length);
return data.map((datum, index)=>({
...datum,
__temp_key__: keys[index]
}));
};
/* -------------------------------------------------------------------------------------------------
* ProtectedEditPage
* -----------------------------------------------------------------------------------------------*/ const ProtectedEditPage = ()=>{
const permissions = hooks.useTypedSelector((state)=>{
const { create = [], update = [], read = [] } = state.admin_app.permissions.settings?.['review-workflows'] ?? {};
return [
...create,
...update,
...read
];
});
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Protect, {
permissions: permissions,
children: /*#__PURE__*/ jsxRuntime.jsx(EditPage, {})
});
};
exports.ProtectedEditPage = ProtectedEditPage;
//# sourceMappingURL=id.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,382 @@
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import * as React from 'react';
import { Page, useAPIErrorHandler, useNotification, useRBAC, Form, BackButton, ConfirmDialog } from '@strapi/admin/strapi-admin';
import { useLicenseLimits } from '@strapi/admin/strapi-admin/ee';
import { Button, Flex, Dialog, Typography } from '@strapi/design-system';
import { Check } from '@strapi/icons';
import { generateNKeysBetween } from 'fractional-indexing';
import { useIntl } from 'react-intl';
import { useParams, useNavigate } from 'react-router-dom';
import * as yup from 'yup';
import { LimitsModal } from '../../components/LimitsModal.mjs';
import { CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME, CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME } from '../../constants.mjs';
import { useTypedSelector } from '../../modules/hooks.mjs';
import { isBaseQueryError } from '../../utils/api.mjs';
import { DragLayerRendered, Header, Root } from './components/Layout.mjs';
import { Stages } from './components/Stages.mjs';
import { WorkflowAttributes } from './components/WorkflowAttributes.mjs';
import { useReviewWorkflows } from './hooks/useReviewWorkflows.mjs';
/* -------------------------------------------------------------------------------------------------
* EditPage
* -----------------------------------------------------------------------------------------------*/ const WORKFLOW_SCHEMA = yup.object({
contentTypes: yup.array().of(yup.string()),
name: yup.string().max(255, {
id: 'review-workflows.validation.name.max-length',
defaultMessage: 'Name can not be longer than 255 characters'
}).required().nullable(),
stages: yup.array().of(yup.object().shape({
name: yup.string().nullable().required({
id: 'review-workflows.validation.stage.name',
defaultMessage: 'Name is required'
}).max(255, {
id: 'review-workflows.validation.stage.max-length',
defaultMessage: 'Name can not be longer than 255 characters'
}).test('unique-name', {
id: 'review-workflows.validation.stage.duplicate',
defaultMessage: 'Stage name must be unique'
}, (stageName, context)=>{
// @ts-expect-error it does exist.
const { stages } = context.from[1].value;
return stages.filter((stage)=>stage.name === stageName).length === 1;
}),
color: yup.string().nullable().required({
id: 'review-workflows.validation.stage.color',
defaultMessage: 'Color is required'
}).matches(/^#(?:[0-9a-fA-F]{3}){1,2}$/i),
permissions: yup.array(yup.object({
role: yup.number().strict().typeError({
id: 'review-workflows.validation.stage.permissions.role.number',
defaultMessage: 'Role must be of type number'
}).required(),
action: yup.string().required({
id: 'review-workflows.validation.stage.permissions.action.required',
defaultMessage: 'Action is a required argument'
})
})).strict()
})).min(1),
stageRequiredToPublish: yup.string().nullable()
});
const EditPage = ()=>{
const { id = '' } = useParams();
const isCreatingWorkflow = id === 'create';
const { formatMessage } = useIntl();
const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
const navigate = useNavigate();
const { toggleNotification } = useNotification();
const { isLoading: isLoadingWorkflow, meta, workflows, error, update, create } = useReviewWorkflows();
const permissions = useTypedSelector((state)=>state.admin_app.permissions['settings']?.['review-workflows']);
const { allowedActions: { canDelete, canUpdate, canCreate } } = useRBAC(permissions);
const [savePrompts, setSavePrompts] = React.useState({});
const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits();
const [showLimitModal, setShowLimitModal] = React.useState(null);
const currentWorkflow = workflows?.find((workflow)=>workflow.id === parseInt(id, 10));
const contentTypesFromOtherWorkflows = workflows?.filter((workflow)=>workflow.id !== parseInt(id, 10)).flatMap((workflow)=>workflow.contentTypes);
const limits = getFeature('review-workflows');
const numberOfWorkflows = limits?.[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME];
const stagesPerWorkflow = limits?.[CHARGEBEE_STAGES_PER_WORKFLOW_ENTITLEMENT_NAME];
const submitForm = async (data, helpers)=>{
try {
const { stageRequiredToPublish, ...rest } = data;
const stageRequiredToPublishName = stageRequiredToPublish === '' ? null : rest.stages.find((stage)=>stage.id === Number(stageRequiredToPublish) || stage.__temp_key__ === stageRequiredToPublish)?.name;
if (!isCreatingWorkflow) {
const res = await update(id, {
...rest,
// compare permissions of stages and only submit them if at least one has
// changed; this enables partial updates e.g. for users who don't have
// permissions to see roles
stages: rest.stages.map((stage)=>{
let hasUpdatedPermissions = true;
const serverStage = currentWorkflow?.stages?.find((serverStage)=>serverStage.id === stage?.id);
if (serverStage) {
hasUpdatedPermissions = serverStage.permissions?.length !== stage.permissions?.length || !serverStage.permissions?.every((serverPermission)=>!!stage.permissions?.find((permission)=>permission.role === serverPermission.role));
}
return {
...stage,
permissions: hasUpdatedPermissions ? stage.permissions : undefined
};
}),
stageRequiredToPublishName
});
if ('error' in res && isBaseQueryError(res.error) && res.error.name === 'ValidationError') {
helpers.setErrors(formatValidationErrors(res.error));
}
} else {
const res = await create({
...rest,
stageRequiredToPublishName
});
if ('error' in res && isBaseQueryError(res.error) && res.error.name === 'ValidationError') {
helpers.setErrors(formatValidationErrors(res.error));
} else if ('data' in res) {
navigate(`../${res.data.id}`, {
replace: true
});
}
}
} catch (error) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
setSavePrompts({});
};
const handleConfirmDeleteDialog = (data, helpers)=>async ()=>{
await submitForm(data, helpers);
};
const handleConfirmClose = ()=>{
setSavePrompts({});
};
const handleSubmit = async (data, helpers)=>{
const isContentTypeReassignment = data.contentTypes.some((contentType)=>contentTypesFromOtherWorkflows?.includes(contentType));
const hasDeletedServerStages = !isCreatingWorkflow && !currentWorkflow?.stages.every((stage)=>data.stages.some((newStage)=>newStage.id === stage.id));
if (meta && numberOfWorkflows && meta?.workflowCount > parseInt(numberOfWorkflows, 10)) {
/**
* If the current license has a limit, check if the total count of workflows
* exceeds that limit and display the limits modal instead of sending the
* update, because it would throw an API error.
*/ setShowLimitModal('workflow');
/**
* If the current license has a limit, check if the total count of stages
* exceeds that limit and display the limits modal instead of sending the
* update, because it would throw an API error.
*/ } else if (data.stages && stagesPerWorkflow && data.stages.length > parseInt(stagesPerWorkflow, 10)) {
setShowLimitModal('stage');
} else if (hasDeletedServerStages || isContentTypeReassignment) {
if (hasDeletedServerStages) {
setSavePrompts((prev)=>({
...prev,
hasDeletedServerStages: true
}));
}
if (isContentTypeReassignment) {
setSavePrompts((prev)=>({
...prev,
hasReassignedContentTypes: true
}));
}
} else {
await submitForm(data, helpers);
}
};
/**
* If the current license has a limit:
* check if the total count of workflows or stages exceeds that limit and display
* the limits modal on page load. It can be closed by the user, but the
* API will throw an error in case they try to create a new workflow or update the
* stages.
*
* If the current license does not have a limit (e.g. offline license):
* do nothing (for now). In case they are trying to create the 201st workflow/ stage
* the API will throw an error.
*
*/ React.useEffect(()=>{
if (!isLoadingWorkflow && !isLicenseLoading) {
if (meta && numberOfWorkflows && meta?.workflowCount > parseInt(numberOfWorkflows, 10)) {
setShowLimitModal('workflow');
} else if (currentWorkflow && currentWorkflow.stages && stagesPerWorkflow && currentWorkflow.stages.length > parseInt(stagesPerWorkflow, 10)) {
setShowLimitModal('stage');
}
}
}, [
currentWorkflow,
isLicenseLoading,
isLoadingWorkflow,
limits,
meta,
numberOfWorkflows,
stagesPerWorkflow
]);
const initialValues = React.useMemo(()=>{
if (isCreatingWorkflow || !currentWorkflow) {
return {
name: '',
stages: [],
contentTypes: [],
stageRequiredToPublish: ''
};
} else {
return {
name: currentWorkflow.name,
stages: addTmpKeysToStages(currentWorkflow.stages),
contentTypes: currentWorkflow.contentTypes,
stageRequiredToPublish: currentWorkflow.stageRequiredToPublish?.id.toString() ?? ''
};
}
}, [
currentWorkflow,
isCreatingWorkflow
]);
if (isLoadingWorkflow) {
return /*#__PURE__*/ jsx(Page.Loading, {});
}
if (error) {
return /*#__PURE__*/ jsx(Page.Error, {});
}
return /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsx(DragLayerRendered, {}),
/*#__PURE__*/ jsx(Form, {
method: isCreatingWorkflow ? 'POST' : 'PUT',
initialValues: initialValues,
validationSchema: WORKFLOW_SCHEMA,
onSubmit: handleSubmit,
children: ({ modified, isSubmitting, values, setErrors })=>/*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsx(Header, {
navigationAction: /*#__PURE__*/ jsx(BackButton, {
fallback: ".."
}),
primaryAction: canUpdate || canCreate ? /*#__PURE__*/ jsx(Button, {
startIcon: /*#__PURE__*/ jsx(Check, {}),
type: "submit",
disabled: !modified || isSubmitting || values.stages.length === 0,
// if the confirm dialog is open the loading state is on
// the confirm button already
loading: !Boolean(Object.keys(savePrompts).length > 0) && isSubmitting,
children: formatMessage({
id: 'global.save',
defaultMessage: 'Save'
})
}) : null,
subtitle: formatMessage({
id: 'review-workflows.page.subtitle',
defaultMessage: '{count, plural, one {# stage} other {# stages}}'
}, {
count: currentWorkflow?.stages?.length ?? 0
}),
title: currentWorkflow?.name || formatMessage({
id: 'Settings.review-workflows.create.page.title',
defaultMessage: 'Create Review Workflow'
})
}),
/*#__PURE__*/ jsx(Root, {
children: /*#__PURE__*/ jsxs(Flex, {
alignItems: "stretch",
direction: "column",
gap: 7,
children: [
/*#__PURE__*/ jsx(WorkflowAttributes, {
canUpdate: canUpdate || canCreate
}),
/*#__PURE__*/ jsx(Stages, {
canDelete: canDelete,
canUpdate: canUpdate || canCreate,
isCreating: isCreatingWorkflow
})
]
})
}),
/*#__PURE__*/ jsx(Dialog.Root, {
open: Object.keys(savePrompts).length > 0,
onOpenChange: handleConfirmClose,
children: /*#__PURE__*/ jsx(ConfirmDialog, {
onConfirm: handleConfirmDeleteDialog(values, {
setErrors
}),
children: /*#__PURE__*/ jsxs(Flex, {
direction: "column",
gap: 5,
children: [
savePrompts.hasDeletedServerStages && /*#__PURE__*/ jsx(Typography, {
textAlign: "center",
variant: "omega",
children: formatMessage({
id: 'review-workflows.page.delete.confirm.stages.body',
defaultMessage: 'All entries assigned to deleted stages will be moved to the previous stage.'
})
}),
savePrompts.hasReassignedContentTypes && /*#__PURE__*/ jsx(Typography, {
textAlign: "center",
variant: "omega",
children: formatMessage({
id: 'review-workflows.page.delete.confirm.contentType.body',
defaultMessage: '{count} {count, plural, one {content-type} other {content-types}} {count, plural, one {is} other {are}} already mapped to {count, plural, one {another workflow} other {other workflows}}. If you save changes, {count, plural, one {this} other {these}} {count, plural, one {content-type} other {{count} content-types}} will no more be mapped to the {count, plural, one {another workflow} other {other workflows}} and all corresponding information will be removed.'
}, {
count: contentTypesFromOtherWorkflows?.filter((contentType)=>values.contentTypes.includes(contentType)).length ?? 0
})
}),
/*#__PURE__*/ jsx(Typography, {
textAlign: "center",
variant: "omega",
children: formatMessage({
id: 'review-workflows.page.delete.confirm.confirm',
defaultMessage: 'Are you sure you want to save?'
})
})
]
})
})
})
]
})
}),
/*#__PURE__*/ jsxs(LimitsModal.Root, {
open: showLimitModal === 'workflow',
onOpenChange: ()=>setShowLimitModal(null),
children: [
/*#__PURE__*/ jsx(LimitsModal.Title, {
children: formatMessage({
id: 'review-workflows.edit.page.workflows.limit.title',
defaultMessage: 'Youve reached the limit of workflows in your plan'
})
}),
/*#__PURE__*/ jsx(LimitsModal.Body, {
children: formatMessage({
id: 'review-workflows.edit.page.workflows.limit.body',
defaultMessage: 'Delete a workflow or contact Sales to enable more workflows.'
})
})
]
}),
/*#__PURE__*/ jsxs(LimitsModal.Root, {
open: showLimitModal === 'stage',
onOpenChange: ()=>setShowLimitModal(null),
children: [
/*#__PURE__*/ jsx(LimitsModal.Title, {
children: formatMessage({
id: 'review-workflows.edit.page.stages.limit.title',
defaultMessage: 'You have reached the limit of stages for this workflow in your plan'
})
}),
/*#__PURE__*/ jsx(LimitsModal.Body, {
children: formatMessage({
id: 'review-workflows.edit.page.stages.limit.body',
defaultMessage: 'Try deleting some stages or contact Sales to enable more stages.'
})
})
]
})
]
});
};
const addTmpKeysToStages = (data)=>{
const keys = generateNKeysBetween(undefined, undefined, data.length);
return data.map((datum, index)=>({
...datum,
__temp_key__: keys[index]
}));
};
/* -------------------------------------------------------------------------------------------------
* ProtectedEditPage
* -----------------------------------------------------------------------------------------------*/ const ProtectedEditPage = ()=>{
const permissions = useTypedSelector((state)=>{
const { create = [], update = [], read = [] } = state.admin_app.permissions.settings?.['review-workflows'] ?? {};
return [
...create,
...update,
...read
];
});
return /*#__PURE__*/ jsx(Page.Protect, {
permissions: permissions,
children: /*#__PURE__*/ jsx(EditPage, {})
});
};
export { ProtectedEditPage };
//# sourceMappingURL=id.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,293 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var ee = require('@strapi/admin/strapi-admin/ee');
var designSystem = require('@strapi/design-system');
var icons = require('@strapi/icons');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var LimitsModal = require('../../components/LimitsModal.js');
var constants = require('../../constants.js');
var hooks = require('../../modules/hooks.js');
var contentManager = require('../../services/content-manager.js');
var Layout = require('./components/Layout.js');
var useReviewWorkflows = require('./hooks/useReviewWorkflows.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 ReviewWorkflowsListView = ()=>{
const { formatMessage } = reactIntl.useIntl();
const navigate = reactRouterDom.useNavigate();
const { trackUsage } = strapiAdmin.useTracking();
const [workflowToDelete, setWorkflowToDelete] = React__namespace.useState(null);
const [showLimitModal, setShowLimitModal] = React__namespace.useState(false);
const { data, isLoading: isLoadingModels } = contentManager.useGetContentTypesQuery();
const { meta, workflows, isLoading, delete: deleteAction } = useReviewWorkflows.useReviewWorkflows();
const { getFeature, isLoading: isLicenseLoading } = ee.useLicenseLimits();
const permissions = hooks.useTypedSelector((state)=>state.admin_app.permissions.settings?.['review-workflows']);
const { allowedActions: { canCreate, canRead, canUpdate, canDelete } } = strapiAdmin.useRBAC(permissions);
const limits = getFeature('review-workflows');
const numberOfWorkflows = limits?.[constants.CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME];
const handleDeleteWorkflow = (workflowId)=>{
setWorkflowToDelete(workflowId);
};
const toggleConfirmDeleteDialog = ()=>{
setWorkflowToDelete(null);
};
const handleConfirmDeleteDialog = async ()=>{
if (!workflowToDelete) return;
await deleteAction(workflowToDelete);
setWorkflowToDelete(null);
};
const handleCreateClick = (event)=>{
event.preventDefault();
/**
* If the current license has a workflow limit:
* check if the total count of workflows exceeds that limit. If so,
* prevent the navigation and show the limits overlay.
*
* If the current license does not have a limit (e.g. offline license):
* allow the user to navigate to the create-view. In case they exceed the
* current hard-limit of 200 they will see an error thrown by the API.
*/ if (numberOfWorkflows && meta && meta?.workflowCount >= parseInt(numberOfWorkflows, 10)) {
event.preventDefault();
setShowLimitModal(true);
} else {
navigate('create');
trackUsage('willCreateWorkflow');
}
};
/**
* If the current license has a limit:
* check if the total count of workflows or stages exceeds that limit and display
* the limits modal on page load. It can be closed by the user, but the
* API will throw an error in case they try to create a new workflow or update the
* stages.
*
* If the current license does not have a limit (e.g. offline license):
* do nothing (for now). In case they are trying to create the 201st workflow/ stage
* the API will throw an error.
*
*/ React__namespace.useEffect(()=>{
if (!isLoading && !isLicenseLoading) {
if (numberOfWorkflows && meta && meta?.workflowCount > parseInt(numberOfWorkflows, 10)) {
setShowLimitModal(true);
}
}
}, [
isLicenseLoading,
isLoading,
meta,
meta?.workflowCount,
numberOfWorkflows
]);
const headers = [
{
label: formatMessage({
id: 'Settings.review-workflows.list.page.list.column.name.title',
defaultMessage: 'Name'
}),
name: 'name'
},
{
label: formatMessage({
id: 'Settings.review-workflows.list.page.list.column.stages.title',
defaultMessage: 'Stages'
}),
name: 'stages'
},
{
label: formatMessage({
id: 'Settings.review-workflows.list.page.list.column.contentTypes.title',
defaultMessage: 'Content Types'
}),
name: 'content-types'
}
];
if (isLoading || isLoadingModels) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Loading, {});
}
const contentTypes = Object.values(data ?? {}).reduce((acc, curr)=>{
acc.push(...curr);
return acc;
}, []);
return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(Layout.Header, {
primaryAction: canCreate ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.LinkButton, {
startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.Plus, {}),
size: "S",
tag: reactRouterDom.NavLink,
to: "create",
onClick: handleCreateClick,
children: formatMessage({
id: 'Settings.review-workflows.list.page.create',
defaultMessage: 'Create new workflow'
})
}) : null,
subtitle: formatMessage({
id: 'Settings.review-workflows.list.page.subtitle',
defaultMessage: 'Manage your content review process'
}),
title: formatMessage({
id: 'Settings.review-workflows.list.page.title',
defaultMessage: 'Review Workflows'
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(Layout.Root, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Root, {
isLoading: isLoading,
rows: workflows,
footer: canCreate ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.TFooter, {
cursor: "pointer",
icon: /*#__PURE__*/ jsxRuntime.jsx(icons.Plus, {}),
onClick: handleCreateClick,
children: formatMessage({
id: 'Settings.review-workflows.list.page.create',
defaultMessage: 'Create new workflow'
})
}) : null,
headers: headers,
children: /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin.Table.Content, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Head, {
children: headers.map((head)=>/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.HeaderCell, {
...head
}, head.name))
}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Body, {
children: workflows.map((workflow)=>/*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin.Table.Row, {
onClick: ()=>{
navigate(`${workflow.id}`);
},
children: [
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Cell, {
width: "25rem",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral800",
fontWeight: "bold",
ellipsis: true,
children: workflow.name
})
}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Cell, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral800",
children: workflow.stages.length
})
}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Cell, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral800",
children: workflow.contentTypes.map((uid)=>{
const contentType = contentTypes.find((contentType)=>contentType.uid === uid);
return contentType?.info.displayName ?? '';
}).join(', ')
})
}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Cell, {
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
alignItems: "center",
justifyContent: "end",
children: [
canRead || canUpdate ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
tag: reactRouterDom.Link,
to: workflow.id.toString(),
label: formatMessage({
id: 'Settings.review-workflows.list.page.list.column.actions.edit.label',
defaultMessage: 'Edit {name}'
}, {
name: workflow.name
}),
variant: "ghost",
children: /*#__PURE__*/ jsxRuntime.jsx(icons.Pencil, {})
}) : null,
workflows.length > 1 && canDelete ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
withTooltip: false,
label: formatMessage({
id: 'Settings.review-workflows.list.page.list.column.actions.delete.label',
defaultMessage: 'Delete {name}'
}, {
name: 'Default workflow'
}),
variant: "ghost",
onClick: (e)=>{
e.stopPropagation();
handleDeleteWorkflow(String(workflow.id));
},
children: /*#__PURE__*/ jsxRuntime.jsx(icons.Trash, {})
}) : null
]
})
})
]
}, workflow.id))
})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Root, {
open: !!workflowToDelete,
onOpenChange: toggleConfirmDeleteDialog,
children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.ConfirmDialog, {
onConfirm: handleConfirmDeleteDialog,
children: formatMessage({
id: 'Settings.review-workflows.list.page.delete.confirm.body',
defaultMessage: 'If you remove this worfklow, all stage-related information will be removed for this content-type. Are you sure you want to remove it?'
})
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(LimitsModal.LimitsModal.Root, {
open: showLimitModal,
onOpenChange: ()=>setShowLimitModal(false),
children: [
/*#__PURE__*/ jsxRuntime.jsx(LimitsModal.LimitsModal.Title, {
children: formatMessage({
id: 'Settings.review-workflows.list.page.workflows.limit.title',
defaultMessage: 'Youve reached the limit of workflows in your plan'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(LimitsModal.LimitsModal.Body, {
children: formatMessage({
id: 'Settings.review-workflows.list.page.workflows.limit.body',
defaultMessage: 'Delete a workflow or contact Sales to enable more workflows.'
})
})
]
})
]
})
]
});
};
const ProtectedListPage = ()=>{
const permissions = hooks.useTypedSelector((state)=>state.admin_app.permissions.settings?.['review-workflows']?.main);
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Protect, {
permissions: permissions,
children: /*#__PURE__*/ jsxRuntime.jsx(ReviewWorkflowsListView, {})
});
};
exports.ProtectedListPage = ProtectedListPage;
exports.ReviewWorkflowsListView = ReviewWorkflowsListView;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,271 @@
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import * as React from 'react';
import { useTracking, useRBAC, Page, Table, ConfirmDialog } from '@strapi/admin/strapi-admin';
import { useLicenseLimits } from '@strapi/admin/strapi-admin/ee';
import { LinkButton, TFooter, Typography, Flex, IconButton, Dialog } from '@strapi/design-system';
import { Plus, Pencil, Trash } from '@strapi/icons';
import { useIntl } from 'react-intl';
import { useNavigate, NavLink, Link } from 'react-router-dom';
import { LimitsModal } from '../../components/LimitsModal.mjs';
import { CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME } from '../../constants.mjs';
import { useTypedSelector } from '../../modules/hooks.mjs';
import { useGetContentTypesQuery } from '../../services/content-manager.mjs';
import { Header, Root } from './components/Layout.mjs';
import { useReviewWorkflows } from './hooks/useReviewWorkflows.mjs';
const ReviewWorkflowsListView = ()=>{
const { formatMessage } = useIntl();
const navigate = useNavigate();
const { trackUsage } = useTracking();
const [workflowToDelete, setWorkflowToDelete] = React.useState(null);
const [showLimitModal, setShowLimitModal] = React.useState(false);
const { data, isLoading: isLoadingModels } = useGetContentTypesQuery();
const { meta, workflows, isLoading, delete: deleteAction } = useReviewWorkflows();
const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits();
const permissions = useTypedSelector((state)=>state.admin_app.permissions.settings?.['review-workflows']);
const { allowedActions: { canCreate, canRead, canUpdate, canDelete } } = useRBAC(permissions);
const limits = getFeature('review-workflows');
const numberOfWorkflows = limits?.[CHARGEBEE_WORKFLOW_ENTITLEMENT_NAME];
const handleDeleteWorkflow = (workflowId)=>{
setWorkflowToDelete(workflowId);
};
const toggleConfirmDeleteDialog = ()=>{
setWorkflowToDelete(null);
};
const handleConfirmDeleteDialog = async ()=>{
if (!workflowToDelete) return;
await deleteAction(workflowToDelete);
setWorkflowToDelete(null);
};
const handleCreateClick = (event)=>{
event.preventDefault();
/**
* If the current license has a workflow limit:
* check if the total count of workflows exceeds that limit. If so,
* prevent the navigation and show the limits overlay.
*
* If the current license does not have a limit (e.g. offline license):
* allow the user to navigate to the create-view. In case they exceed the
* current hard-limit of 200 they will see an error thrown by the API.
*/ if (numberOfWorkflows && meta && meta?.workflowCount >= parseInt(numberOfWorkflows, 10)) {
event.preventDefault();
setShowLimitModal(true);
} else {
navigate('create');
trackUsage('willCreateWorkflow');
}
};
/**
* If the current license has a limit:
* check if the total count of workflows or stages exceeds that limit and display
* the limits modal on page load. It can be closed by the user, but the
* API will throw an error in case they try to create a new workflow or update the
* stages.
*
* If the current license does not have a limit (e.g. offline license):
* do nothing (for now). In case they are trying to create the 201st workflow/ stage
* the API will throw an error.
*
*/ React.useEffect(()=>{
if (!isLoading && !isLicenseLoading) {
if (numberOfWorkflows && meta && meta?.workflowCount > parseInt(numberOfWorkflows, 10)) {
setShowLimitModal(true);
}
}
}, [
isLicenseLoading,
isLoading,
meta,
meta?.workflowCount,
numberOfWorkflows
]);
const headers = [
{
label: formatMessage({
id: 'Settings.review-workflows.list.page.list.column.name.title',
defaultMessage: 'Name'
}),
name: 'name'
},
{
label: formatMessage({
id: 'Settings.review-workflows.list.page.list.column.stages.title',
defaultMessage: 'Stages'
}),
name: 'stages'
},
{
label: formatMessage({
id: 'Settings.review-workflows.list.page.list.column.contentTypes.title',
defaultMessage: 'Content Types'
}),
name: 'content-types'
}
];
if (isLoading || isLoadingModels) {
return /*#__PURE__*/ jsx(Page.Loading, {});
}
const contentTypes = Object.values(data ?? {}).reduce((acc, curr)=>{
acc.push(...curr);
return acc;
}, []);
return /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsx(Header, {
primaryAction: canCreate ? /*#__PURE__*/ jsx(LinkButton, {
startIcon: /*#__PURE__*/ jsx(Plus, {}),
size: "S",
tag: NavLink,
to: "create",
onClick: handleCreateClick,
children: formatMessage({
id: 'Settings.review-workflows.list.page.create',
defaultMessage: 'Create new workflow'
})
}) : null,
subtitle: formatMessage({
id: 'Settings.review-workflows.list.page.subtitle',
defaultMessage: 'Manage your content review process'
}),
title: formatMessage({
id: 'Settings.review-workflows.list.page.title',
defaultMessage: 'Review Workflows'
})
}),
/*#__PURE__*/ jsxs(Root, {
children: [
/*#__PURE__*/ jsx(Table.Root, {
isLoading: isLoading,
rows: workflows,
footer: canCreate ? /*#__PURE__*/ jsx(TFooter, {
cursor: "pointer",
icon: /*#__PURE__*/ jsx(Plus, {}),
onClick: handleCreateClick,
children: formatMessage({
id: 'Settings.review-workflows.list.page.create',
defaultMessage: 'Create new workflow'
})
}) : null,
headers: headers,
children: /*#__PURE__*/ jsxs(Table.Content, {
children: [
/*#__PURE__*/ jsx(Table.Head, {
children: headers.map((head)=>/*#__PURE__*/ jsx(Table.HeaderCell, {
...head
}, head.name))
}),
/*#__PURE__*/ jsx(Table.Body, {
children: workflows.map((workflow)=>/*#__PURE__*/ jsxs(Table.Row, {
onClick: ()=>{
navigate(`${workflow.id}`);
},
children: [
/*#__PURE__*/ jsx(Table.Cell, {
width: "25rem",
children: /*#__PURE__*/ jsx(Typography, {
textColor: "neutral800",
fontWeight: "bold",
ellipsis: true,
children: workflow.name
})
}),
/*#__PURE__*/ jsx(Table.Cell, {
children: /*#__PURE__*/ jsx(Typography, {
textColor: "neutral800",
children: workflow.stages.length
})
}),
/*#__PURE__*/ jsx(Table.Cell, {
children: /*#__PURE__*/ jsx(Typography, {
textColor: "neutral800",
children: workflow.contentTypes.map((uid)=>{
const contentType = contentTypes.find((contentType)=>contentType.uid === uid);
return contentType?.info.displayName ?? '';
}).join(', ')
})
}),
/*#__PURE__*/ jsx(Table.Cell, {
children: /*#__PURE__*/ jsxs(Flex, {
alignItems: "center",
justifyContent: "end",
children: [
canRead || canUpdate ? /*#__PURE__*/ jsx(IconButton, {
tag: Link,
to: workflow.id.toString(),
label: formatMessage({
id: 'Settings.review-workflows.list.page.list.column.actions.edit.label',
defaultMessage: 'Edit {name}'
}, {
name: workflow.name
}),
variant: "ghost",
children: /*#__PURE__*/ jsx(Pencil, {})
}) : null,
workflows.length > 1 && canDelete ? /*#__PURE__*/ jsx(IconButton, {
withTooltip: false,
label: formatMessage({
id: 'Settings.review-workflows.list.page.list.column.actions.delete.label',
defaultMessage: 'Delete {name}'
}, {
name: 'Default workflow'
}),
variant: "ghost",
onClick: (e)=>{
e.stopPropagation();
handleDeleteWorkflow(String(workflow.id));
},
children: /*#__PURE__*/ jsx(Trash, {})
}) : null
]
})
})
]
}, workflow.id))
})
]
})
}),
/*#__PURE__*/ jsx(Dialog.Root, {
open: !!workflowToDelete,
onOpenChange: toggleConfirmDeleteDialog,
children: /*#__PURE__*/ jsx(ConfirmDialog, {
onConfirm: handleConfirmDeleteDialog,
children: formatMessage({
id: 'Settings.review-workflows.list.page.delete.confirm.body',
defaultMessage: 'If you remove this worfklow, all stage-related information will be removed for this content-type. Are you sure you want to remove it?'
})
})
}),
/*#__PURE__*/ jsxs(LimitsModal.Root, {
open: showLimitModal,
onOpenChange: ()=>setShowLimitModal(false),
children: [
/*#__PURE__*/ jsx(LimitsModal.Title, {
children: formatMessage({
id: 'Settings.review-workflows.list.page.workflows.limit.title',
defaultMessage: 'Youve reached the limit of workflows in your plan'
})
}),
/*#__PURE__*/ jsx(LimitsModal.Body, {
children: formatMessage({
id: 'Settings.review-workflows.list.page.workflows.limit.body',
defaultMessage: 'Delete a workflow or contact Sales to enable more workflows.'
})
})
]
})
]
})
]
});
};
const ProtectedListPage = ()=>{
const permissions = useTypedSelector((state)=>state.admin_app.permissions.settings?.['review-workflows']?.main);
return /*#__PURE__*/ jsx(Page.Protect, {
permissions: permissions,
children: /*#__PURE__*/ jsx(ReviewWorkflowsListView, {})
});
};
export { ProtectedListPage, ReviewWorkflowsListView };
//# sourceMappingURL=index.mjs.map

File diff suppressed because one or more lines are too long