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 new file mode 100644 index 0000000000..a8e0c49977 --- /dev/null +++ b/docs/docs/docs/01-core/admin/04-hooks/use-enterprise.mdx @@ -0,0 +1,56 @@ +--- +title: useEnterprise +description: API reference for the useEnterprise hook +tags: + - admin + - hooks + - users +--- + +A hook that returns either community or enterprise-edition data-structures based on the global `window.strapi.isEE` flag. + +## Usage + +``` +import { CE_DATA } from './data'; + +function Component() { + const data = useEnterprise(CE_DATA, async () => (await import('./ee/data')).default); +} +``` + +It accepts an optional third argument to pass in options customizing the hook behavior: + +### `combine()` + +THe `combine` callback can be used as a custom "merge" function for the ce and ee arguments: + +``` +const data = useEnterprise({ a: 1 }, () => { b: 1 }, { combine(ce, ee) { return { ...ce, ...ee } } }); + +console.log(data); // { a: 1, b: 1 } +``` + +### `defaultValue` + +By default the hook returns `null` if `window.strapi.isEE` is true and the enterprise data structure is not yet loaded. Customizing +this value can help implementing various loading scenarios: + +``` +// display a loading state while an EE component is loading +const Component = useEnterprise(() =>
CE
, () =>EE
, { + defaultValue: () =>CE
, () =>EE
, { + defaultValue: () => null +}) + +// display nothing while an EE component is loading +const Component = useEnterprise(() =>CE
, () =>EE
) + +if (!Component) { + return; +} +``` diff --git a/packages/core/admin/admin/src/hooks/useEnterprise/index.js b/packages/core/admin/admin/src/hooks/useEnterprise/index.js new file mode 100644 index 0000000000..72bb02a9a4 --- /dev/null +++ b/packages/core/admin/admin/src/hooks/useEnterprise/index.js @@ -0,0 +1 @@ +export * from './useEnterprise'; 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 new file mode 100644 index 0000000000..47a8d20824 --- /dev/null +++ b/packages/core/admin/admin/src/hooks/useEnterprise/tests/useEnterprise.test.js @@ -0,0 +1,66 @@ +import { renderHook, waitFor } from '@testing-library/react'; + +import { useEnterprise } from '../useEnterprise'; + +const CE_DATA_FIXTURE = ['CE']; +const EE_DATA_FIXTURE = ['EE']; + +function setup(...args) { + return renderHook(() => useEnterprise(...args)); +} + +describe('useEnterprise (CE)', () => { + test('Returns CE data', async () => { + const { result } = setup(CE_DATA_FIXTURE, async () => EE_DATA_FIXTURE); + + expect(result.current).toBe(CE_DATA_FIXTURE); + }); +}); + +describe('useEnterprise (EE)', () => { + beforeAll(() => { + window.strapi.isEE = true; + }); + + afterAll(() => { + window.strapi.isEE = false; + }); + + test('Returns default data on first render and EE data on second', async () => { + const { result } = setup(CE_DATA_FIXTURE, async () => EE_DATA_FIXTURE); + + expect(result.current).toBe(null); + + await waitFor(() => expect(result.current).toBe(EE_DATA_FIXTURE)); + }); + + test('Combines CE and EE data', async () => { + const { result } = setup(CE_DATA_FIXTURE, async () => EE_DATA_FIXTURE, { + combine(ceData, eeData) { + return [...ceData, ...eeData]; + }, + }); + + expect(result.current).toBe(null); + + await waitFor(() => + expect(result.current).toStrictEqual([...CE_DATA_FIXTURE, ...EE_DATA_FIXTURE]) + ); + }); + + test('Returns EE data without custom combine', async () => { + const { result } = setup(CE_DATA_FIXTURE, async () => EE_DATA_FIXTURE); + + await waitFor(() => expect(result.current).toStrictEqual(EE_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, + }); + + expect(result.current).toBe(false); + + await waitFor(() => expect(result.current).toStrictEqual(EE_DATA_FIXTURE)); + }); +}); diff --git a/packages/core/admin/admin/src/hooks/useEnterprise/useEnterprise.js b/packages/core/admin/admin/src/hooks/useEnterprise/useEnterprise.js new file mode 100644 index 0000000000..97688cd872 --- /dev/null +++ b/packages/core/admin/admin/src/hooks/useEnterprise/useEnterprise.js @@ -0,0 +1,36 @@ +import * as React from 'react'; + +import { useCallbackRef } from '@strapi/helper-plugin'; + +function isEnterprise() { + return window.strapi.isEE; +} + +export function useEnterprise( + ceData, + eeCallback, + { defaultValue = null, combine = (ceData, eeData) => eeData } = {} +) { + const eeCallbackRef = useCallbackRef(eeCallback); + const combineCallbackRef = useCallbackRef(combine); + + // 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, + }); + + React.useEffect(() => { + async function importEE() { + const eeData = await eeCallbackRef(); + + setData({ data: combineCallbackRef(ceData, eeData) }); + } + + if (isEnterprise()) { + importEE(); + } + }, [ceData, eeCallbackRef, combineCallbackRef]); + + return data; +} diff --git a/packages/core/admin/admin/src/hooks/useSettingsMenu/constants.js b/packages/core/admin/admin/src/hooks/useSettingsMenu/constants.js new file mode 100644 index 0000000000..6f3af0ed3c --- /dev/null +++ b/packages/core/admin/admin/src/hooks/useSettingsMenu/constants.js @@ -0,0 +1,46 @@ +import adminPermissions from '../../permissions'; + +export const LINKS_CE = { + global: [ + { + intlLabel: { id: 'Settings.application.title', defaultMessage: 'Overview' }, + to: '/settings/application-infos', + id: '000-application-infos', + permissions: [], + }, + { + intlLabel: { id: 'Settings.webhooks.title', defaultMessage: 'Webhooks' }, + to: '/settings/webhooks', + id: 'webhooks', + permissions: adminPermissions.settings.webhooks.main, + }, + { + intlLabel: { id: 'Settings.apiTokens.title', defaultMessage: 'API Tokens' }, + to: '/settings/api-tokens?sort=name:ASC', + id: 'api-tokens', + permissions: adminPermissions.settings['api-tokens'].main, + }, + { + intlLabel: { id: 'Settings.transferTokens.title', defaultMessage: 'Transfer Tokens' }, + to: '/settings/transfer-tokens?sort=name:ASC', + id: 'transfer-tokens', + permissions: adminPermissions.settings['transfer-tokens'].main, + }, + ], + + admin: [ + { + intlLabel: { id: 'global.roles', defaultMessage: 'Roles' }, + to: '/settings/roles', + id: 'roles', + permissions: adminPermissions.settings.roles.main, + }, + { + intlLabel: { id: 'global.users' }, + // Init the search params directly + to: '/settings/users?pageSize=10&page=1&sort=firstname', + id: 'users', + permissions: adminPermissions.settings.users.main, + }, + ], +}; diff --git a/packages/core/admin/admin/src/hooks/useSettingsMenu/index.js b/packages/core/admin/admin/src/hooks/useSettingsMenu/index.js index d561063838..170f827101 100644 --- a/packages/core/admin/admin/src/hooks/useSettingsMenu/index.js +++ b/packages/core/admin/admin/src/hooks/useSettingsMenu/index.js @@ -1,56 +1,95 @@ -import { useEffect, useReducer } from 'react'; +import { useState, useEffect } from 'react'; -import { hasPermissions, useAppInfo, useRBACProvider, useStrapiApp } from '@strapi/helper-plugin'; +import { hasPermissions, useRBACProvider, useStrapiApp, useAppInfo } from '@strapi/helper-plugin'; -import init from './init'; -import reducer, { initialState } from './reducer'; +import { useEnterprise } from '../useEnterprise'; -const useSettingsMenu = (noCheck = false) => { +import { LINKS_CE } from './constants'; +import formatLinks from './utils/formatLinks'; +import sortLinks from './utils/sortLinks'; + +const useSettingsMenu = () => { + const [{ isLoading, menu }, setData] = useState({ + isLoading: true, + menu: [], + }); const { allPermissions: permissions } = useRBACProvider(); const { shouldUpdateStrapi } = useAppInfo(); const { settings } = useStrapiApp(); - - const [{ isLoading, menu }, dispatch] = useReducer(reducer, initialState, () => - init(initialState, { settings, shouldUpdateStrapi }) + const { global: globalLinks, admin: adminLinks } = useEnterprise( + LINKS_CE, + async () => (await import('../../../../ee/admin/hooks/useSettingsMenu/constants')).LINKS_EE, + { + combine(ceLinks, eeLinks) { + return { + admin: [...eeLinks.admin, ...ceLinks.admin], + global: [...ceLinks.global, ...eeLinks.global], + }; + }, + defaultValue: { + admin: [], + global: [], + }, + } ); useEffect(() => { const getData = async () => { - const checkPermissions = async (permissionsToCheck, path) => { - const hasPermission = await hasPermissions(permissions, permissionsToCheck); + const buildMenuPermissions = (sections) => + Promise.all( + sections.reduce((acc, section, sectionIndex) => { + const buildMenuPermissions = (links) => + links.map(async (link, linkIndex) => ({ + hasPermission: await hasPermissions(permissions, link.permissions), + sectionIndex, + linkIndex, + })); - return { hasPermission, path }; - }; + return [...acc, ...buildMenuPermissions(section.links)]; + }, []) + ); - const generateArrayOfPromises = (array) => { - return array.reduce((acc, current, sectionIndex) => { - const generateArrayOfPromises = (array) => - array.map((link, index) => - checkPermissions(array[index].permissions, `${sectionIndex}.links.${index}`) + const menuPermissions = await buildMenuPermissions(sections); + + setData((prev) => ({ + ...prev, + isLoading: false, + menu: sections.map((section, sectionIndex) => ({ + ...section, + links: section.links.map((link, linkIndex) => { + const permission = menuPermissions.find( + (permission) => + permission.sectionIndex === sectionIndex && permission.linkIndex === linkIndex ); - return [...acc, ...generateArrayOfPromises(current.links)]; - }, []); - }; - - const generalSectionLinksArrayOfPromises = generateArrayOfPromises(menu); - - const data = await Promise.all(generalSectionLinksArrayOfPromises); - - dispatch({ - type: 'CHECK_PERMISSIONS_SUCCEEDED', - data, - }); + return { + ...link, + isDisplayed: Boolean(permission.hasPermission), + }; + }), + })), + })); }; - // This hook is also used by the main LeftMenu component in order to know which sections it needs to display/hide - // Therefore, we don't need to make the checking all the times when the hook is used. - if (!noCheck) { - getData(); - } + const { global, ...otherSections } = settings; + const sections = formatLinks([ + { + ...settings.global, + links: sortLinks([...settings.global.links, ...globalLinks]).map((link) => ({ + ...link, + hasNotification: link.id === '000-application-infos' && shouldUpdateStrapi, + })), + }, + { + id: 'permissions', + intlLabel: { id: 'Settings.permissions', defaultMessage: 'Administration Panel' }, + links: adminLinks, + }, + ...Object.values(otherSections), + ]); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [permissions, noCheck]); + getData(); + }, [adminLinks, globalLinks, permissions, settings, shouldUpdateStrapi]); return { isLoading, menu }; }; diff --git a/packages/core/admin/admin/src/hooks/useSettingsMenu/init.js b/packages/core/admin/admin/src/hooks/useSettingsMenu/init.js deleted file mode 100644 index b4bcf97c74..0000000000 --- a/packages/core/admin/admin/src/hooks/useSettingsMenu/init.js +++ /dev/null @@ -1,35 +0,0 @@ -import omit from 'lodash/omit'; - -import adminLinks from './utils/adminLinks'; -import formatLinks from './utils/formatLinks'; -import globalLinks from './utils/globalLinks'; -import sortLinks from './utils/sortLinks'; - -const init = (initialState, { settings, shouldUpdateStrapi }) => { - // Retrieve the links that will be injected into the global section - const pluginsGlobalLinks = settings.global.links; - // Sort the links by name - const sortedGlobalLinks = sortLinks([...pluginsGlobalLinks, ...globalLinks]).map((link) => ({ - ...link, - hasNotification: link.id === '000-application-infos' && shouldUpdateStrapi, - })); - - const otherSections = Object.values(omit(settings, 'global')); - - const menu = [ - { - ...settings.global, - links: sortedGlobalLinks, - }, - { - id: 'permissions', - intlLabel: { id: 'Settings.permissions', defaultMessage: 'Administration Panel' }, - links: adminLinks, - }, - ...otherSections, - ]; - - return { ...initialState, menu: formatLinks(menu) }; -}; - -export default init; diff --git a/packages/core/admin/admin/src/hooks/useSettingsMenu/reducer.js b/packages/core/admin/admin/src/hooks/useSettingsMenu/reducer.js deleted file mode 100644 index 9da7aba16f..0000000000 --- a/packages/core/admin/admin/src/hooks/useSettingsMenu/reducer.js +++ /dev/null @@ -1,41 +0,0 @@ -import produce from 'immer'; -import set from 'lodash/set'; - -const initialState = { - menu: [], - isLoading: true, -}; - -const reducer = (state, action) => - // eslint-disable-next-line consistent-return - produce(state, (draftState) => { - switch (action.type) { - case 'CHECK_PERMISSIONS_SUCCEEDED': { - action.data.forEach((checkedPermissions) => { - if (checkedPermissions.hasPermission) { - set( - draftState, - ['menu', ...checkedPermissions.path.split('.'), 'isDisplayed'], - checkedPermissions.hasPermission - ); - } - }); - - // Remove the not needed links in each section - draftState.menu.forEach((section, sectionIndex) => { - draftState.menu[sectionIndex].links = section.links.filter( - (link) => link.isDisplayed === true - ); - }); - - draftState.isLoading = false; - break; - } - - default: - return draftState; - } - }); - -export default reducer; -export { initialState }; diff --git a/packages/core/admin/admin/src/hooks/useSettingsMenu/tests/reducer.test.js b/packages/core/admin/admin/src/hooks/useSettingsMenu/tests/reducer.test.js deleted file mode 100644 index 5f5ca4328e..0000000000 --- a/packages/core/admin/admin/src/hooks/useSettingsMenu/tests/reducer.test.js +++ /dev/null @@ -1,116 +0,0 @@ -import reducer from '../reducer'; - -describe('ADMIN | hooks | useSettingsMenu | reducer', () => { - describe('DEFAULT_ACTION', () => { - it('should return the state', () => { - const initialState = { - ok: true, - }; - const expected = { - ok: true, - }; - - expect(reducer(initialState, {})).toEqual(expected); - }); - }); - - describe('CHECK_PERMISSIONS_SUCCEEDED', () => { - it('should set the permissions correctly', () => { - const initialState = { - isLoading: true, - menu: [ - { - id: 'global', - links: [ - { - to: 'global.test', - isDisplayed: false, - }, - { - to: 'global.test2', - isDisplayed: false, - }, - ], - }, - { - id: 'test', - links: [ - { - to: 'test.test', - isDisplayed: false, - }, - { - to: 'test.test2', - isDisplayed: false, - }, - { - to: 'test.test3', - isDisplayed: false, - }, - ], - }, - { - id: 'test1', - links: [ - { - to: 'test1.test', - isDisplayed: false, - }, - { - to: 'test1.test2', - isDisplayed: false, - }, - { - to: 'test1.test3', - isDisplayed: false, - }, - ], - }, - ], - }; - const action = { - type: 'CHECK_PERMISSIONS_SUCCEEDED', - data: [ - { hasPermission: true, path: '1.links.0' }, - { hasPermission: true, path: '1.links.1' }, - { hasPermission: true, path: '0.links.1' }, - { hasPermission: undefined, path: '2.links.0' }, - ], - }; - - const expected = { - isLoading: false, - menu: [ - { - id: 'global', - links: [ - { - to: 'global.test2', - isDisplayed: true, - }, - ], - }, - { - id: 'test', - links: [ - { - to: 'test.test', - isDisplayed: true, - }, - { - to: 'test.test2', - isDisplayed: true, - }, - ], - }, - { - id: 'test1', - links: [], - }, - ], - }; - - expect(reducer(initialState, action)).toEqual(expected); - }); - }); -}); diff --git a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/adminLinks.js b/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/adminLinks.js deleted file mode 100644 index 9accbe36fa..0000000000 --- a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/adminLinks.js +++ /dev/null @@ -1,5 +0,0 @@ -import customAdminLinks from 'ee_else_ce/hooks/useSettingsMenu/utils/customAdminLinks'; - -import defaultAdminLinks from './defaultAdminLinks'; - -export default [...customAdminLinks, ...defaultAdminLinks]; diff --git a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/customAdminLinks.js b/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/customAdminLinks.js deleted file mode 100644 index d6d1738de6..0000000000 --- a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/customAdminLinks.js +++ /dev/null @@ -1 +0,0 @@ -export default []; diff --git a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/customGlobalLinks.js b/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/customGlobalLinks.js deleted file mode 100644 index d6d1738de6..0000000000 --- a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/customGlobalLinks.js +++ /dev/null @@ -1 +0,0 @@ -export default []; diff --git a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/defaultAdminLinks.js b/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/defaultAdminLinks.js deleted file mode 100644 index 28d0644fb0..0000000000 --- a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/defaultAdminLinks.js +++ /dev/null @@ -1,21 +0,0 @@ -import adminPermissions from '../../../permissions'; - -const defaultAdminLinks = [ - { - intlLabel: { id: 'global.roles', defaultMessage: 'Roles' }, - to: '/settings/roles', - id: 'roles', - isDisplayed: false, - permissions: adminPermissions.settings.roles.main, - }, - { - intlLabel: { id: 'global.users' }, - // Init the search params directly - to: '/settings/users?pageSize=10&page=1&sort=firstname', - id: 'users', - isDisplayed: false, - permissions: adminPermissions.settings.users.main, - }, -]; - -export default defaultAdminLinks; diff --git a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/defaultGlobalLinks.js b/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/defaultGlobalLinks.js deleted file mode 100644 index fee1aedc0b..0000000000 --- a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/defaultGlobalLinks.js +++ /dev/null @@ -1,34 +0,0 @@ -import adminPermissions from '../../../permissions'; - -const defaultGlobalLinks = [ - { - intlLabel: { id: 'Settings.application.title', defaultMessage: 'Overview' }, - to: '/settings/application-infos', - id: '000-application-infos', - isDisplayed: false, - permissions: [], - }, - { - intlLabel: { id: 'Settings.webhooks.title', defaultMessage: 'Webhooks' }, - to: '/settings/webhooks', - id: 'webhooks', - isDisplayed: false, - permissions: adminPermissions.settings.webhooks.main, - }, - { - intlLabel: { id: 'Settings.apiTokens.title', defaultMessage: 'API Tokens' }, - to: '/settings/api-tokens?sort=name:ASC', - id: 'api-tokens', - isDisplayed: false, - permissions: adminPermissions.settings['api-tokens'].main, - }, - { - intlLabel: { id: 'Settings.transferTokens.title', defaultMessage: 'Transfer Tokens' }, - to: '/settings/transfer-tokens?sort=name:ASC', - id: 'transfer-tokens', - isDisplayed: false, - permissions: adminPermissions.settings['transfer-tokens'].main, - }, -]; - -export default defaultGlobalLinks; diff --git a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/globalLinks.js b/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/globalLinks.js deleted file mode 100644 index 2af1c7708f..0000000000 --- a/packages/core/admin/admin/src/hooks/useSettingsMenu/utils/globalLinks.js +++ /dev/null @@ -1,5 +0,0 @@ -import customGlobalLinks from 'ee_else_ce/hooks/useSettingsMenu/utils/customGlobalLinks'; - -import defaultGlobalLinks from './defaultGlobalLinks'; - -export default [...defaultGlobalLinks, ...customGlobalLinks]; diff --git a/packages/core/admin/admin/src/pages/App/constants.js b/packages/core/admin/admin/src/pages/App/constants.js index c714ab8592..863003a1b9 100644 --- a/packages/core/admin/admin/src/pages/App/constants.js +++ b/packages/core/admin/admin/src/pages/App/constants.js @@ -1 +1,3 @@ export const SET_APP_RUNTIME_STATUS = 'StrapiAdmin/APP/SET_APP_RUNTIME_STATUS'; + +export const ROUTES_CE = []; diff --git a/packages/core/admin/admin/src/pages/App/index.js b/packages/core/admin/admin/src/pages/App/index.js index 711aa401b3..830ad99aa9 100644 --- a/packages/core/admin/admin/src/pages/App/index.js +++ b/packages/core/admin/admin/src/pages/App/index.js @@ -21,19 +21,27 @@ import { Route, Switch } from 'react-router-dom'; import PrivateRoute from '../../components/PrivateRoute'; import { useConfigurations } from '../../hooks'; +import { useEnterprise } from '../../hooks/useEnterprise'; import { createRoute, makeUniqueRoutes } from '../../utils'; import AuthPage from '../AuthPage'; import NotFoundPage from '../NotFoundPage'; import UseCasePage from '../UseCasePage'; +import { ROUTES_CE } from './constants'; import { getUID } from './utils'; -import routes from './utils/routes'; const AuthenticatedApp = lazy(() => import(/* webpackChunkName: "Admin-authenticatedApp" */ '../../components/AuthenticatedApp') ); function App() { + const routes = useEnterprise( + ROUTES_CE, + async () => (await import('../../../../ee/admin/pages/App/constants')).ROUTES_EE, + { + defaultValue: [], + } + ); const toggleNotification = useNotification(); const { updateProjectSettings } = useConfigurations(); const { formatMessage } = useIntl(); @@ -48,7 +56,7 @@ function App() { return makeUniqueRoutes( routes.map(({ to, Component, exact }) => createRoute(Component, to, exact)) ); - }, []); + }, [routes]); const [telemetryProperties, setTelemetryProperties] = useState(null); diff --git a/packages/core/admin/admin/src/pages/App/utils/customRoutes.js b/packages/core/admin/admin/src/pages/App/utils/customRoutes.js deleted file mode 100644 index 090dc83436..0000000000 --- a/packages/core/admin/admin/src/pages/App/utils/customRoutes.js +++ /dev/null @@ -1,3 +0,0 @@ -const customRoutes = []; - -export default customRoutes; diff --git a/packages/core/admin/admin/src/pages/App/utils/defaultRoutes.js b/packages/core/admin/admin/src/pages/App/utils/defaultRoutes.js deleted file mode 100644 index a8ac1b41be..0000000000 --- a/packages/core/admin/admin/src/pages/App/utils/defaultRoutes.js +++ /dev/null @@ -1,3 +0,0 @@ -const defaultRoutes = []; - -export default defaultRoutes; diff --git a/packages/core/admin/admin/src/pages/App/utils/index.js b/packages/core/admin/admin/src/pages/App/utils/index.js index 34ffa1453d..bb5c7e9658 100644 --- a/packages/core/admin/admin/src/pages/App/utils/index.js +++ b/packages/core/admin/admin/src/pages/App/utils/index.js @@ -1,3 +1,3 @@ /* eslint-disable import/prefer-default-export */ -export { default as routes } from './routes'; + export { default as getUID } from './unique-identifier'; diff --git a/packages/core/admin/admin/src/pages/App/utils/routes.js b/packages/core/admin/admin/src/pages/App/utils/routes.js deleted file mode 100644 index 79ecbbf3bf..0000000000 --- a/packages/core/admin/admin/src/pages/App/utils/routes.js +++ /dev/null @@ -1,5 +0,0 @@ -import customRoutes from 'ee_else_ce/pages/App/utils/customRoutes'; - -import defaultRoutes from './defaultRoutes'; - -export default [...customRoutes, ...defaultRoutes]; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/utils/defaultRoutes.js b/packages/core/admin/admin/src/pages/SettingsPage/constants.js similarity index 64% rename from packages/core/admin/admin/src/pages/SettingsPage/utils/defaultRoutes.js rename to packages/core/admin/admin/src/pages/SettingsPage/constants.js index 4e07a98a7d..8c9c255bc6 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/utils/defaultRoutes.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/constants.js @@ -1,8 +1,8 @@ -const defaultRoutes = [ +export const ROUTES_CE = [ { async Component() { const component = await import( - /* webpackChunkName: "admin-roles-list" */ '../pages/Roles/ProtectedListPage' + /* webpackChunkName: "admin-roles-list" */ './pages/Roles/ProtectedListPage' ); return component; @@ -13,7 +13,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "admin-edit-roles-page" */ '../pages/Roles/CreatePage' + /* webpackChunkName: "admin-edit-roles-page" */ './pages/Roles/CreatePage' ); return component; @@ -24,7 +24,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "admin-edit-roles-page" */ '../pages/Roles/CreatePage' + /* webpackChunkName: "admin-edit-roles-page" */ './pages/Roles/CreatePage' ); return component; @@ -35,7 +35,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "admin-edit-roles-page" */ '../pages/Roles/ProtectedEditPage' + /* webpackChunkName: "admin-edit-roles-page" */ './pages/Roles/ProtectedEditPage' ); return component; @@ -46,7 +46,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "admin-users" */ '../pages/Users/ProtectedListPage' + /* webpackChunkName: "admin-users" */ './pages/Users/ProtectedListPage' ); return component; @@ -57,7 +57,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "admin-edit-users" */ '../pages/Users/ProtectedEditPage' + /* webpackChunkName: "admin-edit-users" */ './pages/Users/ProtectedEditPage' ); return component; @@ -68,7 +68,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "webhook-edit-page" */ '../pages/Webhooks/ProtectedCreateView' + /* webpackChunkName: "webhook-edit-page" */ './pages/Webhooks/ProtectedCreateView' ); return component; @@ -79,7 +79,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "webhook-edit-page" */ '../pages/Webhooks/ProtectedEditView' + /* webpackChunkName: "webhook-edit-page" */ './pages/Webhooks/ProtectedEditView' ); return component; @@ -90,7 +90,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "webhook-list-page" */ '../pages/Webhooks/ProtectedListView' + /* webpackChunkName: "webhook-list-page" */ './pages/Webhooks/ProtectedListView' ); return component; @@ -101,7 +101,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "api-tokens-list-page" */ '../pages/ApiTokens/ProtectedListView' + /* webpackChunkName: "api-tokens-list-page" */ './pages/ApiTokens/ProtectedListView' ); return component; @@ -112,7 +112,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "api-tokens-create-page" */ '../pages/ApiTokens/ProtectedCreateView' + /* webpackChunkName: "api-tokens-create-page" */ './pages/ApiTokens/ProtectedCreateView' ); return component; @@ -123,7 +123,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "api-tokens-edit-page" */ '../pages/ApiTokens/ProtectedEditView' + /* webpackChunkName: "api-tokens-edit-page" */ './pages/ApiTokens/ProtectedEditView' ); return component; @@ -134,7 +134,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "transfer-tokens-create-page" */ '../pages/TransferTokens/ProtectedCreateView' + /* webpackChunkName: "transfer-tokens-create-page" */ './pages/TransferTokens/ProtectedCreateView' ); return component; @@ -145,7 +145,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "transfer-tokens-list-page" */ '../pages/TransferTokens/ProtectedListView' + /* webpackChunkName: "transfer-tokens-list-page" */ './pages/TransferTokens/ProtectedListView' ); return component; @@ -156,7 +156,7 @@ const defaultRoutes = [ { async Component() { const component = await import( - /* webpackChunkName: "transfer-tokens-edit-page" */ '../pages/TransferTokens/ProtectedEditView' + /* webpackChunkName: "transfer-tokens-edit-page" */ './pages/TransferTokens/ProtectedEditView' ); return component; @@ -165,5 +165,3 @@ const defaultRoutes = [ exact: true, }, ]; - -export default defaultRoutes; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/index.js b/packages/core/admin/admin/src/pages/SettingsPage/index.js index 6f76be3f9e..8cb573afe8 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/index.js @@ -18,24 +18,36 @@ import { useIntl } from 'react-intl'; import { Redirect, Route, Switch, useParams } from 'react-router-dom'; import { useSettingsMenu } from '../../hooks'; +import { useEnterprise } from '../../hooks/useEnterprise'; import { createRoute, makeUniqueRoutes } from '../../utils'; import SettingsNav from './components/SettingsNav'; +import { ROUTES_CE } from './constants'; import ApplicationInfosPage from './pages/ApplicationInfosPage'; -import { createSectionsRoutes, routes } from './utils'; +import { createSectionsRoutes } from './utils'; function SettingsPage() { const { settingId } = useParams(); const { settings } = useStrapiApp(); const { formatMessage } = useIntl(); const { isLoading, menu } = useSettingsMenu(); + const routes = useEnterprise( + ROUTES_CE, + async () => (await import('../../../../ee/admin/pages/SettingsPage/constants')).ROUTES_EE, + { + combine(ceRoutes, eeRoutes) { + return [...ceRoutes, ...eeRoutes]; + }, + defaultValue: [], + } + ); // Creates the admin routes const adminRoutes = useMemo(() => { return makeUniqueRoutes( routes.map(({ to, Component, exact }) => createRoute(Component, to, exact)) ); - }, []); + }, [routes]); const pluginsRoutes = createSectionsRoutes(settings); diff --git a/packages/core/admin/admin/src/pages/SettingsPage/utils/customRoutes.js b/packages/core/admin/admin/src/pages/SettingsPage/utils/customRoutes.js deleted file mode 100644 index 090dc83436..0000000000 --- a/packages/core/admin/admin/src/pages/SettingsPage/utils/customRoutes.js +++ /dev/null @@ -1,3 +0,0 @@ -const customRoutes = []; - -export default customRoutes; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/utils/index.js b/packages/core/admin/admin/src/pages/SettingsPage/utils/index.js index ca968308d9..3b1fbde662 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/utils/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/utils/index.js @@ -1,3 +1,2 @@ export { default as createSectionsRoutes } from './createSectionsRoutes'; export { default as getSectionsToDisplay } from './getSectionsToDisplay'; -export { default as routes } from './routes'; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/utils/routes.js b/packages/core/admin/admin/src/pages/SettingsPage/utils/routes.js deleted file mode 100644 index 663fd8cbdc..0000000000 --- a/packages/core/admin/admin/src/pages/SettingsPage/utils/routes.js +++ /dev/null @@ -1,6 +0,0 @@ -// This file makes it easier to make the difference between the ee and ce version -import customRoutes from 'ee_else_ce/pages/SettingsPage/utils/customRoutes'; - -import defaultRoutes from './defaultRoutes'; - -export default [...customRoutes, ...defaultRoutes]; diff --git a/packages/core/admin/ee/admin/hooks/useSettingsMenu/constants.js b/packages/core/admin/ee/admin/hooks/useSettingsMenu/constants.js new file mode 100644 index 0000000000..d009de996a --- /dev/null +++ b/packages/core/admin/ee/admin/hooks/useSettingsMenu/constants.js @@ -0,0 +1,43 @@ +import adminPermissions from '../../../../admin/src/permissions'; + +export const LINKS_EE = { + global: [ + ...(window.strapi.features.isEnabled(window.strapi.features.SSO) + ? [ + { + intlLabel: { id: 'Settings.sso.title', defaultMessage: 'Single Sign-On' }, + to: '/settings/single-sign-on', + id: 'sso', + permissions: adminPermissions.settings.sso.main, + }, + ] + : []), + + ...(window.strapi.features.isEnabled(window.strapi.features.REVIEW_WORKFLOWS) + ? [ + { + intlLabel: { + id: 'Settings.review-workflows.page.title', + defaultMessage: 'Review Workflows', + }, + to: '/settings/review-workflows', + id: 'review-workflows', + permissions: adminPermissions.settings['review-workflows'].main, + }, + ] + : []), + ], + + admin: [ + ...(window.strapi.features.isEnabled(window.strapi.features.AUDIT_LOGS) + ? [ + { + intlLabel: { id: 'global.auditLogs', defaultMessage: 'Audit Logs' }, + to: '/settings/audit-logs?pageSize=50&page=1&sort=date:DESC', + id: 'auditLogs', + permissions: adminPermissions.settings.auditLogs.main, + }, + ] + : []), + ], +}; diff --git a/packages/core/admin/ee/admin/hooks/useSettingsMenu/utils/customAdminLinks.js b/packages/core/admin/ee/admin/hooks/useSettingsMenu/utils/customAdminLinks.js deleted file mode 100644 index d4785e2f7a..0000000000 --- a/packages/core/admin/ee/admin/hooks/useSettingsMenu/utils/customAdminLinks.js +++ /dev/null @@ -1,17 +0,0 @@ -import adminPermissions from '../../../../../admin/src/permissions'; - -const items = []; - -if (window.strapi.features.isEnabled(window.strapi.features.AUDIT_LOGS)) { - items.push({ - intlLabel: { id: 'global.auditLogs', defaultMessage: 'Audit Logs' }, - to: '/settings/audit-logs?pageSize=50&page=1&sort=date:DESC', - id: 'auditLogs', - isDisplayed: false, - permissions: adminPermissions.settings.auditLogs.main, - }); -} - -const customAdminLinks = items; - -export default customAdminLinks; diff --git a/packages/core/admin/ee/admin/hooks/useSettingsMenu/utils/customGlobalLinks.js b/packages/core/admin/ee/admin/hooks/useSettingsMenu/utils/customGlobalLinks.js deleted file mode 100644 index fea5e63dc7..0000000000 --- a/packages/core/admin/ee/admin/hooks/useSettingsMenu/utils/customGlobalLinks.js +++ /dev/null @@ -1,25 +0,0 @@ -import adminPermissions from '../../../../../admin/src/permissions'; - -const items = []; - -if (window.strapi.features.isEnabled(window.strapi.features.SSO)) { - items.push({ - intlLabel: { id: 'Settings.sso.title', defaultMessage: 'Single Sign-On' }, - to: '/settings/single-sign-on', - id: 'sso', - isDisplayed: false, - permissions: adminPermissions.settings.sso.main, - }); -} - -if (window.strapi.features.isEnabled(window.strapi.features.REVIEW_WORKFLOWS)) { - items.push({ - intlLabel: { id: 'Settings.review-workflows.page.title', defaultMessage: 'Review Workflows' }, - to: '/settings/review-workflows', - id: 'review-workflows', - isDisplayed: false, - permissions: adminPermissions.settings['review-workflows'].main, - }); -} - -export default items; diff --git a/packages/core/admin/ee/admin/pages/App/utils/customRoutes.js b/packages/core/admin/ee/admin/pages/App/constants.js similarity index 53% rename from packages/core/admin/ee/admin/pages/App/utils/customRoutes.js rename to packages/core/admin/ee/admin/pages/App/constants.js index 785f7758dc..ee674703a7 100644 --- a/packages/core/admin/ee/admin/pages/App/utils/customRoutes.js +++ b/packages/core/admin/ee/admin/pages/App/constants.js @@ -1,11 +1,9 @@ -import AuthResponse from '../../AuthResponse'; +import { AuthResponse } from '../AuthResponse'; -const customRoutes = [ +export const ROUTES_EE = [ { Component: () => ({ default: AuthResponse }), to: '/auth/login/:authResponse', exact: true, }, ]; - -export default customRoutes; diff --git a/packages/core/admin/ee/admin/pages/AuthResponse/index.js b/packages/core/admin/ee/admin/pages/AuthResponse/index.js index b6c8081280..1fe8452759 100644 --- a/packages/core/admin/ee/admin/pages/AuthResponse/index.js +++ b/packages/core/admin/ee/admin/pages/AuthResponse/index.js @@ -7,7 +7,7 @@ import { useHistory, useRouteMatch } from 'react-router-dom'; import { getRequestUrl } from '../../../../admin/src/utils'; -const AuthResponse = () => { +export const AuthResponse = () => { const { params: { authResponse }, } = useRouteMatch('/auth/login/:authResponse'); diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/constants.js b/packages/core/admin/ee/admin/pages/SettingsPage/constants.js new file mode 100644 index 0000000000..9918695c3c --- /dev/null +++ b/packages/core/admin/ee/admin/pages/SettingsPage/constants.js @@ -0,0 +1,49 @@ +export const ROUTES_EE = [ + ...(window.strapi.features.isEnabled(window.strapi.features.AUDIT_LOGS) + ? [ + { + async Component() { + const component = await import( + /* webpackChunkName: "audit-logs-settings-page" */ './pages/AuditLogs/ProtectedListPage' + ); + + return component; + }, + to: '/settings/audit-logs', + exact: true, + }, + ] + : []), + + ...(window.strapi.features.isEnabled(window.strapi.features.REVIEW_WORKFLOWS) + ? [ + { + async Component() { + const component = await import( + /* webpackChunkName: "review-workflows-settings" */ './pages/ReviewWorkflows' + ); + + return component; + }, + to: '/settings/review-workflows', + exact: true, + }, + ] + : []), + + ...(window.strapi.features.isEnabled(window.strapi.features.SSO) + ? [ + { + async Component() { + const component = await import( + /* webpackChunkName: "sso-settings-page" */ './pages/SingleSignOn' + ); + + return component; + }, + to: '/settings/single-sign-on', + exact: true, + }, + ] + : []), +]; diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/utils/customRoutes.js b/packages/core/admin/ee/admin/pages/SettingsPage/utils/customRoutes.js deleted file mode 100644 index dd800e6ad2..0000000000 --- a/packages/core/admin/ee/admin/pages/SettingsPage/utils/customRoutes.js +++ /dev/null @@ -1,45 +0,0 @@ -const routes = []; - -if (window.strapi.features.isEnabled(window.strapi.features.SSO)) { - routes.push({ - async Component() { - const component = await import( - /* webpackChunkName: "sso-settings-page" */ '../pages/SingleSignOn' - ); - - return component; - }, - to: '/settings/single-sign-on', - exact: true, - }); -} - -if (window.strapi.features.isEnabled(window.strapi.features.REVIEW_WORKFLOWS)) { - routes.push({ - async Component() { - const component = await import( - /* webpackChunkName: "review-workflows-settings" */ '../pages/ReviewWorkflows' - ); - - return component; - }, - to: '/settings/review-workflows', - exact: true, - }); -} - -if (window.strapi.features.isEnabled(window.strapi.features.AUDIT_LOGS)) { - routes.push({ - async Component() { - const component = await import( - /* webpackChunkName: "audit-logs-settings-page" */ '../pages/AuditLogs/ProtectedListPage' - ); - - return component; - }, - to: '/settings/audit-logs', - exact: true, - }); -} - -export default routes; diff --git a/packages/core/strapi/lib/types/core-api/router.d.ts b/packages/core/strapi/lib/types/core-api/router.d.ts index 5368985122..f74cebdecb 100644 --- a/packages/core/strapi/lib/types/core-api/router.d.ts +++ b/packages/core/strapi/lib/types/core-api/router.d.ts @@ -48,7 +48,10 @@ export type RouterConfig