1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-20 00:08:02 +01:00

Refactor premium feature (#2627)

https://linear.app/unleash/issue/2-491/improve-premiumfeature-component-and-how-its-implemented


![image](https://user-images.githubusercontent.com/14320932/206237837-6032c3c0-1e42-4be2-8b6f-223e0204e1b5.png)

Refactors `PremiumFeature` to be a bit more straightforward and match
new designs, both in standalone and tooltip modes.
This also fixes the fact that the change requests tab did not display
the premium feature info, along with other smaller fixes.

Co-authored-by: Christopher Kolstad <chriswk@getunleash.ai>
This commit is contained in:
Nuno Góis 2022-12-08 11:42:54 +00:00 committed by GitHub
parent fb2c30244c
commit 1be2483e6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 151 additions and 120 deletions

View File

@ -271,7 +271,7 @@ export const ChangeRequestsTabs = ({
}
elseShow={
<TablePlaceholder>
None of the changes where submitted yet.
None of the changes were submitted yet.
</TablePlaceholder>
}
/>

View File

@ -5,12 +5,17 @@ import { useProjectNameOrId } from 'hooks/api/getters/useProject/useProject';
import { ChangeRequestsTabs } from './ChangeRequestsTabs/ChangeRequestsTabs';
import { SortingRule } from 'react-table';
import { useProjectChangeRequests } from 'hooks/api/getters/useProjectChangeRequests/useProjectChangeRequests';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature';
const defaultSort: SortingRule<string> = { id: 'updatedAt', desc: true };
export const ProjectChangeRequests = () => {
const projectId = useRequiredPathParam('projectId');
const projectName = useProjectNameOrId(projectId);
const { isOss, isPro } = useUiConfig();
usePageTitle(`Change requests ${projectName}`);
@ -21,6 +26,14 @@ export const ProjectChangeRequests = () => {
defaultSort
);
if (isOss() || isPro()) {
return (
<PageContent sx={{ justifyContent: 'center' }}>
<PremiumFeature feature="Change Requests" />
</PageContent>
);
}
return (
<ChangeRequestsTabs
changeRequests={changeRequests}

View File

@ -1,86 +1,152 @@
import { ReactComponent as ProPlanIcon } from 'assets/icons/pro-enterprise-feature-badge.svg';
import { Box, Link, styled, Typography } from '@mui/material';
import { Box, Button, Link, styled, Typography } from '@mui/material';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
const PremiumFeatureWrapper = styled(Box)(({ theme }) => ({
const PremiumFeatureWrapper = styled(Box, {
shouldForwardProp: prop => prop !== 'tooltip',
})<{ tooltip?: boolean }>(({ theme, tooltip }) => ({
display: 'flex',
flexDirection: 'column',
padding: theme.spacing(1, 0.5),
alignItems: tooltip ? 'start' : 'center',
textAlign: tooltip ? 'left' : 'center',
backgroundColor: tooltip ? 'transparent' : theme.palette.secondaryContainer,
borderRadius: tooltip ? 0 : theme.shape.borderRadiusLarge,
padding: tooltip ? theme.spacing(1, 0.5) : theme.spacing(7.5, 1),
}));
const StyledTitle = styled(Typography)(({ theme }) => ({
display: 'inline-flex',
alignItems: 'center',
fontWeight: theme.fontWeight.bold,
fontSize: theme.fontSizes.smallBody,
gap: theme.spacing(1),
}));
const StyledBody = styled(Typography)(({ theme }) => ({
const StyledBody = styled('div', {
shouldForwardProp: prop => prop !== 'tooltip',
})<{ tooltip?: boolean }>(({ theme, tooltip }) => ({
margin: tooltip ? theme.spacing(1, 0) : theme.spacing(3, 0, 5, 0),
}));
const StyledTypography = styled(Typography)(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
margin: theme.spacing(1, 0),
}));
const StyledButtonContainer = styled('div')(() => ({
display: 'flex',
}));
const StyledLink = styled(Link)(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
width: 'fit-content',
}));
enum FeatureLevelTitle {
PRO = 'Pro & Enterprise feature',
ENTERPRISE = 'Enterprise feature',
enum FeaturePlan {
PRO = 'Pro & Enterprise',
ENTERPRISE = 'Enterprise',
}
export enum PlausibleOrigin {
PROJECT = 'Projects',
ACCESS = 'Access',
CHANGE_REQUEST = 'Change Request',
}
const PremiumFeatures = {
['Adding new projects']: {
plan: FeaturePlan.PRO,
url: '',
},
['Access']: {
plan: FeaturePlan.PRO,
url: 'https://docs.getunleash.io/reference/rbac',
},
['Change Requests']: {
plan: FeaturePlan.ENTERPRISE,
url: 'https://docs.getunleash.io/reference/change-requests',
},
};
type PremiumFeature = keyof typeof PremiumFeatures;
const UPGRADE_URL = 'https://www.getunleash.io/plans';
export interface PremiumFeatureProps {
children: React.ReactNode;
origin?: PlausibleOrigin;
center?: boolean;
enterpriseOnly?: boolean;
feature: PremiumFeature;
tooltip?: boolean;
}
export const PremiumFeature = ({
children,
origin,
center,
enterpriseOnly = false,
}: PremiumFeatureProps) => {
export const PremiumFeature = ({ feature, tooltip }: PremiumFeatureProps) => {
const { url, plan } = PremiumFeatures[feature];
const tracker = usePlausibleTracker();
const handleClick = () => {
if (origin) {
tracker.trackEvent('upgrade_plan_clicked', {
props: { origin },
});
}
tracker.trackEvent('upgrade_plan_clicked', {
props: { feature },
});
};
const featureLabel = Boolean(url) ? (
<StyledLink href={url} target="_blank">
{feature}
</StyledLink>
) : (
feature
);
const featureMessage = (
<>
{featureLabel} is a feature available for the{' '}
<strong>{plan}</strong>{' '}
{plan === FeaturePlan.PRO ? 'plans' : 'plan'}
</>
);
return (
<PremiumFeatureWrapper
sx={{
alignItems: center ? 'center' : 'start',
textAlign: center ? 'center' : 'left',
}}
>
<PremiumFeatureWrapper tooltip={tooltip}>
<StyledTitle>
<ProPlanIcon />
{enterpriseOnly
? FeatureLevelTitle.ENTERPRISE
: FeatureLevelTitle.PRO}
{`${plan} feature`}
</StyledTitle>
<StyledBody>{children}</StyledBody>
<StyledLink
href={'https://www.getunleash.io/plans'}
target="_blank"
onClick={handleClick}
>
Upgrade now
</StyledLink>
<ConditionallyRender
condition={Boolean(tooltip)}
show={
<>
<StyledBody tooltip>
<StyledTypography>
{featureMessage}. You need to upgrade your plan
if you want to use it
</StyledTypography>
</StyledBody>
<StyledButtonContainer>
<StyledLink
href={UPGRADE_URL}
target="_blank"
onClick={handleClick}
>
Upgrade now
</StyledLink>
</StyledButtonContainer>
</>
}
elseShow={
<>
<StyledBody>
<StyledTypography>
{featureMessage}
</StyledTypography>
<StyledTypography>
You need to upgrade your plan if you want to use
it
</StyledTypography>
</StyledBody>
<StyledButtonContainer>
<Button
variant="outlined"
href={UPGRADE_URL}
target="_blank"
onClick={handleClick}
>
Upgrade now
</Button>
</StyledButtonContainer>
</>
}
/>
</PremiumFeatureWrapper>
);
};

View File

@ -1,7 +1,7 @@
import { useContext } from 'react';
import { PageContent } from 'component/common/PageContent/PageContent';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { Alert, Link, styled } from '@mui/material';
import { Alert } from '@mui/material';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import AccessContext from 'contexts/AccessContext';
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
@ -9,15 +9,8 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { usePageTitle } from 'hooks/usePageTitle';
import { useProjectNameOrId } from 'hooks/api/getters/useProject/useProject';
import { ChangeRequestTable } from './ChangeRequestTable';
import {
PlausibleOrigin,
PremiumFeature,
} from 'component/common/PremiumFeature/PremiumFeature';
const StyledLink = styled(Link)(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
width: 'fit-content',
}));
import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature';
import { ChangeRequestProcessHelp } from './ChangeRequestProcessHelp/ChangeRequestProcessHelp';
export const ChangeRequestConfiguration = () => {
const projectId = useRequiredPathParam('projectId');
@ -25,37 +18,29 @@ export const ChangeRequestConfiguration = () => {
const { hasAccess } = useContext(AccessContext);
const { isOss, isPro } = useUiConfig();
usePageTitle(`Project change request ${projectName}`);
usePageTitle(`Project change request configuration ${projectName}`);
if (isOss() || isPro()) {
return (
<PageContent
header={<PageHeader title="Change request configuration" />}
header={
<PageHeader
titleElement="Change request configuration"
actions={<ChangeRequestProcessHelp />}
/>
}
sx={{ justifyContent: 'center' }}
>
<PremiumFeature
origin={PlausibleOrigin.CHANGE_REQUEST}
enterpriseOnly
center
>
<>
If you want to use{' '}
<StyledLink
href={'https://www.getunleash.io/plans'} // TODO: Add link to change request docs when available
target="_blank"
>
"Change Requests"
</StyledLink>{' '}
you will need to upgrade to Enterprise plan
</>
</PremiumFeature>
<PremiumFeature feature="Change Requests" />
</PageContent>
);
}
if (!hasAccess(UPDATE_PROJECT, projectId)) {
return (
<PageContent header={<PageHeader title="Project access" />}>
<PageContent
header={<PageHeader title="Change request configuration" />}
>
<Alert severity="error">
You need project owner permissions to access this section.
</Alert>

View File

@ -1,7 +1,7 @@
import { useContext } from 'react';
import { PageContent } from 'component/common/PageContent/PageContent';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { Alert, Box, Link, styled } from '@mui/material';
import { Alert } from '@mui/material';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import AccessContext from 'contexts/AccessContext';
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
@ -9,15 +9,7 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { usePageTitle } from 'hooks/usePageTitle';
import { ProjectAccessTable } from 'component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable';
import { useProjectNameOrId } from 'hooks/api/getters/useProject/useProject';
import {
PlausibleOrigin,
PremiumFeature,
} from 'component/common/PremiumFeature/PremiumFeature';
const StyledLink = styled(Link)(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
width: 'fit-content',
}));
import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature';
export const ProjectAccess = () => {
const projectId = useRequiredPathParam('projectId');
@ -28,37 +20,18 @@ export const ProjectAccess = () => {
if (isOss()) {
return (
<PageContent header={<PageHeader title="Project access" />}>
<Box
sx={{
display: 'inline-flex',
maxWidth: '50%',
margin: '0 25%',
}}
alignSelf={'center'}
>
<PremiumFeature origin={PlausibleOrigin.ACCESS} center>
<>
Controlling access to projects requires a paid
version of Unleash. Check out{' '}
<StyledLink
href="https://www.getunleash.io"
target="_blank"
rel="noreferrer"
>
getunleash.io
</StyledLink>{' '}
to find out more.
</>
</PremiumFeature>
</Box>
<PageContent
header={<PageHeader title="Access" />}
sx={{ justifyContent: 'center' }}
>
<PremiumFeature feature="Access" />
</PageContent>
);
}
if (!hasAccess(UPDATE_PROJECT, projectId)) {
return (
<PageContent header={<PageHeader title="Project access" />}>
<PageContent header={<PageHeader title="Access" />}>
<Alert severity="error">
You need project owner permissions to access this section.
</Alert>

View File

@ -20,10 +20,7 @@ import { TablePlaceholder } from 'component/common/Table';
import { useMediaQuery } from '@mui/material';
import theme from 'themes/theme';
import { Search } from 'component/common/Search/Search';
import {
PlausibleOrigin,
PremiumFeature,
} from 'component/common/PremiumFeature/PremiumFeature';
import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature';
import { ITooltipResolverProps } from 'component/common/TooltipResolver/TooltipResolver';
import { ReactComponent as ProPlanIcon } from 'assets/icons/pro-enterprise-feature-badge.svg';
@ -48,10 +45,7 @@ function resolveCreateButtonData(
disabled: true,
tooltip: {
titleComponent: (
<PremiumFeature origin={PlausibleOrigin.PROJECT}>
To be able to add more projects you need to upgrade to
Pro or Enterprise plan
</PremiumFeature>
<PremiumFeature feature="Adding new projects" tooltip />
),
sx: { maxWidth: '320px' },
variant: 'custom',