From 798e5ed7a736dbb88f7e5194dc3bd5d4fe3d6eb9 Mon Sep 17 00:00:00 2001
From: yyh <92089059+lyzno1@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:32:03 +0800
Subject: [PATCH] refactor(web): derive workspace billing from current
workspace (#39674)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
---
.../__tests__/workspace-card.spec.tsx | 42 +++++--------------
.../main-nav/components/workspace-card.tsx | 17 ++++----
2 files changed, 18 insertions(+), 41 deletions(-)
diff --git a/web/app/components/main-nav/components/__tests__/workspace-card.spec.tsx b/web/app/components/main-nav/components/__tests__/workspace-card.spec.tsx
index dfc1b35d04b..fb9205b2248 100644
--- a/web/app/components/main-nav/components/__tests__/workspace-card.spec.tsx
+++ b/web/app/components/main-nav/components/__tests__/workspace-card.spec.tsx
@@ -1,6 +1,7 @@
+import type { PostWorkspacesCurrentResponse } from '@dify/contracts/api/console/workspaces/types.gen'
import type { ModalContextState } from '@/context/modal-context'
import type { ProviderContextState } from '@/context/provider-context'
-import type { ICurrentWorkspace, IWorkspace } from '@/models/common'
+import type { IWorkspace } from '@/models/common'
import { fireEvent, screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Plan } from '@/app/components/billing/type'
@@ -95,14 +96,13 @@ vi.mock('@/service/client', async (importOriginal) => {
}
})
-const currentWorkspaceValue: ICurrentWorkspace = {
+const currentWorkspaceValue: PostWorkspacesCurrentResponse = {
id: 'workspace-1',
name: 'Solar Studio',
plan: Plan.sandbox,
status: 'normal',
created_at: 0,
role: 'owner',
- providers: [],
trial_credits: 10000,
trial_credits_used: 2500,
trial_credits_exhausted_at: 0,
@@ -111,11 +111,11 @@ const currentWorkspaceValue: ICurrentWorkspace = {
const mockSetShowPricingModal = vi.fn()
const mockSetShowAccountSettingModal = vi.fn()
-let mockCurrentWorkspace: ICurrentWorkspace | undefined = currentWorkspaceValue
+let mockCurrentWorkspace: PostWorkspacesCurrentResponse | undefined = currentWorkspaceValue
let mockWorkspaces: IWorkspace[] = []
const mockCurrentWorkspaceQuery = (
- data: ICurrentWorkspace | undefined = currentWorkspaceValue,
+ data: PostWorkspacesCurrentResponse | undefined = currentWorkspaceValue,
isPending = false,
) => {
mockCurrentWorkspace = isPending ? undefined : data
@@ -284,13 +284,12 @@ describe('WorkspaceCard', () => {
plan: Plan.team,
})
vi.mocked(useProviderContext).mockReturnValue({
- enableBilling: true,
+ enableBilling: false,
isEducationAccount: false,
isEducationWorkspace: false,
isFetchedPlan: true,
- plan: { type: Plan.team },
+ plan: { type: Plan.sandbox },
} as ProviderContextState)
-
renderWorkspaceCard({ systemFeatures: { deployment_edition: 'CLOUD' } })
expect(screen.getByText(Plan.team)).toBeInTheDocument()
@@ -304,14 +303,6 @@ describe('WorkspaceCard', () => {
...currentWorkspaceValue,
plan: Plan.team,
})
- vi.mocked(useProviderContext).mockReturnValue({
- enableBilling: true,
- isEducationAccount: false,
- isEducationWorkspace: false,
- isFetchedPlan: true,
- plan: { type: Plan.team },
- } as ProviderContextState)
-
renderWorkspaceCard({ systemFeatures: { deployment_edition: 'CLOUD' } })
expect(screen.getByText(Plan.team)).toBeInTheDocument()
@@ -322,14 +313,6 @@ describe('WorkspaceCard', () => {
...currentWorkspaceValue,
plan: '',
})
- vi.mocked(useProviderContext).mockReturnValue({
- enableBilling: true,
- isEducationAccount: false,
- isEducationWorkspace: false,
- isFetchedPlan: false,
- plan: { type: Plan.sandbox },
- } as ProviderContextState)
-
renderWorkspaceCard({
systemFeatures: {
deployment_edition: 'ENTERPRISE',
@@ -471,13 +454,10 @@ describe('WorkspaceCard', () => {
})
it('opens members settings from workspace menu when billing is disabled', async () => {
- vi.mocked(useProviderContext).mockReturnValue({
- enableBilling: false,
- isEducationAccount: false,
- isEducationWorkspace: false,
- isFetchedPlan: false,
- plan: { type: Plan.sandbox },
- } as ProviderContextState)
+ mockCurrentWorkspaceQuery({
+ ...currentWorkspaceValue,
+ plan: null,
+ })
renderWorkspaceCard()
diff --git a/web/app/components/main-nav/components/workspace-card.tsx b/web/app/components/main-nav/components/workspace-card.tsx
index 4c446efa3e5..301a57ef2b7 100644
--- a/web/app/components/main-nav/components/workspace-card.tsx
+++ b/web/app/components/main-nav/components/workspace-card.tsx
@@ -16,7 +16,6 @@ import LicenseNav from '@/app/components/header/license-env'
import { buildIntegrationPath } from '@/app/components/integrations/routes'
import { useModalContext } from '@/context/modal-context'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
-import { useProviderContext } from '@/context/provider-context'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import Link from '@/next/link'
import { consoleQuery } from '@/service/client'
@@ -259,26 +258,24 @@ export function WorkspaceCard() {
const switchWorkspaceMutation = useMutation(consoleQuery.workspaces.switch.post.mutationOptions())
const currentWorkspace = currentWorkspaceQuery.data
const workspaces = workspacesQuery.data?.workspaces
- const { enableBilling } = useProviderContext()
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const { setShowPricingModal, setShowAccountSettingModal } = useModalContext()
- const showCloudBilling = deploymentEdition === 'CLOUD' && enableBilling
+ const isCloudEdition = deploymentEdition === 'CLOUD'
const prefetchWorkspaces = () => {
void queryClient.prefetchQuery(workspacesQueryOptions)
}
if (currentWorkspaceQuery.isPending || !currentWorkspace?.name) {
return (
-
+
)
}
const workspacePlan = isWorkspacePlan(currentWorkspace.plan) ? currentWorkspace.plan : null
- const isFreePlan = workspacePlan === Plan.sandbox
+ const hasBillingPlan = typeof currentWorkspace.plan === 'string'
+ const showCloudBilling = isCloudEdition && hasBillingPlan
const showPlanAction = showCloudBilling && workspacePlan !== null
+ const isFreePlan = workspacePlan === Plan.sandbox
const planActionLabel = t(
($) => $[isFreePlan ? 'upgradeBtn.encourageShort' : 'upgradeBtn.plain'],
{ ns: 'billing' },
@@ -286,7 +283,7 @@ export function WorkspaceCard() {
const showInviteMembers = hasPermission(workspacePermissionKeys, 'workspace.member.manage')
const renderWorkspaceStatus = () => {
if (deploymentEdition === 'CLOUD')
- return enableBilling && workspacePlan ? : null
+ return workspacePlan ? : null
if (deploymentEdition === 'ENTERPRISE') return
return null
}
@@ -333,7 +330,7 @@ export function WorkspaceCard() {
onOpenSettings={() => {
setOpen(false)
setShowAccountSettingModal({
- payload: enableBilling ? ACCOUNT_SETTING_TAB.BILLING : ACCOUNT_SETTING_TAB.MEMBERS,
+ payload: hasBillingPlan ? ACCOUNT_SETTING_TAB.BILLING : ACCOUNT_SETTING_TAB.MEMBERS,
})
}}
onInviteMembers={() => {