diff --git a/frontend/src/assets/icons/pro-enterprise-feature-badge.svg b/frontend/src/assets/icons/pro-enterprise-feature-badge.svg new file mode 100644 index 0000000000..c180559714 --- /dev/null +++ b/frontend/src/assets/icons/pro-enterprise-feature-badge.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/component/common/PermissionIconButton/PermissionIconButton.tsx b/frontend/src/component/common/PermissionIconButton/PermissionIconButton.tsx index aa3c44c3c0..bd8c4fcdb5 100644 --- a/frontend/src/component/common/PermissionIconButton/PermissionIconButton.tsx +++ b/frontend/src/component/common/PermissionIconButton/PermissionIconButton.tsx @@ -1,10 +1,10 @@ import { IconButton, IconButtonProps } from '@mui/material'; -import React, { useContext, ReactNode } from 'react'; +import React, { ReactNode, useContext } from 'react'; import AccessContext from 'contexts/AccessContext'; import { Link } from 'react-router-dom'; import { - TooltipResolver, ITooltipResolverProps, + TooltipResolver, } from 'component/common/TooltipResolver/TooltipResolver'; import { formatAccessText } from 'utils/formatAccessText'; import { useId } from 'hooks/useId'; diff --git a/frontend/src/component/common/ProFeatureTooltip/ProFeatureTooltip.tsx b/frontend/src/component/common/ProFeatureTooltip/ProFeatureTooltip.tsx new file mode 100644 index 0000000000..937501eefc --- /dev/null +++ b/frontend/src/component/common/ProFeatureTooltip/ProFeatureTooltip.tsx @@ -0,0 +1,42 @@ +import { ReactComponent as ProPlanIcon } from 'assets/icons/pro-enterprise-feature-badge.svg'; +import { Box, Link, styled, Typography } from '@mui/material'; + +export interface ProFeatureTooltipProps { + text: string; +} + +const ProFeatureTooltipWrapper = styled(Box)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + backgroundColor: theme.palette.background.paper, + padding: theme.spacing(1, 1.5), + borderRadius: theme.shape.borderRadius, + color: theme.palette.text.primary, + width: '100%', +})); + +const StyledTitle = styled(Typography)(({ theme }) => ({ + display: 'inline-flex', + justifyContent: 'flex-start', + flexDirection: 'row', + marginBottom: theme.spacing(1), +})); + +export const ProFeatureTooltip = ({ text }: ProFeatureTooltipProps) => { + return ( + + + + + Pro & Enterprise feature + + + {text} + + + Upgrade now + + + + ); +}; diff --git a/frontend/src/component/common/ResponsiveButton/ResponsiveButton.tsx b/frontend/src/component/common/ResponsiveButton/ResponsiveButton.tsx index 128c60ddaf..40923fa42f 100644 --- a/frontend/src/component/common/ResponsiveButton/ResponsiveButton.tsx +++ b/frontend/src/component/common/ResponsiveButton/ResponsiveButton.tsx @@ -1,11 +1,14 @@ +import React from 'react'; import { useMediaQuery } from '@mui/material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import PermissionButton from '../PermissionButton/PermissionButton'; import PermissionIconButton from '../PermissionIconButton/PermissionIconButton'; -import React from 'react'; +import { ITooltipResolverProps } from '../TooltipResolver/TooltipResolver'; interface IResponsiveButtonProps { Icon: React.ElementType; + endIcon?: React.ReactNode; + tooltipProps?: Omit; onClick: () => void; disabled?: boolean; permission: string; diff --git a/frontend/src/component/common/TooltipResolver/TooltipResolver.tsx b/frontend/src/component/common/TooltipResolver/TooltipResolver.tsx index 237bb45ee5..e07ff9e6ae 100644 --- a/frontend/src/component/common/TooltipResolver/TooltipResolver.tsx +++ b/frontend/src/component/common/TooltipResolver/TooltipResolver.tsx @@ -1,20 +1,34 @@ +import React, { ReactNode } from 'react'; import { Tooltip, TooltipProps } from '@mui/material'; +import { HtmlTooltip } from '../HtmlTooltip/HtmlTooltip'; export interface ITooltipResolverProps extends Omit { - title: string | undefined; + title?: string | undefined; + titleComponent?: ReactNode | undefined; + variant?: 'default' | 'white'; } export const TooltipResolver = ({ title, children, + variant = 'default', + titleComponent, ...rest }: ITooltipResolverProps) => { - if (!title) { + if (!title && !titleComponent) { return children; } + if (variant === 'white') { + return ( + + {children} + + ); + } + return ( - + {children} ); diff --git a/frontend/src/component/project/ProjectList/ProjectList.tsx b/frontend/src/component/project/ProjectList/ProjectList.tsx index 522ec04647..3be7399204 100644 --- a/frontend/src/component/project/ProjectList/ProjectList.tsx +++ b/frontend/src/component/project/ProjectList/ProjectList.tsx @@ -21,6 +21,9 @@ import { TablePlaceholder } from 'component/common/Table'; import { useMediaQuery } from '@mui/material'; import theme from 'themes/theme'; import { Search } from 'component/common/Search/Search'; +import { ProFeatureTooltip } from '../../common/ProFeatureTooltip/ProFeatureTooltip'; +import { ITooltipResolverProps } from '../../common/TooltipResolver/TooltipResolver'; +import { ReactComponent as ProPlanIcon } from 'assets/icons/pro-enterprise-feature-badge.svg'; type PageQueryType = Partial>; @@ -28,20 +31,39 @@ type projectMap = { [index: string]: boolean; }; -function resolveCreateButtonData(isOss: boolean, hasAccess: boolean) { +interface ICreateButtonData { + disabled: boolean; + tooltip?: Omit; +} + +function resolveCreateButtonData( + isOss: boolean, + hasAccess: boolean +): ICreateButtonData { if (isOss) { return { - title: 'You must be on a paid subscription to create new projects', disabled: true, + tooltip: { + titleComponent: ( + + ), + variant: 'white', + }, }; } else if (!hasAccess) { return { - title: 'You do not have permission to create new projects', + tooltip: { + title: 'You do not have permission to create new projects', + }, disabled: true, }; } else { return { - title: 'Click to create a new project', + tooltip: { title: 'Click to create a new project' }, disabled: false, }; } @@ -174,10 +196,12 @@ export const ProjectListNew = () => { /> } onClick={() => navigate('/projects/create')} maxWidth="700px" permission={CREATE_PROJECT} disabled={createButtonData.disabled} + tooltipProps={createButtonData.tooltip} > New project diff --git a/frontend/src/hooks/usePlausibleTracker.ts b/frontend/src/hooks/usePlausibleTracker.ts index 0dfe62a3fc..4bce09b4a3 100644 --- a/frontend/src/hooks/usePlausibleTracker.ts +++ b/frontend/src/hooks/usePlausibleTracker.ts @@ -8,7 +8,7 @@ import { EventOptions, PlausibleOptions } from 'plausible-tracker'; * @see https://plausible.io/docs/custom-event-goals#2-create-a-custom-event-goal-in-your-plausible-analytics-account * @example `'download | 'invite' | 'signup'` **/ -type CustomEvents = 'invite'; +type CustomEvents = 'invite' | 'upgrade_plan_clicked'; export const usePlausibleTracker = () => { const plausible = useContext(PlausibleContext);