feat: make billing management entry prominent and enable current plan portal (#29321)

This commit is contained in:
yyh
2025-12-09 15:43:51 +08:00
committed by GitHub
parent c24835ca87
commit c1c1fd0509
7 changed files with 84 additions and 31 deletions

View File

@ -2,36 +2,61 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import useSWR from 'swr'
import {
RiArrowRightUpLine,
} from '@remixicon/react'
import PlanComp from '../plan'
import Divider from '@/app/components/base/divider'
import { fetchBillingUrl } from '@/service/billing'
import { useAppContext } from '@/context/app-context'
import { useProviderContext } from '@/context/provider-context'
import { useBillingUrl } from '@/service/use-billing'
const Billing: FC = () => {
const { t } = useTranslation()
const { isCurrentWorkspaceManager } = useAppContext()
const { enableBilling } = useProviderContext()
const { data: billingUrl } = useSWR(
(!enableBilling || !isCurrentWorkspaceManager) ? null : ['/billing/invoices'],
() => fetchBillingUrl().then(data => data.url),
)
const { data: billingUrl, isFetching, refetch } = useBillingUrl(enableBilling && isCurrentWorkspaceManager)
const handleOpenBilling = async () => {
// Open synchronously to preserve user gesture for popup blockers
if (billingUrl) {
window.open(billingUrl, '_blank', 'noopener,noreferrer')
return
}
const newWindow = window.open('', '_blank', 'noopener,noreferrer')
try {
const url = (await refetch()).data
if (url && newWindow) {
newWindow.location.href = url
return
}
}
catch (err) {
console.error('Failed to fetch billing url', err)
}
// Close the placeholder window if we failed to fetch the URL
newWindow?.close()
}
return (
<div>
<PlanComp loc={'billing-page'} />
{enableBilling && isCurrentWorkspaceManager && billingUrl && (
<>
<Divider className='my-4' />
<a className='system-xs-medium flex cursor-pointer items-center text-text-accent-light-mode-only' href={billingUrl} target='_blank' rel='noopener noreferrer'>
<span className='pr-0.5'>{t('billing.viewBilling')}</span>
{enableBilling && isCurrentWorkspaceManager && (
<button
type='button'
className='mt-3 flex w-full items-center justify-between rounded-xl bg-background-section-burn px-4 py-3'
onClick={handleOpenBilling}
disabled={isFetching}
>
<div className='flex flex-col gap-0.5 text-left'>
<div className='system-md-semibold text-text-primary'>{t('billing.viewBillingTitle')}</div>
<div className='system-sm-regular text-text-secondary'>{t('billing.viewBillingDescription')}</div>
</div>
<span className='inline-flex h-8 w-24 items-center justify-center gap-0.5 rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3 py-2 text-saas-dify-blue-accessible shadow-[0_1px_2px_rgba(9,9,11,0.05)] backdrop-blur-[5px]'>
<span className='system-sm-medium leading-[1]'>{t('billing.viewBillingAction')}</span>
<RiArrowRightUpLine className='h-4 w-4' />
</a>
</>
</span>
</button>
)}
</div>
)

View File

@ -8,7 +8,7 @@ import { ALL_PLANS } from '../../../config'
import Toast from '../../../../base/toast'
import { PlanRange } from '../../plan-switcher/plan-range-switcher'
import { useAppContext } from '@/context/app-context'
import { fetchSubscriptionUrls } from '@/service/billing'
import { fetchBillingUrl, fetchSubscriptionUrls } from '@/service/billing'
import List from './list'
import Button from './button'
import { Professional, Sandbox, Team } from '../../assets'
@ -39,7 +39,8 @@ const CloudPlanItem: FC<CloudPlanItemProps> = ({
const planInfo = ALL_PLANS[plan]
const isYear = planRange === PlanRange.yearly
const isCurrent = plan === currentPlan
const isPlanDisabled = planInfo.level <= ALL_PLANS[currentPlan].level
const isCurrentPaidPlan = isCurrent && !isFreePlan
const isPlanDisabled = isCurrentPaidPlan ? false : planInfo.level <= ALL_PLANS[currentPlan].level
const { isCurrentWorkspaceManager } = useAppContext()
const btnText = useMemo(() => {
@ -60,10 +61,6 @@ const CloudPlanItem: FC<CloudPlanItemProps> = ({
if (isPlanDisabled)
return
if (isFreePlan)
return
// Only workspace manager can buy plan
if (!isCurrentWorkspaceManager) {
Toast.notify({
type: 'error',
@ -74,6 +71,15 @@ const CloudPlanItem: FC<CloudPlanItemProps> = ({
}
setLoading(true)
try {
if (isCurrentPaidPlan) {
const res = await fetchBillingUrl()
window.open(res.url, '_blank')
return
}
if (isFreePlan)
return
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