From 4e68a266a05b74fd109ea97e6fe0a45d8ff8ff4d Mon Sep 17 00:00:00 2001 From: bymyself Date: Wed, 10 Dec 2025 23:54:22 -0800 Subject: [PATCH] Revert "fix: make subscription panel reactive to actual tier (#7354)" This reverts commit d3d02d0d4b96f57b4c64356773d42d8e6c61d7ec. --- src/locales/en/main.json | 41 +++--- .../components/SubscriptionPanel.vue | 40 ++---- .../SubscriptionRequiredDialogContent.vue | 15 +-- .../composables/useSubscription.ts | 17 ++- .../components/SubscriptionPanel.test.ts | 121 ++---------------- .../subscription/useSubscription.test.ts | 24 +--- 6 files changed, 57 insertions(+), 201 deletions(-) diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 535f25c3d..05c35a3a3 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -1900,29 +1900,25 @@ }, "tiers": { "founder": { - "name": "Founder's Edition", + "name": "Founder's Edition Standard", "price": "20.00", "benefits": { - "monthlyCredits": "5,460", - "monthlyCreditsLabel": "monthly credits", - "maxDuration": "30 min", - "maxDurationLabel": "max duration of each workflow run", - "gpuLabel": "RTX 6000 Pro (96GB VRAM)", - "addCreditsLabel": "Add more credits whenever", - "customLoRAsLabel": "Import your own LoRAs" + "monthlyCredits": "5,460 monthly credits", + "maxDuration": "30 min max duration of each workflow run", + "rtx6000": "RTX 6000 Pro (96GB VRAM)", + "addCredits": "Add more credits whenever" } }, "standard": { "name": "Standard", "price": "20.00", "benefits": { - "monthlyCredits": "4,200", - "monthlyCreditsLabel": "monthly credits", - "maxDuration": "30 min", - "maxDurationLabel": "max duration of each workflow run", - "gpuLabel": "RTX 6000 Pro (96GB VRAM)", - "addCreditsLabel": "Add more credits whenever", - "customLoRAsLabel": "Import your own LoRAs" + "monthlyCredits": "4,200 monthly credits", + "maxDuration": "30 min max duration of each workflow run", + "rtx6000": "RTX 6000 Pro (96GB VRAM)", + "addCredits": "Add more credits whenever", + "customLoRAs": "Import your own LoRAs", + "videoEstimate": "164" } }, "creator": { @@ -1940,15 +1936,14 @@ }, "pro": { "name": "Pro", - "price": "100.00", + "price": "100.00", "benefits": { - "monthlyCredits": "21,100", - "monthlyCreditsLabel": "monthly credits", - "maxDuration": "1 hr", - "maxDurationLabel": "max duration of each workflow run", - "gpuLabel": "RTX 6000 Pro (96GB VRAM)", - "addCreditsLabel": "Add more credits whenever", - "customLoRAsLabel": "Import your own LoRAs" + "monthlyCredits": "21,100 monthly credits", + "maxDuration": "1 hr max duration of each workflow run", + "rtx6000": "RTX 6000 Pro (96GB VRAM)", + "addCredits": "Add more credits whenever", + "customLoRAs": "Import your own LoRAs", + "videoEstimate": "821" } } }, diff --git a/src/platform/cloud/subscription/components/SubscriptionPanel.vue b/src/platform/cloud/subscription/components/SubscriptionPanel.vue index d5db565e8..3a342e99c 100644 --- a/src/platform/cloud/subscription/components/SubscriptionPanel.vue +++ b/src/platform/cloud/subscription/components/SubscriptionPanel.vue @@ -353,21 +353,8 @@ import { useSubscription } from '@/platform/cloud/subscription/composables/useSu import { useSubscriptionActions } from '@/platform/cloud/subscription/composables/useSubscriptionActions' import { useSubscriptionCredits } from '@/platform/cloud/subscription/composables/useSubscriptionCredits' import { useSubscriptionDialog } from '@/platform/cloud/subscription/composables/useSubscriptionDialog' -import type { components } from '@/types/comfyRegistryTypes' import { cn } from '@/utils/tailwindUtil' -type SubscriptionTier = components['schemas']['SubscriptionTier'] - -/** Maps API subscription tier values to i18n translation keys */ -const TIER_TO_I18N_KEY: Record = { - STANDARD: 'standard', - CREATOR: 'creator', - PRO: 'pro', - FOUNDERS_EDITION: 'founder' -} - -const DEFAULT_TIER_KEY = 'standard' - const { buildDocsUrl } = useExternalLink() const { t } = useI18n() @@ -376,20 +363,14 @@ const { isCancelled, formattedRenewalDate, formattedEndDate, - subscriptionTier, handleInvoiceHistory } = useSubscription() const { show: showSubscriptionDialog } = useSubscriptionDialog() -const tierKey = computed(() => { - const tier = subscriptionTier.value - if (!tier) return DEFAULT_TIER_KEY - return TIER_TO_I18N_KEY[tier] ?? DEFAULT_TIER_KEY -}) - -const tierName = computed(() => t(`subscription.tiers.${tierKey.value}.name`)) -const tierPrice = computed(() => t(`subscription.tiers.${tierKey.value}.price`)) +// Tier data - hardcoded for Creator tier as requested +const tierName = computed(() => t('subscription.tiers.creator.name')) +const tierPrice = computed(() => t('subscription.tiers.creator.price')) // Tier benefits for v-for loop type BenefitType = 'metric' | 'feature' @@ -402,34 +383,33 @@ interface Benefit { } const tierBenefits = computed(() => { - const key = tierKey.value const baseBenefits: Benefit[] = [ { key: 'monthlyCredits', type: 'metric', - value: t(`subscription.tiers.${key}.benefits.monthlyCredits`), - label: t(`subscription.tiers.${key}.benefits.monthlyCreditsLabel`) + value: t('subscription.tiers.creator.benefits.monthlyCredits'), + label: t('subscription.tiers.creator.benefits.monthlyCreditsLabel') }, { key: 'maxDuration', type: 'metric', - value: t(`subscription.tiers.${key}.benefits.maxDuration`), - label: t(`subscription.tiers.${key}.benefits.maxDurationLabel`) + value: t('subscription.tiers.creator.benefits.maxDuration'), + label: t('subscription.tiers.creator.benefits.maxDurationLabel') }, { key: 'gpu', type: 'feature', - label: t(`subscription.tiers.${key}.benefits.gpuLabel`) + label: t('subscription.tiers.creator.benefits.gpuLabel') }, { key: 'addCredits', type: 'feature', - label: t(`subscription.tiers.${key}.benefits.addCreditsLabel`) + label: t('subscription.tiers.creator.benefits.addCreditsLabel') }, { key: 'customLoRAs', type: 'feature', - label: t(`subscription.tiers.${key}.benefits.customLoRAsLabel`) + label: t('subscription.tiers.creator.benefits.customLoRAsLabel') } ] diff --git a/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue b/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue index ddb1680d8..2dd4ae548 100644 --- a/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue +++ b/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue @@ -139,7 +139,6 @@ import { computed, onBeforeUnmount, watch } from 'vue' import CloudBadge from '@/components/topbar/CloudBadge.vue' import { useFeatureFlags } from '@/composables/useFeatureFlags' -import { MONTHLY_SUBSCRIPTION_PRICE } from '@/config/subscriptionPricesConfig' import StripePricingTable from '@/platform/cloud/subscription/components/StripePricingTable.vue' import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue' import SubscriptionBenefits from '@/platform/cloud/subscription/components/SubscriptionBenefits.vue' @@ -156,18 +155,8 @@ const emit = defineEmits<{ close: [subscribed: boolean] }>() -const { fetchStatus, isActiveSubscription } = useSubscription() - -// Legacy price for non-tier flow with locale-aware formatting -const formattedMonthlyPrice = new Intl.NumberFormat( - navigator.language || 'en-US', - { - style: 'currency', - currency: 'USD', - minimumFractionDigits: 0, - maximumFractionDigits: 0 - } -).format(MONTHLY_SUBSCRIPTION_PRICE) +const { formattedMonthlyPrice, fetchStatus, isActiveSubscription } = + useSubscription() const { featureFlag } = useFeatureFlags() const subscriptionTiersEnabled = featureFlag( 'subscription_tiers_enabled', diff --git a/src/platform/cloud/subscription/composables/useSubscription.ts b/src/platform/cloud/subscription/composables/useSubscription.ts index 4e6c7aba2..9762647d8 100644 --- a/src/platform/cloud/subscription/composables/useSubscription.ts +++ b/src/platform/cloud/subscription/composables/useSubscription.ts @@ -5,6 +5,7 @@ import { useCurrentUser } from '@/composables/auth/useCurrentUser' import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions' import { useErrorHandling } from '@/composables/useErrorHandling' import { getComfyApiBaseUrl, getComfyPlatformBaseUrl } from '@/config/comfyApi' +import { MONTHLY_SUBSCRIPTION_PRICE } from '@/config/subscriptionPricesConfig' import { t } from '@/i18n' import { isCloud } from '@/platform/distribution/types' import { useTelemetry } from '@/platform/telemetry' @@ -13,16 +14,18 @@ import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore' import { useDialogService } from '@/services/dialogService' -import type { operations } from '@/types/comfyRegistryTypes' import { useSubscriptionCancellationWatcher } from './useSubscriptionCancellationWatcher' type CloudSubscriptionCheckoutResponse = { checkout_url: string } -export type CloudSubscriptionStatusResponse = NonNullable< - operations['GetCloudSubscriptionStatus']['responses']['200']['content']['application/json'] -> +export type CloudSubscriptionStatusResponse = { + is_active: boolean + subscription_id: string + renewal_date: string | null + end_date?: string | null +} function useSubscriptionInternal() { const subscriptionStatus = ref(null) @@ -69,8 +72,8 @@ function useSubscriptionInternal() { }) }) - const subscriptionTier = computed( - () => subscriptionStatus.value?.subscription_tier ?? null + const formattedMonthlyPrice = computed( + () => `$${MONTHLY_SUBSCRIPTION_PRICE.toFixed(0)}` ) const buildApiUrl = (path: string) => `${getComfyApiBaseUrl()}${path}` @@ -224,7 +227,7 @@ function useSubscriptionInternal() { isCancelled, formattedRenewalDate, formattedEndDate, - subscriptionTier, + formattedMonthlyPrice, // Actions subscribe, diff --git a/tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts b/tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts index 6bed98305..5be9f7d80 100644 --- a/tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts +++ b/tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts @@ -1,25 +1,18 @@ import { createTestingPinia } from '@pinia/testing' import { mount } from '@vue/test-utils' import { beforeEach, describe, expect, it, vi } from 'vitest' -import { computed, ref } from 'vue' import { createI18n } from 'vue-i18n' import SubscriptionPanel from '@/platform/cloud/subscription/components/SubscriptionPanel.vue' -// Mock state refs that can be modified between tests -const mockIsActiveSubscription = ref(false) -const mockIsCancelled = ref(false) -const mockSubscriptionTier = ref< - 'STANDARD' | 'CREATOR' | 'PRO' | 'FOUNDERS_EDITION' | null ->('CREATOR') - -// Mock composables - using computed to match composable return types +// Mock composables const mockSubscriptionData = { - isActiveSubscription: computed(() => mockIsActiveSubscription.value), - isCancelled: computed(() => mockIsCancelled.value), - formattedRenewalDate: computed(() => '2024-12-31'), - formattedEndDate: computed(() => '2024-12-31'), - subscriptionTier: computed(() => mockSubscriptionTier.value), + isActiveSubscription: false, + isCancelled: false, + formattedRenewalDate: '2024-12-31', + formattedEndDate: '2024-12-31', + formattedMonthlyPrice: '$9.99', + manageSubscription: vi.fn(), handleInvoiceHistory: vi.fn() } @@ -57,15 +50,6 @@ vi.mock( }) ) -vi.mock( - '@/platform/cloud/subscription/composables/useSubscriptionDialog', - () => ({ - useSubscriptionDialog: () => ({ - show: vi.fn() - }) - }) -) - // Create i18n instance for testing const i18n = createI18n({ legacy: false, @@ -74,15 +58,12 @@ const i18n = createI18n({ en: { subscription: { title: 'Subscription', - titleUnsubscribed: 'Subscribe', perMonth: '/ month', subscribeNow: 'Subscribe Now', manageSubscription: 'Manage Subscription', partnerNodesBalance: 'Partner Nodes Balance', partnerNodesDescription: 'Credits for partner nodes', totalCredits: 'Total Credits', - creditsRemainingThisMonth: 'Credits remaining this month', - creditsYouveAdded: "Credits you've added", monthlyBonusDescription: 'Monthly bonus', prepaidDescription: 'Prepaid credits', monthlyCreditsRollover: 'Monthly credits rollover info', @@ -90,67 +71,11 @@ const i18n = createI18n({ viewUsageHistory: 'View Usage History', addCredits: 'Add Credits', yourPlanIncludes: 'Your plan includes', - viewMoreDetailsPlans: 'View more details about plans & pricing', learnMore: 'Learn More', messageSupport: 'Message Support', invoiceHistory: 'Invoice History', - partnerNodesCredits: 'Partner nodes pricing', renewsDate: 'Renews {date}', - expiresDate: 'Expires {date}', - tiers: { - founder: { - name: "Founder's Edition", - price: '20.00', - benefits: { - monthlyCredits: '5,460', - monthlyCreditsLabel: 'monthly credits', - maxDuration: '30 min', - maxDurationLabel: 'max duration of each workflow run', - gpuLabel: 'RTX 6000 Pro (96GB VRAM)', - addCreditsLabel: 'Add more credits whenever', - customLoRAsLabel: 'Import your own LoRAs' - } - }, - standard: { - name: 'Standard', - price: '20.00', - benefits: { - monthlyCredits: '4,200', - monthlyCreditsLabel: 'monthly credits', - maxDuration: '30 min', - maxDurationLabel: 'max duration of each workflow run', - gpuLabel: 'RTX 6000 Pro (96GB VRAM)', - addCreditsLabel: 'Add more credits whenever', - customLoRAsLabel: 'Import your own LoRAs' - } - }, - creator: { - name: 'Creator', - price: '35.00', - benefits: { - monthlyCredits: '7,400', - monthlyCreditsLabel: 'monthly credits', - maxDuration: '30 min', - maxDurationLabel: 'max duration of each workflow run', - gpuLabel: 'RTX 6000 Pro (96GB VRAM)', - addCreditsLabel: 'Add more credits whenever', - customLoRAsLabel: 'Import your own LoRAs' - } - }, - pro: { - name: 'Pro', - price: '100.00', - benefits: { - monthlyCredits: '21,100', - monthlyCreditsLabel: 'monthly credits', - maxDuration: '1 hr', - maxDurationLabel: 'max duration of each workflow run', - gpuLabel: 'RTX 6000 Pro (96GB VRAM)', - addCreditsLabel: 'Add more credits whenever', - customLoRAsLabel: 'Import your own LoRAs' - } - } - } + expiresDate: 'Expires {date}' } } } @@ -191,22 +116,18 @@ function createWrapper(overrides = {}) { describe('SubscriptionPanel', () => { beforeEach(() => { vi.clearAllMocks() - // Reset mock state - mockIsActiveSubscription.value = false - mockIsCancelled.value = false - mockSubscriptionTier.value = 'CREATOR' }) describe('subscription state functionality', () => { it('shows correct UI for active subscription', () => { - mockIsActiveSubscription.value = true + mockSubscriptionData.isActiveSubscription = true const wrapper = createWrapper() expect(wrapper.text()).toContain('Manage Subscription') expect(wrapper.text()).toContain('Add Credits') }) it('shows correct UI for inactive subscription', () => { - mockIsActiveSubscription.value = false + mockSubscriptionData.isActiveSubscription = false const wrapper = createWrapper() expect(wrapper.findComponent({ name: 'SubscribeButton' }).exists()).toBe( true @@ -216,32 +137,18 @@ describe('SubscriptionPanel', () => { }) it('shows renewal date for active non-cancelled subscription', () => { - mockIsActiveSubscription.value = true - mockIsCancelled.value = false + mockSubscriptionData.isActiveSubscription = true + mockSubscriptionData.isCancelled = false const wrapper = createWrapper() expect(wrapper.text()).toContain('Renews 2024-12-31') }) it('shows expiry date for cancelled subscription', () => { - mockIsActiveSubscription.value = true - mockIsCancelled.value = true + mockSubscriptionData.isActiveSubscription = true + mockSubscriptionData.isCancelled = true const wrapper = createWrapper() expect(wrapper.text()).toContain('Expires 2024-12-31') }) - - it('displays FOUNDERS_EDITION tier correctly', () => { - mockSubscriptionTier.value = 'FOUNDERS_EDITION' - const wrapper = createWrapper() - expect(wrapper.text()).toContain("Founder's Edition") - expect(wrapper.text()).toContain('5,460') - }) - - it('displays CREATOR tier correctly', () => { - mockSubscriptionTier.value = 'CREATOR' - const wrapper = createWrapper() - expect(wrapper.text()).toContain('Creator') - expect(wrapper.text()).toContain('7,400') - }) }) describe('credit display functionality', () => { diff --git a/tests-ui/tests/platform/cloud/subscription/useSubscription.test.ts b/tests-ui/tests/platform/cloud/subscription/useSubscription.test.ts index 9409709d3..1305e9f15 100644 --- a/tests-ui/tests/platform/cloud/subscription/useSubscription.test.ts +++ b/tests-ui/tests/platform/cloud/subscription/useSubscription.test.ts @@ -152,28 +152,10 @@ describe('useSubscription', () => { expect(formattedRenewalDate.value).toBe('') }) - it('should return subscription tier from status', async () => { - vi.mocked(global.fetch).mockResolvedValue({ - ok: true, - json: async () => ({ - is_active: true, - subscription_id: 'sub_123', - subscription_tier: 'CREATOR', - renewal_date: '2025-11-16T12:00:00Z' - }) - } as Response) + it('should format monthly price correctly', () => { + const { formattedMonthlyPrice } = useSubscription() - mockIsLoggedIn.value = true - const { subscriptionTier, fetchStatus } = useSubscription() - - await fetchStatus() - expect(subscriptionTier.value).toBe('CREATOR') - }) - - it('should return null when subscription tier is not available', () => { - const { subscriptionTier } = useSubscription() - - expect(subscriptionTier.value).toBeNull() + expect(formattedMonthlyPrice.value).toBe('$20') }) })