| @@ -214,6 +214,7 @@ function CreateApp({ onClose, onSuccess, onCreateFromTemplate }: CreateAppProps) | |||
| /> | |||
| </div> | |||
| </div> | |||
| {isAppsFull && <AppsFull className='mt-4' loc='app-create' />} | |||
| <div className='flex items-center justify-between pb-10 pt-5'> | |||
| <div className='system-xs-regular flex cursor-pointer items-center gap-1 text-text-tertiary' onClick={onCreateFromTemplate}> | |||
| <span>{t('app.newApp.noIdeaTip')}</span> | |||
| @@ -251,13 +252,6 @@ function CreateApp({ onClose, onSuccess, onCreateFromTemplate }: CreateAppProps) | |||
| </div> | |||
| </div> | |||
| </div> | |||
| { | |||
| isAppsFull && ( | |||
| <div className='px-8 py-2'> | |||
| <AppsFull loc='app-create' /> | |||
| </div> | |||
| ) | |||
| } | |||
| </> | |||
| } | |||
| type CreateAppDialogProps = CreateAppProps & { | |||
| @@ -96,7 +96,7 @@ const DuplicateAppModal = ({ | |||
| className='h-10' | |||
| /> | |||
| </div> | |||
| {isAppsFull && <AppsFull loc='app-duplicate-create' />} | |||
| {isAppsFull && <AppsFull className='mt-4' loc='app-duplicate-create' />} | |||
| </div> | |||
| <div className='flex flex-row-reverse'> | |||
| <Button disabled={isAppsFull} className='ml-2 w-24' variant='primary' onClick={submit}>{t('app.duplicate')}</Button> | |||
| @@ -3,35 +3,82 @@ import type { FC } from 'react' | |||
| import React from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import UpgradeBtn from '../upgrade-btn' | |||
| import AppsInfo from '../usage-info/apps-info' | |||
| import ProgressBar from '@/app/components/billing/progress-bar' | |||
| import Button from '@/app/components/base/button' | |||
| import { mailToSupport } from '@/app/components/header/utils/util' | |||
| import { useProviderContext } from '@/context/provider-context' | |||
| import { useAppContext } from '@/context/app-context' | |||
| import { Plan } from '@/app/components/billing/type' | |||
| import s from './style.module.css' | |||
| import cn from '@/utils/classnames' | |||
| import GridMask from '@/app/components/base/grid-mask' | |||
| const AppsFull: FC<{ loc: string; className?: string }> = ({ | |||
| const LOW = 50 | |||
| const MIDDLE = 80 | |||
| const AppsFull: FC<{ loc: string; className?: string; }> = ({ | |||
| loc, | |||
| className, | |||
| }) => { | |||
| const { t } = useTranslation() | |||
| const { plan } = useProviderContext() | |||
| const { userProfile, langeniusVersionInfo } = useAppContext() | |||
| const isTeam = plan.type === Plan.team | |||
| const usage = plan.usage.buildApps | |||
| const total = plan.total.buildApps | |||
| const percent = usage / total * 100 | |||
| const color = (() => { | |||
| if (percent < LOW) | |||
| return 'bg-components-progress-bar-progress-solid' | |||
| if (percent < MIDDLE) | |||
| return 'bg-components-progress-warning-progress' | |||
| return 'bg-components-progress-error-progress' | |||
| })() | |||
| return ( | |||
| <GridMask wrapperClassName='rounded-lg' canvasClassName='rounded-lg' gradientClassName='rounded-lg'> | |||
| <div className={cn( | |||
| 'mt-6 flex cursor-pointer flex-col rounded-lg border-2 border-solid border-transparent px-3.5 py-4 shadow-md transition-all duration-200 ease-in-out', | |||
| className, | |||
| )}> | |||
| <div className='flex items-center justify-between'> | |||
| <div className={cn(s.textGradient, 'text-base font-semibold leading-[24px]')}> | |||
| <div>{t('billing.apps.fullTipLine1')}</div> | |||
| <div>{t('billing.apps.fullTipLine2')}</div> | |||
| <div className={cn( | |||
| 'flex flex-col gap-3 rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg p-4 shadow-xs backdrop-blur-sm', | |||
| className, | |||
| )}> | |||
| <div className='flex justify-between'> | |||
| {!isTeam && ( | |||
| <div> | |||
| <div className={cn('title-xl-semi-bold mb-1', s.textGradient)}> | |||
| {t('billing.apps.fullTip1')} | |||
| </div> | |||
| <div className='system-xs-regular text-text-tertiary'>{t('billing.apps.fullTip1des')}</div> | |||
| </div> | |||
| <div className='flex'> | |||
| <UpgradeBtn loc={loc} /> | |||
| )} | |||
| {isTeam && ( | |||
| <div> | |||
| <div className={cn('title-xl-semi-bold mb-1', s.textGradient)}> | |||
| {t('billing.apps.fullTip2')} | |||
| </div> | |||
| <div className='system-xs-regular text-text-tertiary'>{t('billing.apps.fullTip2des')}</div> | |||
| </div> | |||
| )} | |||
| {(plan.type === Plan.sandbox || plan.type === Plan.professional) && ( | |||
| <UpgradeBtn isShort loc={loc} /> | |||
| )} | |||
| {plan.type !== Plan.sandbox && plan.type !== Plan.professional && ( | |||
| <Button variant='secondary-accent'> | |||
| <a target='_blank' rel='noopener noreferrer' href={mailToSupport(userProfile.email, plan.type, langeniusVersionInfo.current_version)}> | |||
| {t('billing.apps.contactUs')} | |||
| </a> | |||
| </Button> | |||
| )} | |||
| </div> | |||
| <div className='flex flex-col gap-2'> | |||
| <div className='system-xs-medium flex items-center justify-between text-text-secondary'> | |||
| <div>{t('billing.usagePage.buildApps')}</div> | |||
| <div>{usage}/{total}</div> | |||
| </div> | |||
| <AppsInfo className='mt-4' /> | |||
| <ProgressBar | |||
| percent={percent} | |||
| color={color} | |||
| /> | |||
| </div> | |||
| </GridMask> | |||
| </div> | |||
| ) | |||
| } | |||
| export default React.memo(AppsFull) | |||
| @@ -1,5 +1,5 @@ | |||
| .textGradient { | |||
| background: linear-gradient(92deg, #2250F2 -29.55%, #0EBCF3 75.22%); | |||
| background: linear-gradient(92deg, #0EBCF3 -29.55%, #2250F2 75.22%); | |||
| -webkit-background-clip: text; | |||
| -webkit-text-fill-color: transparent; | |||
| background-clip: text; | |||
| @@ -1,27 +0,0 @@ | |||
| 'use client' | |||
| import type { FC } from 'react' | |||
| import React from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import UpgradeBtn from '../upgrade-btn' | |||
| import s from './style.module.css' | |||
| import cn from '@/utils/classnames' | |||
| import GridMask from '@/app/components/base/grid-mask' | |||
| const AppsFull: FC = () => { | |||
| const { t } = useTranslation() | |||
| return ( | |||
| <GridMask wrapperClassName='rounded-lg' canvasClassName='rounded-lg' gradientClassName='rounded-lg'> | |||
| <div className='col-span-1 flex min-h-[160px] cursor-pointer flex-col rounded-lg border-2 border-solid border-transparent px-3.5 pt-3.5 shadow-xs transition-all duration-200 ease-in-out hover:shadow-lg'> | |||
| <div className={cn(s.textGradient, 'text-base font-semibold leading-[24px]')}> | |||
| <div>{t('billing.apps.fullTipLine1')}</div> | |||
| <div>{t('billing.apps.fullTipLine2')}</div> | |||
| </div> | |||
| <div className='mt-8 flex'> | |||
| <UpgradeBtn loc='app-create' /> | |||
| </div> | |||
| </div> | |||
| </GridMask> | |||
| ) | |||
| } | |||
| export default React.memo(AppsFull) | |||
| @@ -1,7 +0,0 @@ | |||
| .textGradient { | |||
| background: linear-gradient(92deg, #2250F2 -29.55%, #0EBCF3 75.22%); | |||
| -webkit-background-clip: text; | |||
| -webkit-text-fill-color: transparent; | |||
| background-clip: text; | |||
| text-fill-color: transparent; | |||
| } | |||
| @@ -142,7 +142,7 @@ const CreateAppModal = ({ | |||
| <p className='body-xs-regular text-text-tertiary'>{t('app.answerIcon.descriptionInExplore')}</p> | |||
| </div> | |||
| )} | |||
| {!isEditModal && isAppsFull && <AppsFull loc='app-explore-create' />} | |||
| {!isEditModal && isAppsFull && <AppsFull className='mt-4' loc='app-explore-create' />} | |||
| </div> | |||
| <div className='flex flex-row-reverse'> | |||
| <Button disabled={!isEditModal && isAppsFull} className='ml-2 w-24' variant='primary' onClick={submit}>{!isEditModal ? t('common.operation.create') : t('common.operation.save')}</Button> | |||
| @@ -170,8 +170,11 @@ const translation = { | |||
| fullSolution: 'Upgrade your plan to get more space.', | |||
| }, | |||
| apps: { | |||
| fullTipLine1: 'Upgrade your plan to', | |||
| fullTipLine2: 'build more apps.', | |||
| fullTip1: 'Upgrade to create more apps', | |||
| fullTip1des: 'You\'ve reached the limit of build apps on this plan', | |||
| fullTip2: 'Plan limit reached', | |||
| fullTip2des: 'It is recommended to clean up inactive applications to free up usage, or contact us.', | |||
| contactUs: 'Contact us', | |||
| }, | |||
| annotatedResponse: { | |||
| fullTipLine1: 'Upgrade your plan to', | |||
| @@ -169,8 +169,11 @@ const translation = { | |||
| fullSolution: '升级您的套餐以获得更多空间。', | |||
| }, | |||
| apps: { | |||
| fullTipLine1: '升级您的套餐以', | |||
| fullTipLine2: '构建更多的程序。', | |||
| fullTip1: '升级以创建更多应用', | |||
| fullTip1des: '您已达到此计划上构建应用的限制', | |||
| fullTip2: '计划限制已达到', | |||
| fullTip2des: '推荐您清理不活跃的应用或者联系我们', | |||
| contactUs: '联系我们', | |||
| }, | |||
| annotatedResponse: { | |||
| fullTipLine1: '升级您的套餐以', | |||