first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View 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
)
}
/>
);
}

View 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>
);
}

View 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." />
}
/>
);
}

View 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." />;
}

View File

@@ -0,0 +1,4 @@
export type PolicyFailReason =
| 'overQuota'
| 'noPermission'
| 'noWorkspacePermission';

View 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>
);
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB