diff --git a/.changeset/cyan-ties-tease.md b/.changeset/cyan-ties-tease.md new file mode 100644 index 00000000..84df2c10 --- /dev/null +++ b/.changeset/cyan-ties-tease.md @@ -0,0 +1,11 @@ +--- +"@comet/brevo-admin": major +--- + +Remove factory createEmailCampaignsPage + +The `EmailCampaignsPage` can now be created like this: + +```tsx + +``` diff --git a/.changeset/witty-coats-perform.md b/.changeset/witty-coats-perform.md new file mode 100644 index 00000000..ab41905e --- /dev/null +++ b/.changeset/witty-coats-perform.md @@ -0,0 +1,24 @@ +--- +"@comet/brevo-admin": major +--- + +Define scopeParts in BrevoConfig + +Previously the scopeParts had to be defined in the functions: + +- createBrevoContactsPage +- createTargetGroupsPage +- createEmailCampaignsPage + +Now it has to be defined once in the BrevoConfig: + +```tsx + + {children} + +``` diff --git a/demo/admin/src/App.tsx b/demo/admin/src/App.tsx index 5d4d66b8..7db3d016 100644 --- a/demo/admin/src/App.tsx +++ b/demo/admin/src/App.tsx @@ -2,7 +2,7 @@ import "@fontsource-variable/roboto-flex/full.css"; import "material-design-icons/iconfont/material-icons.css"; import { ApolloProvider } from "@apollo/client"; -import { ErrorDialogHandler, MuiThemeProvider, RouterBrowserRouter, SnackbarProvider } from "@comet/admin"; +import { ErrorDialogHandler, MasterLayout, MuiThemeProvider, RouterBrowserRouter, SnackbarProvider } from "@comet/admin"; import { BrevoConfigProvider } from "@comet/brevo-admin"; import { AllCategories, @@ -11,22 +11,26 @@ import { createHttpClient, CurrentUserProvider, LocaleProvider, + SitePreview, SitesConfigProvider, } from "@comet/cms-admin"; import { css, Global } from "@emotion/react"; -import { ContentScope } from "@src/common/ContentScopeProvider"; +import { ContentScope, ContentScopeProvider } from "@src/common/ContentScopeProvider"; +import { MasterRoutes } from "@src/common/MasterMenu"; import { getMessages } from "@src/lang"; import { theme } from "@src/theme"; import * as React from "react"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; import { FormattedMessage, IntlProvider } from "react-intl"; +import { Route, Switch } from "react-router"; import { createApolloClient } from "./common/apollo/createApolloClient"; +import { MasterHeader } from "./common/MasterHeader"; +import { AppMasterMenu } from "./common/MasterMenu"; import { createConfig } from "./config"; import { Link } from "./documents/links/Link"; import { Page } from "./documents/pages/Page"; -import { Routes } from "./Routes"; const GlobalStyle = () => ( { return `${config.campaignUrl}/block-preview/${scope.domain}/${scope.language}`; @@ -99,7 +104,21 @@ export function App() { > - + + {({ match }) => ( + + } + /> + + + + + + + )} + diff --git a/demo/admin/src/Routes.tsx b/demo/admin/src/Routes.tsx deleted file mode 100644 index 2e31869a..00000000 --- a/demo/admin/src/Routes.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { MasterLayout, RouteWithErrorBoundary } from "@comet/admin"; -import { createBrevoContactsPage, createEmailCampaignsPage, createTargetGroupsPage } from "@comet/brevo-admin"; -import { ContentScopeIndicator, createRedirectsPage, DamPage, PagesPage, PublisherPage, SitePreview } from "@comet/cms-admin"; -import { getBrevoContactConfig } from "@src/common/brevoModuleConfig/brevoContactsPageAttributesConfig"; -import { pageTreeCategories, urlParamToCategory } from "@src/pageTree/pageTreeCategories"; -import * as React from "react"; -import { useIntl } from "react-intl"; -import { RouteComponentProps } from "react-router"; -import { Redirect, Route, Switch } from "react-router-dom"; - -import { additionalFormConfig } from "./common/brevoModuleConfig/targetGroupFormConfig"; -import { ContentScope, ContentScopeProvider } from "./common/ContentScopeProvider"; -import { MasterHeader } from "./common/MasterHeader"; -import { MasterMenu } from "./common/MasterMenu"; -import { DashboardPage } from "./dashboard/DashboardPage"; -import { Link } from "./documents/links/Link"; -import { Page } from "./documents/pages/Page"; -import { EmailCampaignContentBlock } from "./emailCampaigns/blocks/EmailCampaignContentBlock"; - -const RedirectsPage = createRedirectsPage(); - -export const Routes: React.FC = () => { - const intl = useIntl(); - const brevoContactConfig = getBrevoContactConfig(intl); - - const BrevoContactsPage = createBrevoContactsPage({ - scopeParts: ["domain", "language"], - additionalAttributesFragment: brevoContactConfig.additionalAttributesFragment, - additionalGridFields: brevoContactConfig.additionalGridFields, - additionalFormFields: brevoContactConfig.additionalFormFields, - input2State: brevoContactConfig.input2State, - }); - - const TargetGroupsPage = createTargetGroupsPage({ - scopeParts: ["domain", "language"], - additionalFormFields: additionalFormConfig.additionalFormFields, - exportTargetGroupOptions: { - additionalAttributesFragment: brevoContactConfig.additionalAttributesFragment, - exportFields: brevoContactConfig.exportFields, - }, - nodeFragment: additionalFormConfig.nodeFragment, - input2State: additionalFormConfig.input2State, - }); - - const EmailCampaignsPage = createEmailCampaignsPage({ - scopeParts: ["domain", "language"], - EmailCampaignContentBlock: EmailCampaignContentBlock, - }); - - return ( - - {({ match }) => ( - - } /> - ( - - - - ) => { - const category = urlParamToCategory(params.category); - - if (category === undefined) { - return ; - } - - return ( - } - /> - ); - }} - /> - - - - - - - - - } - /> - - - - )} - /> - - )} - - ); -}; diff --git a/demo/admin/src/common/MasterMenu.tsx b/demo/admin/src/common/MasterMenu.tsx index 5281c6eb..07776154 100644 --- a/demo/admin/src/common/MasterMenu.tsx +++ b/demo/admin/src/common/MasterMenu.tsx @@ -1,69 +1,169 @@ -import { Menu, MenuCollapsibleItem, MenuContext, MenuItemRouterLink, useWindowSize } from "@comet/admin"; import { Assets, Dashboard, Mail, PageTree, Wrench } from "@comet/admin-icons"; -import * as React from "react"; -import { useIntl } from "react-intl"; -import { useRouteMatch } from "react-router"; +import { createBrevoContactsPage, createTargetGroupsPage, EmailCampaignsPage } from "@comet/brevo-admin"; +import { + AllCategories, + ContentScopeIndicator, + createRedirectsPage, + DamPage, + DocumentInterface, + MasterMenu, + MasterMenuData, + MasterMenuRoutes, + PagesPage, + PublisherPage, +} from "@comet/cms-admin"; +import { BrevoContactConfig, getBrevoContactConfig } from "@src/common/brevoModuleConfig/brevoContactsPageAttributesConfig"; +import { additionalFormConfig } from "@src/common/brevoModuleConfig/targetGroupFormConfig"; +import { DashboardPage } from "@src/dashboard/DashboardPage"; +import { Link } from "@src/documents/links/Link"; +import { Page } from "@src/documents/pages/Page"; +import { EmailCampaignContentBlock } from "@src/emailCampaigns/blocks/EmailCampaignContentBlock"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; -const permanentMenuMinWidth = 1024; +export const pageTreeCategories: AllCategories = [ + { + category: "MainNavigation", + label: , + }, +]; -export const MasterMenu: React.FC = () => { - const { open, toggleOpen } = React.useContext(MenuContext); - const windowSize = useWindowSize(); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const pageTreeDocumentTypes: Record> = { + Page, + Link, +}; +const RedirectsPage = createRedirectsPage({ scopeParts: ["domain"] }); + +const getMasterMenuData = ({ brevoContactConfig }: { brevoContactConfig: BrevoContactConfig }): MasterMenuData => { + const BrevoContactsPage = createBrevoContactsPage({ + additionalAttributesFragment: brevoContactConfig.additionalAttributesFragment, + additionalGridFields: brevoContactConfig.additionalGridFields, + additionalFormFields: brevoContactConfig.additionalFormFields, + input2State: brevoContactConfig.input2State, + }); + + const TargetGroupsPage = createTargetGroupsPage({ + additionalFormFields: additionalFormConfig.additionalFormFields, + exportTargetGroupOptions: { + additionalAttributesFragment: brevoContactConfig.additionalAttributesFragment, + exportFields: brevoContactConfig.exportFields, + }, + nodeFragment: additionalFormConfig.nodeFragment, + input2State: additionalFormConfig.input2State, + }); + + return [ + { + type: "route", + primary: , + icon: , + route: { + path: "/dashboard", + component: DashboardPage, + }, + }, + { + type: "route", + primary: , + icon: , + route: { + path: "/pages/pagetree/main-navigation", + render: () => ( + } + /> + ), + }, + requiredPermission: "pageTree", + }, + { + type: "collapsible", + primary: , + icon: , + items: [ + { + type: "route", + primary: , + route: { + path: "/newsletter/email-campaigns", + component: () => , + }, + }, + { + type: "route", + primary: , + route: { + path: "/newsletter/contacts", + render: () => , + }, + }, + { + type: "route", + primary: , + route: { + path: "/newsletter/target-groups", + render: () => , + }, + }, + ], + requiredPermission: "brevo-newsletter", + }, + { + type: "route", + primary: , + icon: , + route: { + path: "/assets", + component: DamPage, + }, + requiredPermission: "dam", + }, + { + type: "collapsible", + primary: , + icon: , + items: [ + { + type: "route", + primary: , + route: { + path: "/system/publisher", + component: PublisherPage, + }, + requiredPermission: "builds", + }, + { + type: "route", + primary: , + route: { + path: "/system/redirects", + render: () => , + }, + requiredPermission: "pageTree", + }, + ], + requiredPermission: "pageTree", + }, + ]; +}; + +export const AppMasterMenu = () => { const intl = useIntl(); - const match = useRouteMatch(); - const useTemporaryMenu: boolean = windowSize.width < permanentMenuMinWidth; + const masterMenuDataForScope = React.useMemo(() => getMasterMenuData({ brevoContactConfig: getBrevoContactConfig(intl) }), [intl]); + + return ; +}; + +export const MasterRoutes = () => { + const intl = useIntl(); - // Open menu when changing to permanent variant and close when changing to temporary variant. - React.useEffect(() => { - if ((useTemporaryMenu && open) || (!useTemporaryMenu && !open)) { - toggleOpen(); - } - // useEffect dependencies must only include `location`, because the function should only be called once after changing the location. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [location]); + const masterMenuDataForScope = React.useMemo(() => getMasterMenuData({ brevoContactConfig: getBrevoContactConfig(intl) }), [intl]); - return ( - - } - to={`${match.url}/dashboard`} - /> - } - to={`${match.url}/pages/pagetree/main-navigation`} - /> - }> - - - - - } - to={`${match.url}/assets`} - /> - }> - - - - - ); + return ; }; diff --git a/demo/admin/src/common/brevoModuleConfig/brevoContactsPageAttributesConfig.tsx b/demo/admin/src/common/brevoModuleConfig/brevoContactsPageAttributesConfig.tsx index 208219c0..d1712c62 100644 --- a/demo/admin/src/common/brevoModuleConfig/brevoContactsPageAttributesConfig.tsx +++ b/demo/admin/src/common/brevoModuleConfig/brevoContactsPageAttributesConfig.tsx @@ -59,9 +59,7 @@ export const additionalFormConfig = { nodeFragment: attributesFragment, }; -export const getBrevoContactConfig = ( - intl: IntlShape, -): { +export interface BrevoContactConfig { additionalGridFields: GridColDef[]; additionalFormFields: React.ReactNode; additionalAttributesFragment: { @@ -77,7 +75,9 @@ export const getBrevoContactConfig = ( renderValue: (row: GQLBrevoContactAttributesFragmentFragment) => string; headerName: string; }[]; -} => { +} + +export const getBrevoContactConfig = (intl: IntlShape): BrevoContactConfig => { return { additionalGridFields: [ { diff --git a/packages/admin/src/brevoContacts/BrevoContactsPage.tsx b/packages/admin/src/brevoContacts/BrevoContactsPage.tsx index 8ad01cb0..15c59038 100644 --- a/packages/admin/src/brevoContacts/BrevoContactsPage.tsx +++ b/packages/admin/src/brevoContacts/BrevoContactsPage.tsx @@ -4,11 +4,11 @@ import { DocumentNode } from "graphql"; import * as React from "react"; import { useIntl } from "react-intl"; +import { useBrevoConfig } from "../common/BrevoConfigProvider"; import { BrevoContactsGrid } from "./BrevoContactsGrid"; import { BrevoContactForm, EditBrevoContactFormValues } from "./form/BrevoContactForm"; interface CreateContactsPageOptions { - scopeParts: string[]; additionalAttributesFragment?: { name: string; fragment: DocumentNode }; additionalGridFields?: GridColDef[]; additionalFormFields?: React.ReactNode; @@ -16,7 +16,6 @@ interface CreateContactsPageOptions { } function createBrevoContactsPage({ - scopeParts, additionalAttributesFragment, additionalFormFields, additionalGridFields, @@ -24,6 +23,7 @@ function createBrevoContactsPage({ }: CreateContactsPageOptions) { function BrevoContactsPage(): JSX.Element { const intl = useIntl(); + const { scopeParts } = useBrevoConfig(); const { scope: completeScope } = useContentScope(); const scope = scopeParts.reduce((acc, scopePart) => { diff --git a/packages/admin/src/common/BrevoConfigProvider.tsx b/packages/admin/src/common/BrevoConfigProvider.tsx index bce4926c..3f067da4 100644 --- a/packages/admin/src/common/BrevoConfigProvider.tsx +++ b/packages/admin/src/common/BrevoConfigProvider.tsx @@ -3,6 +3,7 @@ import React from "react"; export interface BrevoConfig { apiUrl: string; + scopeParts: string[]; resolvePreviewUrlForScope: (scope: ContentScopeInterface) => string; } @@ -19,6 +20,7 @@ export const BrevoConfigProvider = ({ children, value }: React.PropsWithChildren interface UseBrevoConfigReturn { apiUrl: string; previewUrl: string; + scopeParts: string[]; } export const useBrevoConfig = (): UseBrevoConfigReturn => { diff --git a/packages/admin/src/emailCampaigns/EmailCampaignsPage.tsx b/packages/admin/src/emailCampaigns/EmailCampaignsPage.tsx index 13b08af5..dbda3f55 100644 --- a/packages/admin/src/emailCampaigns/EmailCampaignsPage.tsx +++ b/packages/admin/src/emailCampaigns/EmailCampaignsPage.tsx @@ -4,53 +4,51 @@ import { ContentScopeIndicator, useContentScope } from "@comet/cms-admin"; import * as React from "react"; import { useIntl } from "react-intl"; +import { useBrevoConfig } from "../common/BrevoConfigProvider"; import { EmailCampaignsGrid } from "./EmailCampaignsGrid"; import { EmailCampaignForm } from "./form/EmailCampaignForm"; import { EmailCampaignStatistics } from "./statistics/EmailCampaignStatistics"; import { EmailCampaignView } from "./view/EmailCampaignView"; -interface CreateEmailCampaignsPageOptions { - scopeParts: string[]; +interface EmailCampaignsPageOptions { EmailCampaignContentBlock: BlockInterface; } -export function createEmailCampaignsPage({ scopeParts, EmailCampaignContentBlock }: CreateEmailCampaignsPageOptions) { - function EmailCampaignsPage(): JSX.Element { - const { scope: completeScope } = useContentScope(); - const intl = useIntl(); +export function EmailCampaignsPage({ EmailCampaignContentBlock }: EmailCampaignsPageOptions): JSX.Element { + const { scopeParts } = useBrevoConfig(); + const { scope: completeScope } = useContentScope(); + const intl = useIntl(); - const scope = scopeParts.reduce((acc, scopePart) => { - acc[scopePart] = completeScope[scopePart]; - return acc; - }, {} as { [key: string]: unknown }); + const scope = scopeParts.reduce((acc, scopePart) => { + acc[scopePart] = completeScope[scopePart]; + return acc; + }, {} as { [key: string]: unknown }); - return ( - - - - } /> - - - {(selectedId) => } - - {(selectedId) => } - + return ( + + + + } /> + + + {(selectedId) => } + + {(selectedId) => } + - - {(selectedId) => } - - - - - - - ); - } - return EmailCampaignsPage; + + {(selectedId) => } + + + + + + + ); } diff --git a/packages/admin/src/index.ts b/packages/admin/src/index.ts index abf8d740..98e9e29c 100644 --- a/packages/admin/src/index.ts +++ b/packages/admin/src/index.ts @@ -1,6 +1,6 @@ export { createBrevoContactsPage } from "./brevoContacts/BrevoContactsPage"; export { EditBrevoContactFormValues } from "./brevoContacts/form/BrevoContactForm"; export { BrevoConfig, BrevoConfigProvider, useBrevoConfig } from "./common/BrevoConfigProvider"; -export { createEmailCampaignsPage } from "./emailCampaigns/EmailCampaignsPage"; +export { EmailCampaignsPage } from "./emailCampaigns/EmailCampaignsPage"; export { EditTargetGroupFinalFormValues } from "./targetGroups/TargetGroupForm"; export { createTargetGroupsPage } from "./targetGroups/TargetGroupsPage"; diff --git a/packages/admin/src/targetGroups/TargetGroupsPage.tsx b/packages/admin/src/targetGroups/TargetGroupsPage.tsx index 9fa9be8c..ffceba69 100644 --- a/packages/admin/src/targetGroups/TargetGroupsPage.tsx +++ b/packages/admin/src/targetGroups/TargetGroupsPage.tsx @@ -4,11 +4,11 @@ import { DocumentNode } from "graphql"; import * as React from "react"; import { useIntl } from "react-intl"; +import { useBrevoConfig } from "../common/BrevoConfigProvider"; import { EditTargetGroupFinalFormValues, TargetGroupForm } from "./TargetGroupForm"; import { AdditionalContactAttributesType, TargetGroupsGrid } from "./TargetGroupsGrid"; interface CreateContactsPageOptions { - scopeParts: string[]; additionalFormFields?: React.ReactNode; exportTargetGroupOptions?: { additionalAttributesFragment: { name: string; fragment: DocumentNode }; @@ -19,14 +19,9 @@ interface CreateContactsPageOptions { valuesToOutput?: (values: EditTargetGroupFinalFormValues) => EditTargetGroupFinalFormValues; } -export function createTargetGroupsPage({ - scopeParts, - additionalFormFields, - nodeFragment, - input2State, - exportTargetGroupOptions, -}: CreateContactsPageOptions) { +export function createTargetGroupsPage({ additionalFormFields, nodeFragment, input2State, exportTargetGroupOptions }: CreateContactsPageOptions) { function TargetGroupsPage(): JSX.Element { + const { scopeParts } = useBrevoConfig(); const { scope: completeScope } = useContentScope(); const intl = useIntl();