diff --git a/docs/docs/docs/01-core/admin/04-hooks/use-enterprise.mdx b/docs/docs/docs/01-core/admin/04-hooks/use-enterprise.mdx index a8e0c49977..ca95abc1b8 100644 --- a/docs/docs/docs/01-core/admin/04-hooks/use-enterprise.mdx +++ b/docs/docs/docs/01-core/admin/04-hooks/use-enterprise.mdx @@ -54,3 +54,8 @@ if (!Component) { return; } ``` + +### `enabled` + +Similar to react-query this boolean flag allows disabling the EE import, e.g. when more than one condition needs to be applied. If `enabled` +is set to false, the first argument (CE_DATA) will be returned. diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js deleted file mode 100644 index e19585b823..0000000000 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js +++ /dev/null @@ -1,2 +0,0 @@ -// Overwritten in EE -export default () => null; diff --git a/packages/core/admin/admin/src/content-manager/pages/ListView/components/TableRows/index.js b/packages/core/admin/admin/src/content-manager/pages/ListView/components/TableRows/index.js index f0da516370..b6e7e36e91 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListView/components/TableRows/index.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListView/components/TableRows/index.js @@ -1,6 +1,16 @@ import React from 'react'; -import { BaseCheckbox, IconButton, Tbody, Td, Tr, Flex } from '@strapi/design-system'; +import { + BaseCheckbox, + IconButton, + Tbody, + Td, + Tr, + Flex, + lightTheme, + Status, + Typography, +} from '@strapi/design-system'; import { useTracking, useFetchClient, useAPIErrorHandler } from '@strapi/helper-plugin'; import { Trash, Duplicate, Pencil } from '@strapi/icons'; import { AxiosError } from 'axios'; @@ -8,15 +18,19 @@ import PropTypes from 'prop-types'; import { useIntl } from 'react-intl'; import { Link, useHistory } from 'react-router-dom'; +import { useEnterprise } from '../../../../../hooks/useEnterprise'; import { getFullName } from '../../../../../utils'; import { usePluginsQueryParams } from '../../../../hooks'; import { getTrad } from '../../../../utils'; import CellContent from '../CellContent'; +const REVIEW_WORKFLOW_COLUMNS_CE = () => null; + export const TableRows = ({ canCreate, canDelete, contentType, + features: { hasDraftAndPublish, hasReviewWorkflows }, headers, entriesToDelete, onClickDelete, @@ -33,6 +47,18 @@ export const TableRows = ({ const { trackUsage } = useTracking(); const pluginsQueryParams = usePluginsQueryParams(); const { formatAPIError } = useAPIErrorHandler(getTrad); + const ReviewWorkflowsStage = useEnterprise( + REVIEW_WORKFLOW_COLUMNS_CE, + async () => + ( + await import( + '../../../../../../../ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn' + ) + ).ReviewWorkflowsStageEE, + { + enabled: hasReviewWorkflows, + } + ); /** * @@ -74,6 +100,11 @@ export const TableRows = ({ } }; + // block rendering until the review stage component is fully loaded in EE + if (!ReviewWorkflowsStage) { + return null; + } + /** * Table Cells with actions e.g edit, delete, duplicate have `stopPropagation` * to prevent the row from being selected. @@ -113,20 +144,59 @@ export const TableRows = ({ /> )} + {headers.map(({ key, cellFormatter, name, ...rest }) => { + if (hasDraftAndPublish && name === 'publishedAt') { + return ( + + + + {formatMessage({ + id: getTrad( + `containers.List.${data.publishedAt ? 'published' : 'draft'}` + ), + defaultMessage: data.publishedAt ? 'Published' : 'Draft', + })} + + + + ); + } + + if (hasReviewWorkflows && name === 'strapi_reviewWorkflows_stage') { + return ( + + {data.strapi_reviewWorkflows_stage ? ( + + ) : ( + - + )} + + ); + } + return ( - {typeof cellFormatter === 'function' ? ( - cellFormatter(data, { key, name, ...rest }) - ) : ( - - )} + ); })} @@ -212,6 +282,10 @@ TableRows.propTypes = { uid: PropTypes.string.isRequired, }).isRequired, entriesToDelete: PropTypes.array, + features: PropTypes.shape({ + hasDraftAndPublish: PropTypes.bool.isRequired, + hasReviewWorkflows: PropTypes.bool.isRequired, + }).isRequired, headers: PropTypes.array.isRequired, onClickDelete: PropTypes.func, onSelectRow: PropTypes.func, diff --git a/packages/core/admin/admin/src/content-manager/pages/ListView/index.js b/packages/core/admin/admin/src/content-manager/pages/ListView/index.js index 1eca5b378a..983b5c6ba9 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListView/index.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListView/index.js @@ -10,8 +10,6 @@ import { HeaderLayout, useNotifyAT, Flex, - Typography, - Status, } from '@strapi/design-system'; import { NoPermissions, @@ -33,7 +31,6 @@ import { } from '@strapi/helper-plugin'; import { ArrowLeft, Cog, Plus } from '@strapi/icons'; import axios from 'axios'; -import getReviewWorkflowsColumn from 'ee_else_ce/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn'; import isEqual from 'lodash/isEqual'; import PropTypes from 'prop-types'; import { stringify } from 'qs'; @@ -45,6 +42,7 @@ import { bindActionCreators, compose } from 'redux'; import styled from 'styled-components'; import { INJECT_COLUMN_IN_TABLE } from '../../../exposedHooks'; +import { useEnterprise } from '../../../hooks/useEnterprise'; import { selectAdminPermissions } from '../../../pages/App/selectors'; import { InjectionZone } from '../../../shared/components'; import AttributeFilter from '../../components/AttributeFilter'; @@ -67,6 +65,8 @@ const ConfigureLayoutBox = styled(Box)` } `; +const REVIEW_WORKFLOW_COLUMNS_CE = null; + function ListView({ canCreate, canDelete, @@ -107,8 +107,24 @@ function ListView({ const { pathname } = useLocation(); const { push } = useHistory(); const { formatMessage } = useIntl(); - const hasDraftAndPublish = options?.draftAndPublish || false; const fetchClient = useFetchClient(); + + const hasDraftAndPublish = options?.draftAndPublish ?? false; + const hasReviewWorkflows = options?.reviewWorkflows ?? false; + + const reviewWorkflowColumns = useEnterprise( + REVIEW_WORKFLOW_COLUMNS_CE, + async () => + ( + await import( + '../../../../../ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants' + ) + ).REVIEW_WORKFLOW_COLUMNS_EE, + { + enabled: !!options?.reviewWorkflows, + } + ); + const { post, del } = fetchClient; const bulkPublishMutation = useMutation( @@ -388,24 +404,8 @@ function ListView({ }; }); - if (!hasDraftAndPublish) { - return formattedHeaders; - } - - // this should not exist. Ideally we would use registerHook() similar to what has been done - // in the i18n plugin. In order to do that review-workflows should have been a plugin. In - // a future iteration we need to find a better pattern. - - // In CE this will return null - in EE a column definition including the custom formatting component. - const reviewWorkflowColumn = getReviewWorkflowsColumn(layout); - - if (reviewWorkflowColumn) { - formattedHeaders.push(reviewWorkflowColumn); - } - - return [ - ...formattedHeaders, - { + if (hasDraftAndPublish) { + formattedHeaders.push({ key: '__published_at_temp_key__', name: 'publishedAt', fieldSchema: { @@ -419,25 +419,29 @@ function ListView({ searchable: false, sortable: true, }, - // eslint-disable-next-line react/no-unstable-nested-components - cellFormatter(cellData) { - const isPublished = cellData.publishedAt; - const variant = isPublished ? 'success' : 'secondary'; + }); + } - return ( - - - {formatMessage({ - id: getTrad(`containers.List.${isPublished ? 'published' : 'draft'}`), - defaultMessage: isPublished ? 'Published' : 'Draft', - })} - - - ); - }, - }, - ]; - }, [runHookWaterfall, displayedHeaders, layout, hasDraftAndPublish, formatMessage]); + if (reviewWorkflowColumns) { + // Make sure the column header label is translated + if (typeof reviewWorkflowColumns.metadatas.label !== 'string') { + reviewWorkflowColumns.metadatas.label = formatMessage( + reviewWorkflowColumns.metadatas.label + ); + } + + formattedHeaders.push(reviewWorkflowColumns); + } + + return formattedHeaders; + }, [ + runHookWaterfall, + displayedHeaders, + layout, + reviewWorkflowColumns, + hasDraftAndPublish, + formatMessage, + ]); const subtitle = canRead ? formatMessage( @@ -580,6 +584,10 @@ function ListView({ canCreate={canCreate} canDelete={canDelete} contentType={contentType} + features={{ + hasDraftAndPublish, + hasReviewWorkflows, + }} headers={tableHeaders} rows={data} withBulkActions diff --git a/packages/core/admin/admin/src/hooks/useEnterprise/tests/useEnterprise.test.js b/packages/core/admin/admin/src/hooks/useEnterprise/tests/useEnterprise.test.js index 47a8d20824..96b3a59e79 100644 --- a/packages/core/admin/admin/src/hooks/useEnterprise/tests/useEnterprise.test.js +++ b/packages/core/admin/admin/src/hooks/useEnterprise/tests/useEnterprise.test.js @@ -54,6 +54,14 @@ describe('useEnterprise (EE)', () => { await waitFor(() => expect(result.current).toStrictEqual(EE_DATA_FIXTURE)); }); + test('Returns CE data, when enabled is set to false', async () => { + const { result } = setup(CE_DATA_FIXTURE, async () => EE_DATA_FIXTURE, { + enabled: false, + }); + + await waitFor(() => expect(result.current).toStrictEqual(CE_DATA_FIXTURE)); + }); + test('Returns a custom defaultValue on first render followed by the EE data', async () => { const { result } = setup(CE_DATA_FIXTURE, async () => EE_DATA_FIXTURE, { defaultValue: false, diff --git a/packages/core/admin/admin/src/hooks/useEnterprise/useEnterprise.js b/packages/core/admin/admin/src/hooks/useEnterprise/useEnterprise.js index 97688cd872..03ab26c88f 100644 --- a/packages/core/admin/admin/src/hooks/useEnterprise/useEnterprise.js +++ b/packages/core/admin/admin/src/hooks/useEnterprise/useEnterprise.js @@ -9,7 +9,7 @@ function isEnterprise() { export function useEnterprise( ceData, eeCallback, - { defaultValue = null, combine = (ceData, eeData) => eeData } = {} + { defaultValue = null, combine = (ceData, eeData) => eeData, enabled = true } = {} ) { const eeCallbackRef = useCallbackRef(eeCallback); const combineCallbackRef = useCallbackRef(combine); @@ -17,7 +17,7 @@ export function useEnterprise( // We have to use a nested object here, because functions (e.g. Components) // can not be stored as value directly const [{ data }, setData] = React.useState({ - data: isEnterprise() ? defaultValue : ceData, + data: isEnterprise() && enabled ? defaultValue : ceData, }); React.useEffect(() => { @@ -27,10 +27,10 @@ export function useEnterprise( setData({ data: combineCallbackRef(ceData, eeData) }); } - if (isEnterprise()) { + if (isEnterprise() && enabled) { importEE(); } - }, [ceData, eeCallbackRef, combineCallbackRef]); + }, [ceData, eeCallbackRef, combineCallbackRef, enabled]); return data; } diff --git a/packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js b/packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js deleted file mode 100644 index c5afe6c8de..0000000000 --- a/packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; - -import { Typography } from '@strapi/design-system'; -import { useIntl } from 'react-intl'; - -import getTrad from '../../../../../../../admin/src/content-manager/utils/getTrad'; -import { STAGE_COLOR_DEFAULT } from '../../../../../pages/SettingsPage/pages/ReviewWorkflows/constants'; - -import ReviewWorkflowsStage from '.'; - -export default (layout) => { - const { formatMessage } = useIntl(); - - // TODO: As soon as the feature was enabled in EE mode, the BE currently does not have a way to send - // `false` once a user is in CE mode again. We shouldn't have to perform the window.strapi.isEE check here - // and it is meant to be in interim solution until we find a better one. - const hasReviewWorkflows = - (window.strapi.features.isEnabled(window.strapi.features.REVIEW_WORKFLOWS) && - layout.contentType.options?.reviewWorkflows) ?? - false; - - if (!hasReviewWorkflows) { - return null; - } - - return { - key: '__strapi_reviewWorkflows_stage_temp_key__', - name: 'strapi_reviewWorkflows_stage', - fieldSchema: { - type: 'relation', - }, - metadatas: { - label: formatMessage({ - id: getTrad(`containers.ListPage.table-headers.reviewWorkflows.stage`), - defaultMessage: 'Review stage', - }), - searchable: false, - sortable: true, - mainField: { - name: 'name', - schema: { - type: 'string', - }, - }, - }, - cellFormatter({ strapi_reviewWorkflows_stage }) { - // if entities are created e.g. through lifecycle methods - // they may not have a stage assigned - if (!strapi_reviewWorkflows_stage) { - return -; - } - - const { color, name } = strapi_reviewWorkflows_stage; - - return ; - }, - }; -}; diff --git a/packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/index.js b/packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/index.js deleted file mode 100644 index 9d18524090..0000000000 --- a/packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import { ReviewWorkflowsStageEE } from './ReviewWorkflowsStageEE'; - -export default ReviewWorkflowsStageEE; diff --git a/packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/ReviewWorkflowsStageEE.js b/packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/ReviewWorkflowsStageEE.js similarity index 88% rename from packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/ReviewWorkflowsStageEE.js rename to packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/ReviewWorkflowsStageEE.js index d10767b7f7..a3ac28b204 100644 --- a/packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/ReviewWorkflowsStageEE.js +++ b/packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/ReviewWorkflowsStageEE.js @@ -4,7 +4,7 @@ import { Box, Flex, Typography } from '@strapi/design-system'; import { pxToRem } from '@strapi/helper-plugin'; import PropTypes from 'prop-types'; -import { getStageColorByHex } from '../../../../../pages/SettingsPage/pages/ReviewWorkflows/utils/colors'; +import { getStageColorByHex } from '../../../../pages/SettingsPage/pages/ReviewWorkflows/utils/colors'; export function ReviewWorkflowsStageEE({ color, name }) { const { themeColorName } = getStageColorByHex(color); diff --git a/packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants.js b/packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants.js new file mode 100644 index 0000000000..1ec56488b0 --- /dev/null +++ b/packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants.js @@ -0,0 +1,24 @@ +import getTrad from '../../../../../../admin/src/content-manager/utils/getTrad'; + +export const REVIEW_WORKFLOW_COLUMNS_EE = { + key: '__strapi_reviewWorkflows_stage_temp_key__', + name: 'strapi_reviewWorkflows_stage', + fieldSchema: { + type: 'relation', + }, + metadatas: { + // formatMessage() will be applied when the column is rendered + label: { + id: getTrad(`containers.ListPage.table-headers.reviewWorkflows.stage`), + defaultMessage: 'Review stage', + }, + searchable: false, + sortable: true, + mainField: { + name: 'name', + schema: { + type: 'string', + }, + }, + }, +}; diff --git a/packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/index.js b/packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/index.js new file mode 100644 index 0000000000..354bfb6848 --- /dev/null +++ b/packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/index.js @@ -0,0 +1 @@ +export * from './ReviewWorkflowsStageEE'; diff --git a/packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/tests/ReviewWorkflowsStage.test.js b/packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/tests/ReviewWorkflowsStage.test.js similarity index 87% rename from packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/tests/ReviewWorkflowsStage.test.js rename to packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/tests/ReviewWorkflowsStage.test.js index 76ec2a92a4..5d704a62a8 100644 --- a/packages/core/admin/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/tests/ReviewWorkflowsStage.test.js +++ b/packages/core/admin/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/tests/ReviewWorkflowsStage.test.js @@ -4,12 +4,12 @@ import { lightTheme, ThemeProvider } from '@strapi/design-system'; import { render } from '@testing-library/react'; import { IntlProvider } from 'react-intl'; -import ReviewWorkflowsStage from '..'; +import { ReviewWorkflowsStageEE } from '..'; const ComponentFixture = (props) => ( - + );