25
common/resources/client/billing/upgrade/feature-locked-dialog.tsx
Executable file
25
common/resources/client/billing/upgrade/feature-locked-dialog.tsx
Executable file
@@ -0,0 +1,25 @@
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {ReactNode} from 'react';
|
||||
import {UpgradeDialog} from '@common/billing/upgrade/upgrade-dialog';
|
||||
|
||||
interface FeatureLockedDialogProps {
|
||||
message?: ReactNode;
|
||||
messageSuffix?: ReactNode;
|
||||
}
|
||||
export function FeatureLockedDialog({
|
||||
message,
|
||||
messageSuffix,
|
||||
}: FeatureLockedDialogProps) {
|
||||
return (
|
||||
<UpgradeDialog
|
||||
message={message}
|
||||
messageSuffix={
|
||||
messageSuffix === undefined ? (
|
||||
<Trans message="Upgrade to unlock this feature and many more." />
|
||||
) : (
|
||||
messageSuffix
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
63
common/resources/client/billing/upgrade/no-permission-button.tsx
Executable file
63
common/resources/client/billing/upgrade/no-permission-button.tsx
Executable file
@@ -0,0 +1,63 @@
|
||||
import {LockIcon} from '@common/icons/material/Lock';
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {DialogTrigger} from '@common/ui/overlays/dialog/dialog-trigger';
|
||||
import {ReactNode} from 'react';
|
||||
import {Tooltip} from '@common/ui/tooltip/tooltip';
|
||||
import {useSettings} from '@common/core/settings/use-settings';
|
||||
import {FeatureLockedDialog} from '@common/billing/upgrade/feature-locked-dialog';
|
||||
import clsx from 'clsx';
|
||||
import {IconButton} from '@common/ui/buttons/icon-button';
|
||||
import {Button} from '@common/ui/buttons/button';
|
||||
|
||||
interface UpgradeButtonProps {
|
||||
message?: ReactNode;
|
||||
className?: string;
|
||||
iconButton?: boolean;
|
||||
}
|
||||
export function NoPermissionButton({
|
||||
message,
|
||||
className,
|
||||
iconButton,
|
||||
}: UpgradeButtonProps) {
|
||||
const {billing} = useSettings();
|
||||
|
||||
if (!billing.enable) {
|
||||
return <GenericButton className={className} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<DialogTrigger type="popover" triggerOnHover>
|
||||
{iconButton ? (
|
||||
<IconButton className={className} color="primary" size="sm">
|
||||
<LockIcon />
|
||||
</IconButton>
|
||||
) : (
|
||||
<Button
|
||||
variant="flat"
|
||||
color="primary"
|
||||
size="2xs"
|
||||
startIcon={<LockIcon />}
|
||||
className={className}
|
||||
>
|
||||
<Trans message="Upgrade" />
|
||||
</Button>
|
||||
)}
|
||||
<FeatureLockedDialog message={message} />
|
||||
</DialogTrigger>
|
||||
);
|
||||
}
|
||||
|
||||
interface GenericButtonProps {
|
||||
className?: string;
|
||||
}
|
||||
function GenericButton({className}: GenericButtonProps) {
|
||||
return (
|
||||
<Tooltip
|
||||
label={
|
||||
<Trans message="You don't have permissions to access this feature." />
|
||||
}
|
||||
>
|
||||
<LockIcon size="sm" className={clsx('text-muted', className)} />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
24
common/resources/client/billing/upgrade/over-quota-dialog.tsx
Executable file
24
common/resources/client/billing/upgrade/over-quota-dialog.tsx
Executable file
@@ -0,0 +1,24 @@
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {MessageDescriptor} from '@common/i18n/message-descriptor';
|
||||
import {useTrans} from '@common/i18n/use-trans';
|
||||
import {UpgradeDialog} from '@common/billing/upgrade/upgrade-dialog';
|
||||
|
||||
interface FeatureLockedDialogProps {
|
||||
resourceName: MessageDescriptor;
|
||||
}
|
||||
export function OverQuotaDialog({resourceName}: FeatureLockedDialogProps) {
|
||||
const {trans} = useTrans();
|
||||
return (
|
||||
<UpgradeDialog
|
||||
message={
|
||||
<Trans
|
||||
message="You've reached the maximum number of :resource allowed for your current plan."
|
||||
values={{resource: trans(resourceName)}}
|
||||
/>
|
||||
}
|
||||
messageSuffix={
|
||||
<Trans message="Upgrade to increase this limit and unlock other features." />
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
84
common/resources/client/billing/upgrade/policy-fail-message.tsx
Executable file
84
common/resources/client/billing/upgrade/policy-fail-message.tsx
Executable file
@@ -0,0 +1,84 @@
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {Link} from 'react-router-dom';
|
||||
import {LinkStyle} from '@common/ui/buttons/external-link';
|
||||
import {ReactElement, ReactNode} from 'react';
|
||||
import {SectionHelper, SectionHelperProps} from '@common/ui/section-helper';
|
||||
import {useSettings} from '@common/core/settings/use-settings';
|
||||
import {PolicyFailReason} from '@common/billing/upgrade/policy-fail-reason';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
// plural name in lowercase (e.g. 'projects')
|
||||
resourceName?: ReactElement | string;
|
||||
reason?: PolicyFailReason;
|
||||
size?: SectionHelperProps['size'];
|
||||
color?: SectionHelperProps['color'];
|
||||
message?: ReactNode;
|
||||
}
|
||||
export function PolicyFailMessage({
|
||||
resourceName,
|
||||
className,
|
||||
size = 'md',
|
||||
color = 'bgAlt',
|
||||
reason = 'overQuota',
|
||||
...other
|
||||
}: Props) {
|
||||
const message = other.message ?? (
|
||||
<MessageText resourceName={resourceName!} reason={reason} />
|
||||
);
|
||||
|
||||
return (
|
||||
<SectionHelper
|
||||
color={color}
|
||||
size={size}
|
||||
className={className}
|
||||
description={message}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
interface MessageTextProps {
|
||||
resourceName: ReactElement | string;
|
||||
reason?: PolicyFailReason;
|
||||
}
|
||||
function MessageText({resourceName, reason}: MessageTextProps) {
|
||||
const {billing} = useSettings();
|
||||
|
||||
if (reason === 'noWorkspacePermission') {
|
||||
return (
|
||||
<Trans
|
||||
message="You can't create new :name in this workspace."
|
||||
values={{name: resourceName}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const upgradeMsgValues = {
|
||||
name: resourceName,
|
||||
a: (text: ReactNode) => (
|
||||
<Link className={LinkStyle} to="/pricing">
|
||||
{text}
|
||||
</Link>
|
||||
),
|
||||
};
|
||||
|
||||
if (reason === 'overQuota' && billing.enable) {
|
||||
return (
|
||||
<Trans
|
||||
message="Your plan is at its maximum number of :name allowed. <a>Upgrade to add more.</a>"
|
||||
values={upgradeMsgValues}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (reason === 'noPermission' && billing.enable) {
|
||||
return (
|
||||
<Trans
|
||||
message="To unlock ability to create :name. <a>Upgrade your plan.</a>"
|
||||
values={upgradeMsgValues}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <Trans message="You don't have permissions to create :name." />;
|
||||
}
|
||||
4
common/resources/client/billing/upgrade/policy-fail-reason.ts
Executable file
4
common/resources/client/billing/upgrade/policy-fail-reason.ts
Executable file
@@ -0,0 +1,4 @@
|
||||
export type PolicyFailReason =
|
||||
| 'overQuota'
|
||||
| 'noPermission'
|
||||
| 'noWorkspacePermission';
|
||||
58
common/resources/client/billing/upgrade/upgrade-dialog.tsx
Executable file
58
common/resources/client/billing/upgrade/upgrade-dialog.tsx
Executable file
@@ -0,0 +1,58 @@
|
||||
import {ReactNode} from 'react';
|
||||
import {useDialogContext} from '@common/ui/overlays/dialog/dialog-context';
|
||||
import {Dialog} from '@common/ui/overlays/dialog/dialog';
|
||||
import {DialogHeader} from '@common/ui/overlays/dialog/dialog-header';
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {DialogBody} from '@common/ui/overlays/dialog/dialog-body';
|
||||
import {SvgImage} from '@common/ui/images/svg-image/svg-image';
|
||||
import upgradeSvg from '@common/billing/upgrade/upgrade.svg';
|
||||
import {DialogFooter} from '@common/ui/overlays/dialog/dialog-footer';
|
||||
import {Button} from '@common/ui/buttons/button';
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
interface UpgradeDialogProps {
|
||||
message?: ReactNode;
|
||||
messageSuffix?: ReactNode;
|
||||
}
|
||||
export function UpgradeDialog({message, messageSuffix}: UpgradeDialogProps) {
|
||||
const {close} = useDialogContext();
|
||||
|
||||
return (
|
||||
<Dialog size="sm">
|
||||
<DialogHeader>
|
||||
<Trans message="Join the PROs" />
|
||||
</DialogHeader>
|
||||
<DialogBody>
|
||||
<div className="mb-20 text-center">
|
||||
<SvgImage src={upgradeSvg} className="mx-auto" height="h-100" />
|
||||
</div>
|
||||
<div>
|
||||
{message} {messageSuffix}
|
||||
</div>
|
||||
</DialogBody>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="text"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
close();
|
||||
}}
|
||||
>
|
||||
<Trans message="Maybe later" />
|
||||
</Button>
|
||||
<Button
|
||||
autoFocus
|
||||
variant="flat"
|
||||
size="xs"
|
||||
color="primary"
|
||||
elementType={Link}
|
||||
to="/pricing"
|
||||
target="_blank"
|
||||
onClick={() => close()}
|
||||
>
|
||||
<Trans message="Find out more" />
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
1
common/resources/client/billing/upgrade/upgrade.svg
Executable file
1
common/resources/client/billing/upgrade/upgrade.svg
Executable file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 26 KiB |
Reference in New Issue
Block a user