This commit is contained in:
2025-12-01 17:21:38 +08:00
parent 32fee2b8ab
commit fab8c13cb3
7511 changed files with 996300 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
import type { FC } from 'react'
import React from 'react'
import type { Category } from '../index'
import { useTranslation } from 'react-i18next'
import { Cloud, SelfHosted } from '../assets'
import Tab from './tab'
import Divider from '@/app/components/base/divider'
import type { PlanRange } from './plan-range-switcher'
import PlanRangeSwitcher from './plan-range-switcher'
type PlanSwitcherProps = {
currentCategory: Category
currentPlanRange: PlanRange
onChangeCategory: (category: Category) => void
onChangePlanRange: (value: PlanRange) => void
}
const PlanSwitcher: FC<PlanSwitcherProps> = ({
currentCategory,
currentPlanRange,
onChangeCategory,
onChangePlanRange,
}) => {
const { t } = useTranslation()
const isCloud = currentCategory === 'cloud'
const tabs = {
cloud: {
value: 'cloud' as Category,
label: t('billing.plansCommon.cloud'),
Icon: Cloud,
},
self: {
value: 'self' as Category,
label: t('billing.plansCommon.self'),
Icon: SelfHosted,
},
}
return (
<div className='flex w-full justify-center border-t border-divider-accent px-10'>
<div className='flex max-w-[1680px] grow items-center justify-between border-x border-divider-accent p-1'>
<div className='flex items-center'>
<Tab<Category>
{...tabs.cloud}
isActive={currentCategory === tabs.cloud.value}
onClick={onChangeCategory}
/>
<Divider type='vertical' className='mx-2 h-4 bg-divider-accent' />
<Tab<Category>
{...tabs.self}
isActive={currentCategory === tabs.self.value}
onClick={onChangeCategory}
/>
</div>
{isCloud && (
<PlanRangeSwitcher
value={currentPlanRange}
onChange={onChangePlanRange}
/>
)}
</div>
</div>
)
}
export default React.memo(PlanSwitcher)

View File

@@ -0,0 +1,38 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import Switch from '../../../base/switch'
export enum PlanRange {
monthly = 'monthly',
yearly = 'yearly',
}
type PlanRangeSwitcherProps = {
value: PlanRange
onChange: (value: PlanRange) => void
}
const PlanRangeSwitcher: FC<PlanRangeSwitcherProps> = ({
value,
onChange,
}) => {
const { t } = useTranslation()
return (
<div className='flex items-center justify-end gap-x-3 pr-5'>
<Switch
size='l'
defaultValue={value === PlanRange.yearly}
onChange={(v) => {
onChange(v ? PlanRange.yearly : PlanRange.monthly)
}}
/>
<span className='system-md-regular text-text-tertiary'>
{t('billing.plansCommon.annualBilling', { percent: 17 })}
</span>
</div>
)
}
export default React.memo(PlanRangeSwitcher)

View File

@@ -0,0 +1,41 @@
import React, { useCallback } from 'react'
import cn from '@/utils/classnames'
type TabProps<T> = {
Icon: React.ComponentType<{ isActive: boolean }>
value: T
label: string
isActive: boolean
onClick: (value: T) => void
}
const Tab = <T,>({
Icon,
value,
label,
isActive,
onClick,
}: TabProps<T>) => {
const handleClick = useCallback(() => {
onClick(value)
}, [onClick, value])
return (
<div
className='flex cursor-pointer items-center justify-center gap-x-2 px-5 py-3'
onClick={handleClick}
>
<Icon isActive={isActive} />
<span
className={cn(
'system-xl-semibold text-text-secondary',
isActive && 'text-saas-dify-blue-accessible',
)}
>
{label}
</span>
</div>
)
}
export default React.memo(Tab) as typeof Tab