From 66be6d1e8991e57a1f7f9a702e9ab2eabaa5c126 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Mon, 6 Mar 2023 18:01:47 +0000 Subject: [PATCH 1/4] fix: return isCloud boolean in getAutomatischInfo --- packages/backend/src/graphql/schema.graphql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index cd7026e8..f3db6a75 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -466,7 +466,7 @@ type AppHealth { } type GetAutomatischInfo { - isCloud: String + isCloud: Boolean } type GetUsageData { From 26d8e5856a1471bb8ac0ccb903584a79ab9a8633 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Mon, 6 Mar 2023 18:04:47 +0000 Subject: [PATCH 2/4] feat: make automatisch info available --- packages/web/src/contexts/AutomatischInfo.tsx | 38 +++++++++++++++++++ .../graphql/queries/get-automatisch-info.ts | 10 +++++ packages/web/src/hooks/useAutomatischInfo.ts | 14 +++++++ packages/web/src/hooks/useCloud.ts | 7 ++++ packages/web/src/index.tsx | 13 ++++--- 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 packages/web/src/contexts/AutomatischInfo.tsx create mode 100644 packages/web/src/graphql/queries/get-automatisch-info.ts create mode 100644 packages/web/src/hooks/useAutomatischInfo.ts create mode 100644 packages/web/src/hooks/useCloud.ts diff --git a/packages/web/src/contexts/AutomatischInfo.tsx b/packages/web/src/contexts/AutomatischInfo.tsx new file mode 100644 index 00000000..9223ffa3 --- /dev/null +++ b/packages/web/src/contexts/AutomatischInfo.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import { useQuery } from '@apollo/client'; + +import { GET_AUTOMATISCH_INFO } from 'graphql/queries/get-automatisch-info'; + +export type AutomatischInfoContextParams = { + isCloud: boolean; +}; + +export const AutomatischInfoContext = + React.createContext({ + isCloud: false, + }); + +type AutomatischInfoProviderProps = { + children: React.ReactNode; +}; + +export const AutomatischInfoProvider = ( + props: AutomatischInfoProviderProps +): React.ReactElement => { + const { children } = props; + const { data } = useQuery(GET_AUTOMATISCH_INFO); + + const isCloud = data?.getAutomatischInfo?.isCloud || false; + + const value = React.useMemo(() => { + return { + isCloud, + }; + }, [isCloud]); + + return ( + + {children} + + ); +}; diff --git a/packages/web/src/graphql/queries/get-automatisch-info.ts b/packages/web/src/graphql/queries/get-automatisch-info.ts new file mode 100644 index 00000000..e8dcdd90 --- /dev/null +++ b/packages/web/src/graphql/queries/get-automatisch-info.ts @@ -0,0 +1,10 @@ +import { gql } from '@apollo/client'; + +export const GET_AUTOMATISCH_INFO = gql` + query GetAutomatischInfo { + getAutomatischInfo { + isCloud + } + } +`; + diff --git a/packages/web/src/hooks/useAutomatischInfo.ts b/packages/web/src/hooks/useAutomatischInfo.ts new file mode 100644 index 00000000..8c33f38b --- /dev/null +++ b/packages/web/src/hooks/useAutomatischInfo.ts @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { AutomatischInfoContext } from 'contexts/AutomatischInfo'; + +type UseAutomatischInfoReturn = { + isCloud: boolean; +}; + +export default function useAutomatischInfo(): UseAutomatischInfoReturn { + const automatischInfoContext = React.useContext(AutomatischInfoContext); + + return { + isCloud: automatischInfoContext.isCloud, + }; +} diff --git a/packages/web/src/hooks/useCloud.ts b/packages/web/src/hooks/useCloud.ts new file mode 100644 index 00000000..43c1360f --- /dev/null +++ b/packages/web/src/hooks/useCloud.ts @@ -0,0 +1,7 @@ +import useAutomatischInfo from './useAutomatischInfo'; + +export default function useCloud(): boolean { + const { isCloud } = useAutomatischInfo(); + + return isCloud; +} diff --git a/packages/web/src/index.tsx b/packages/web/src/index.tsx index bb89e0a5..946c4beb 100644 --- a/packages/web/src/index.tsx +++ b/packages/web/src/index.tsx @@ -5,6 +5,7 @@ import IntlProvider from 'components/IntlProvider'; import ApolloProvider from 'components/ApolloProvider'; import SnackbarProvider from 'components/SnackbarProvider'; import { AuthenticationProvider } from 'contexts/Authentication'; +import { AutomatischInfoProvider } from 'contexts/AutomatischInfo'; import Router from 'components/Router'; import routes from 'routes'; import reportWebVitals from './reportWebVitals'; @@ -13,11 +14,13 @@ ReactDOM.render( - - - {routes} - - + + + + {routes} + + + , From dbeeb61cc5ffa3fa4c83835e1df5bb613933b834 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Mon, 6 Mar 2023 18:05:14 +0000 Subject: [PATCH 3/4] feat: add empty billing and usage page --- .../src/components/SettingsLayout/index.tsx | 30 +++++++++++++---- packages/web/src/config/urls.ts | 2 ++ packages/web/src/locales/en.json | 2 ++ .../BillingAndUsageSettings/index.ee.tsx | 33 +++++++++++++++++++ .../web/src/pages/ProfileSettings/index.tsx | 2 +- packages/web/src/settingsRoutes.tsx | 10 ++++++ 6 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 packages/web/src/pages/BillingAndUsageSettings/index.ee.tsx diff --git a/packages/web/src/components/SettingsLayout/index.tsx b/packages/web/src/components/SettingsLayout/index.tsx index 7e8f1be8..488349ea 100644 --- a/packages/web/src/components/SettingsLayout/index.tsx +++ b/packages/web/src/components/SettingsLayout/index.tsx @@ -5,8 +5,10 @@ import { useTheme } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import AccountCircleIcon from '@mui/icons-material/AccountCircle'; import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew'; +import PaymentIcon from '@mui/icons-material/Payment'; import * as URLS from 'config/urls'; +import useAutomatischInfo from 'hooks/useAutomatischInfo'; import AppBar from 'components/AppBar'; import Drawer from 'components/Drawer'; @@ -14,13 +16,25 @@ type SettingsLayoutProps = { children: React.ReactNode; }; -const drawerLinks = [ - { - Icon: AccountCircleIcon, - primary: 'settingsDrawer.myProfile', - to: URLS.SETTINGS_PROFILE, - }, -]; +function createDrawerLinks({ isCloud }: { isCloud: boolean }) { + const items = [ + { + Icon: AccountCircleIcon, + primary: 'settingsDrawer.myProfile', + to: URLS.SETTINGS_PROFILE, + } + ] + + if (isCloud) { + items.push({ + Icon: PaymentIcon, + primary: 'settingsDrawer.billingAndUsage', + to: URLS.SETTINGS_BILLING_AND_USAGE, + }); + } + + return items; +} const drawerBottomLinks = [ { @@ -33,6 +47,7 @@ const drawerBottomLinks = [ export default function SettingsLayout({ children, }: SettingsLayoutProps): React.ReactElement { + const { isCloud } = useAutomatischInfo(); const theme = useTheme(); const matchSmallScreens = useMediaQuery(theme.breakpoints.down('lg'), { noSsr: true, @@ -41,6 +56,7 @@ export default function SettingsLayout({ const openDrawer = () => setDrawerOpen(true); const closeDrawer = () => setDrawerOpen(false); + const drawerLinks = createDrawerLinks({ isCloud }); return ( <> diff --git a/packages/web/src/config/urls.ts b/packages/web/src/config/urls.ts index 5500a6ab..07f3c448 100644 --- a/packages/web/src/config/urls.ts +++ b/packages/web/src/config/urls.ts @@ -64,8 +64,10 @@ export const FLOW_PATTERN = '/flows/:flowId'; export const SETTINGS = '/settings'; export const SETTINGS_DASHBOARD = SETTINGS; export const PROFILE = 'profile'; +export const BILLING_AND_USAGE = 'billing'; export const UPDATES = '/updates'; export const SETTINGS_PROFILE = `${SETTINGS}/${PROFILE}`; +export const SETTINGS_BILLING_AND_USAGE = `${SETTINGS}/${BILLING_AND_USAGE}`; export const DASHBOARD = FLOWS; diff --git a/packages/web/src/locales/en.json b/packages/web/src/locales/en.json index 87192f13..fbc85e78 100644 --- a/packages/web/src/locales/en.json +++ b/packages/web/src/locales/en.json @@ -11,6 +11,7 @@ "settingsDrawer.myProfile": "My Profile", "settingsDrawer.goBack": "Go to the dashboard", "settingsDrawer.notifications": "Notifications", + "settingsDrawer.billingAndUsage": "Billing and usage", "app.connectionCount": "{count} connections", "app.flowCount": "{count} flows", "app.addConnection": "Add connection", @@ -96,6 +97,7 @@ "profileSettings.deleteAccountResult2": "All your flows", "profileSettings.deleteAccountResult3": "All your connections", "profileSettings.deleteAccountResult4": "All execution history", + "billingAndUsageSettings.title": "Billing and usage", "deleteAccountDialog.title": "Delete account?", "deleteAccountDialog.description": "This will permanently delete your account and all the associated data with it.", "deleteAccountDialog.cancel": "Cancel?", diff --git a/packages/web/src/pages/BillingAndUsageSettings/index.ee.tsx b/packages/web/src/pages/BillingAndUsageSettings/index.ee.tsx new file mode 100644 index 00000000..c9fd31f1 --- /dev/null +++ b/packages/web/src/pages/BillingAndUsageSettings/index.ee.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { Navigate } from 'react-router-dom'; +import Grid from '@mui/material/Grid'; + +import * as URLS from 'config/urls' +import PageTitle from 'components/PageTitle'; +import Container from 'components/Container'; +import useFormatMessage from 'hooks/useFormatMessage'; +import useCloud from 'hooks/useCloud'; + +function BillingAndUsageSettings() { + const isCloud = useCloud(); + const formatMessage = useFormatMessage(); + + if (!isCloud) { + return () + } + + return ( + + + + {formatMessage('billingAndUsageSettings.title')} + + + + + + + ); +} + +export default BillingAndUsageSettings; diff --git a/packages/web/src/pages/ProfileSettings/index.tsx b/packages/web/src/pages/ProfileSettings/index.tsx index 7ec20ca5..f13968f1 100644 --- a/packages/web/src/pages/ProfileSettings/index.tsx +++ b/packages/web/src/pages/ProfileSettings/index.tsx @@ -101,7 +101,7 @@ function ProfileSettings() { + + + + } + /> + } From 5e7b4bfe45d5190c5d76974b795911da502ebc01 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Mon, 6 Mar 2023 19:42:41 +0000 Subject: [PATCH 4/4] feat: show payment portal information --- .../PaymentInformation/index.ee.tsx | 28 +++++++++++++++++++ packages/web/src/contexts/AutomatischInfo.tsx | 7 +++-- .../queries/get-payment-portal-url.ee.ts | 10 +++++++ .../web/src/helpers/translation-values.tsx | 6 ++-- .../web/src/hooks/usePaymentPortalUrl.ee.ts | 16 +++++++++++ packages/web/src/locales/en.json | 2 ++ .../BillingAndUsageSettings/index.ee.tsx | 11 ++++++-- 7 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 packages/web/src/components/PaymentInformation/index.ee.tsx create mode 100644 packages/web/src/graphql/queries/get-payment-portal-url.ee.ts create mode 100644 packages/web/src/hooks/usePaymentPortalUrl.ee.ts diff --git a/packages/web/src/components/PaymentInformation/index.ee.tsx b/packages/web/src/components/PaymentInformation/index.ee.tsx new file mode 100644 index 00000000..e0f8e22e --- /dev/null +++ b/packages/web/src/components/PaymentInformation/index.ee.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; +import Typography from '@mui/material/Typography'; + +import PageTitle from 'components/PageTitle'; +import { generateExternalLink } from 'helpers/translation-values'; +import usePaymentPortalUrl from 'hooks/usePaymentPortalUrl.ee'; +import useFormatMessage from 'hooks/useFormatMessage'; + +export default function PaymentInformation() { + const paymentPortal = usePaymentPortalUrl(); + const formatMessage = useFormatMessage(); + + return ( + + + {formatMessage('billingAndUsageSettings.paymentInformation')} + + + + {formatMessage( + 'billingAndUsageSettings.paymentPortalInformation', + { link: generateExternalLink(paymentPortal.url) })} + + + ); +} diff --git a/packages/web/src/contexts/AutomatischInfo.tsx b/packages/web/src/contexts/AutomatischInfo.tsx index 9223ffa3..d6647635 100644 --- a/packages/web/src/contexts/AutomatischInfo.tsx +++ b/packages/web/src/contexts/AutomatischInfo.tsx @@ -20,15 +20,16 @@ export const AutomatischInfoProvider = ( props: AutomatischInfoProviderProps ): React.ReactElement => { const { children } = props; - const { data } = useQuery(GET_AUTOMATISCH_INFO); + const { data, loading } = useQuery(GET_AUTOMATISCH_INFO); - const isCloud = data?.getAutomatischInfo?.isCloud || false; + const isCloud = data?.getAutomatischInfo?.isCloud; const value = React.useMemo(() => { return { isCloud, + loading }; - }, [isCloud]); + }, [isCloud, loading]); return ( diff --git a/packages/web/src/graphql/queries/get-payment-portal-url.ee.ts b/packages/web/src/graphql/queries/get-payment-portal-url.ee.ts new file mode 100644 index 00000000..9d7ec0da --- /dev/null +++ b/packages/web/src/graphql/queries/get-payment-portal-url.ee.ts @@ -0,0 +1,10 @@ +import { gql } from '@apollo/client'; + +export const GET_PAYMENT_PORTAL_URL = gql` + query GetPaymentPortalUrl { + getPaymentPortalUrl { + url + } + } +`; + diff --git a/packages/web/src/helpers/translation-values.tsx b/packages/web/src/helpers/translation-values.tsx index 86e84ce5..1d2925ff 100644 --- a/packages/web/src/helpers/translation-values.tsx +++ b/packages/web/src/helpers/translation-values.tsx @@ -1,6 +1,8 @@ +import Link from '@mui/material/Link'; + export const generateExternalLink = (link: string) => (str: string) => ( - + {str} - + ); diff --git a/packages/web/src/hooks/usePaymentPortalUrl.ee.ts b/packages/web/src/hooks/usePaymentPortalUrl.ee.ts new file mode 100644 index 00000000..323cdedd --- /dev/null +++ b/packages/web/src/hooks/usePaymentPortalUrl.ee.ts @@ -0,0 +1,16 @@ +import { useQuery } from '@apollo/client'; +import { GET_PAYMENT_PORTAL_URL } from 'graphql/queries/get-payment-portal-url.ee'; + +type UsePaymentPortalUrlReturn = { + url: string; + loading: boolean; +}; + +export default function usePaymentPortalUrl(): UsePaymentPortalUrlReturn { + const { data, loading } = useQuery(GET_PAYMENT_PORTAL_URL); + + return { + url: data?.getPaymentPortalUrl?.url, + loading + }; +} diff --git a/packages/web/src/locales/en.json b/packages/web/src/locales/en.json index fbc85e78..2612facf 100644 --- a/packages/web/src/locales/en.json +++ b/packages/web/src/locales/en.json @@ -98,6 +98,8 @@ "profileSettings.deleteAccountResult3": "All your connections", "profileSettings.deleteAccountResult4": "All execution history", "billingAndUsageSettings.title": "Billing and usage", + "billingAndUsageSettings.paymentInformation": "Payment information", + "billingAndUsageSettings.paymentPortalInformation": "To manage your subscription, click here to go to the payment portal.", "deleteAccountDialog.title": "Delete account?", "deleteAccountDialog.description": "This will permanently delete your account and all the associated data with it.", "deleteAccountDialog.cancel": "Cancel?", diff --git a/packages/web/src/pages/BillingAndUsageSettings/index.ee.tsx b/packages/web/src/pages/BillingAndUsageSettings/index.ee.tsx index c9fd31f1..0386fc68 100644 --- a/packages/web/src/pages/BillingAndUsageSettings/index.ee.tsx +++ b/packages/web/src/pages/BillingAndUsageSettings/index.ee.tsx @@ -3,6 +3,7 @@ import { Navigate } from 'react-router-dom'; import Grid from '@mui/material/Grid'; import * as URLS from 'config/urls' +import PaymentInformation from 'components/PaymentInformation/index.ee'; import PageTitle from 'components/PageTitle'; import Container from 'components/Container'; import useFormatMessage from 'hooks/useFormatMessage'; @@ -12,10 +13,15 @@ function BillingAndUsageSettings() { const isCloud = useCloud(); const formatMessage = useFormatMessage(); - if (!isCloud) { + // redirect to the initial settings page + if (isCloud === false) { return () } + // render nothing until we know if it's cloud or not + // here, `isCloud` is not `false`, but `undefined` + if (!isCloud) return + return ( @@ -23,7 +29,8 @@ function BillingAndUsageSettings() { {formatMessage('billingAndUsageSettings.title')} - + +