You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.tsx 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import { SelfHostedPlan } from '../../../type'
  6. import { contactSalesUrl, getStartedWithCommunityUrl, getWithPremiumUrl } from '../../../config'
  7. import Toast from '../../../../base/toast'
  8. import cn from '@/utils/classnames'
  9. import { useAppContext } from '@/context/app-context'
  10. import Button from './button'
  11. import List from './list'
  12. import { Azure, GoogleCloud } from '@/app/components/base/icons/src/public/billing'
  13. import { Community, Enterprise, EnterpriseNoise, Premium, PremiumNoise } from '../../assets'
  14. const STYLE_MAP = {
  15. [SelfHostedPlan.community]: {
  16. icon: <Community />,
  17. bg: '',
  18. noise: null,
  19. },
  20. [SelfHostedPlan.premium]: {
  21. icon: <Premium />,
  22. bg: 'bg-billing-plan-card-premium-bg opacity-10',
  23. noise: (
  24. <div className='absolute -top-12 left-0 right-0 -z-10'>
  25. <PremiumNoise />
  26. </div>
  27. ),
  28. },
  29. [SelfHostedPlan.enterprise]: {
  30. icon: <Enterprise />,
  31. bg: 'bg-billing-plan-card-enterprise-bg opacity-10',
  32. noise: (
  33. <div className='absolute -top-12 left-0 right-0 -z-10'>
  34. <EnterpriseNoise />
  35. </div>
  36. ),
  37. },
  38. }
  39. type SelfHostedPlanItemProps = {
  40. plan: SelfHostedPlan
  41. }
  42. const SelfHostedPlanItem: FC<SelfHostedPlanItemProps> = ({
  43. plan,
  44. }) => {
  45. const { t } = useTranslation()
  46. const i18nPrefix = `billing.plans.${plan}`
  47. const isFreePlan = plan === SelfHostedPlan.community
  48. const isPremiumPlan = plan === SelfHostedPlan.premium
  49. const isEnterprisePlan = plan === SelfHostedPlan.enterprise
  50. const { isCurrentWorkspaceManager } = useAppContext()
  51. const handleGetPayUrl = useCallback(() => {
  52. // Only workspace manager can buy plan
  53. if (!isCurrentWorkspaceManager) {
  54. Toast.notify({
  55. type: 'error',
  56. message: t('billing.buyPermissionDeniedTip'),
  57. className: 'z-[1001]',
  58. })
  59. return
  60. }
  61. if (isFreePlan) {
  62. window.location.href = getStartedWithCommunityUrl
  63. return
  64. }
  65. if (isPremiumPlan) {
  66. window.location.href = getWithPremiumUrl
  67. return
  68. }
  69. if (isEnterprisePlan)
  70. window.location.href = contactSalesUrl
  71. }, [isCurrentWorkspaceManager, isFreePlan, isPremiumPlan, isEnterprisePlan, t])
  72. return (
  73. <div className='relative flex flex-1 flex-col overflow-hidden'>
  74. <div className={cn('absolute inset-0 -z-10', STYLE_MAP[plan].bg)} />
  75. {/* Noise Effect */}
  76. {STYLE_MAP[plan].noise}
  77. <div className='flex flex-col px-5 py-4'>
  78. <div className=' flex flex-col gap-y-6 px-1 pt-10'>
  79. {STYLE_MAP[plan].icon}
  80. <div className='flex min-h-[104px] flex-col gap-y-2'>
  81. <div className='text-[30px] font-medium leading-[1.2] text-text-primary'>{t(`${i18nPrefix}.name`)}</div>
  82. <div className='system-md-regular line-clamp-2 text-text-secondary'>{t(`${i18nPrefix}.description`)}</div>
  83. </div>
  84. </div>
  85. {/* Price */}
  86. <div className='flex items-end gap-x-2 px-1 pb-8 pt-4'>
  87. <div className='title-4xl-semi-bold shrink-0 text-text-primary'>{t(`${i18nPrefix}.price`)}</div>
  88. {!isFreePlan && (
  89. <span className='system-md-regular pb-0.5 text-text-tertiary'>
  90. {t(`${i18nPrefix}.priceTip`)}
  91. </span>
  92. )}
  93. </div>
  94. <Button
  95. plan={plan}
  96. handleGetPayUrl={handleGetPayUrl}
  97. />
  98. </div>
  99. <List plan={plan} />
  100. {isPremiumPlan && (
  101. <div className='flex grow flex-col justify-end gap-y-2 p-6 pt-0'>
  102. <div className='flex items-center gap-x-1'>
  103. <div className='flex size-8 items-center justify-center rounded-lg border-[0.5px] border-components-panel-border-subtle bg-background-default shadow-xs shadow-shadow-shadow-3'>
  104. <Azure />
  105. </div>
  106. <div className='flex size-8 items-center justify-center rounded-lg border-[0.5px] border-components-panel-border-subtle bg-background-default shadow-xs shadow-shadow-shadow-3'>
  107. <GoogleCloud />
  108. </div>
  109. </div>
  110. <span className='system-xs-regular text-text-tertiary'>
  111. {t('billing.plans.premium.comingSoon')}
  112. </span>
  113. </div>
  114. )}
  115. </div>
  116. )
  117. }
  118. export default React.memo(SelfHostedPlanItem)