'use client' import type { FC, ReactNode } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' import { RiApps2Line, RiBook2Line, RiBrain2Line, RiChatAiLine, RiFileEditLine, RiFolder6Line, RiGroupLine, RiHardDrive3Line, RiHistoryLine, RiProgress3Line, RiQuestionLine, RiSeoLine } from '@remixicon/react' import { Plan } from '../type' import { ALL_PLANS, NUM_INFINITE } from '../config' import Toast from '../../base/toast' import Tooltip from '../../base/tooltip' import Divider from '../../base/divider' import { ArCube1, Group2, Keyframe, SparklesSoft } from '../../base/icons/src/public/billing' import { PlanRange } from './select-plan-range' import cn from '@/utils/classnames' import { useAppContext } from '@/context/app-context' import { fetchSubscriptionUrls } from '@/service/billing' type Props = { currentPlan: Plan plan: Plan planRange: PlanRange canPay: boolean } const KeyValue = ({ icon, label, tooltip }: { icon: ReactNode; label: string; tooltip?: ReactNode }) => { return (
{icon}
{label}
{tooltip && (
)}
) } const priceClassName = 'leading-[125%] text-[28px] font-bold text-text-primary' const style = { [Plan.sandbox]: { icon: , description: 'text-util-colors-gray-gray-600', btnStyle: 'bg-components-button-secondary-bg hover:bg-components-button-secondary-bg-hover border-[0.5px] border-components-button-secondary-border text-text-primary', btnDisabledStyle: 'bg-components-button-secondary-bg-disabled hover:bg-components-button-secondary-bg-disabled border-components-button-secondary-border-disabled text-components-button-secondary-text-disabled', }, [Plan.professional]: { icon: , description: 'text-util-colors-blue-brand-blue-brand-600', btnStyle: 'bg-components-button-primary-bg hover:bg-components-button-primary-bg-hover border border-components-button-primary-border text-components-button-primary-text', btnDisabledStyle: 'bg-components-button-primary-bg-disabled hover:bg-components-button-primary-bg-disabled border-components-button-primary-border-disabled text-components-button-primary-text-disabled', }, [Plan.team]: { icon: , description: 'text-util-colors-indigo-indigo-600', btnStyle: 'bg-components-button-indigo-bg hover:bg-components-button-indigo-bg-hover border border-components-button-primary-border text-components-button-primary-text', btnDisabledStyle: 'bg-components-button-indigo-bg-disabled hover:bg-components-button-indigo-bg-disabled border-components-button-indigo-border-disabled text-components-button-primary-text-disabled', }, } const PlanItem: FC = ({ plan, currentPlan, planRange, }) => { const { t } = useTranslation() const [loading, setLoading] = React.useState(false) const i18nPrefix = `billing.plans.${plan}` const isFreePlan = plan === Plan.sandbox const isMostPopularPlan = plan === Plan.professional const planInfo = ALL_PLANS[plan] const isYear = planRange === PlanRange.yearly const isCurrent = plan === currentPlan const isPlanDisabled = planInfo.level <= ALL_PLANS[currentPlan].level const { isCurrentWorkspaceManager } = useAppContext() const btnText = (() => { if (isCurrent) return t('billing.plansCommon.currentPlan') return ({ [Plan.sandbox]: t('billing.plansCommon.startForFree'), [Plan.professional]: t('billing.plansCommon.getStarted'), [Plan.team]: t('billing.plansCommon.getStarted'), })[plan] })() const handleGetPayUrl = async () => { if (loading) return if (isPlanDisabled) return if (isFreePlan) return // Only workspace manager can buy plan if (!isCurrentWorkspaceManager) { Toast.notify({ type: 'error', message: t('billing.buyPermissionDeniedTip'), className: 'z-[1001]', }) return } setLoading(true) try { const res = await fetchSubscriptionUrls(plan, isYear ? 'year' : 'month') // Adb Block additional tracking block the gtag, so we need to redirect directly window.location.href = res.url } finally { setLoading(false) } } return (
{style[plan].icon}
{t(`${i18nPrefix}.name`)}
{isMostPopularPlan &&
{t('billing.plansCommon.mostPopular')}
}
{t(`${i18nPrefix}.description`)}
{/* Price */} {isFreePlan && (
{t('billing.plansCommon.free')}
)} {!isFreePlan && (
${isYear ? planInfo.price * 10 : planInfo.price}
{isYear &&
{t('billing.plansCommon.save')}${planInfo.price * 2}
}
{t('billing.plansCommon.priceTip')} {t(`billing.plansCommon.${!isYear ? 'month' : 'year'}`)}
)}
{btnText}
} label={isFreePlan ? t('billing.plansCommon.messageRequest.title', { count: planInfo.messageRequest }) : t('billing.plansCommon.messageRequest.titlePerMonth', { count: planInfo.messageRequest })} tooltip={t('billing.plansCommon.messageRequest.tooltip') as string} /> } label={t('billing.plansCommon.modelProviders')} /> } label={t('billing.plansCommon.teamWorkspace', { count: planInfo.teamWorkspace })} /> } label={t('billing.plansCommon.teamMember', { count: planInfo.teamMembers })} /> } label={t('billing.plansCommon.buildApps', { count: planInfo.buildApps })} /> } label={t('billing.plansCommon.documents', { count: planInfo.documents })} tooltip={t('billing.plansCommon.documentsTooltip') as string} /> } label={t('billing.plansCommon.vectorSpace', { size: planInfo.vectorSpace })} tooltip={t('billing.plansCommon.vectorSpaceTooltip') as string} /> } label={t('billing.plansCommon.documentsRequestQuota', { count: planInfo.documentsRequestQuota })} tooltip={t('billing.plansCommon.documentsRequestQuotaTooltip')} /> } label={[t(`billing.plansCommon.priority.${planInfo.documentProcessingPriority}`), t('billing.plansCommon.documentProcessingPriority')].join('')} /> } label={t('billing.plansCommon.annotatedResponse.title', { count: planInfo.annotatedResponse })} tooltip={t('billing.plansCommon.annotatedResponse.tooltip') as string} /> } label={t('billing.plansCommon.logsHistory', { days: planInfo.logHistory === NUM_INFINITE ? t('billing.plansCommon.unlimited') as string : `${planInfo.logHistory} ${t('billing.plansCommon.days')}` })} />
) } export default React.memo(PlanItem)