mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-21 06:49:37 +00:00
fix: make subscription panel reactive to actual tier (#7354)
## Summary Was previously hard-coded, now is actually reactive to value returned from server ## Details - Update CloudSubscriptionStatusResponse to use generated types from comfyRegistryTypes which includes subscription_tier - Add subscriptionTier computed to useSubscription composable - Make SubscriptionPanel tierName, tierPrice, and tierBenefits reactive to actual subscription tier from API - Normalize i18n tier structure with consistent value/label format - Add FOUNDERS_EDITION tier support ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7354-fix-make-subscription-panel-reactive-to-actual-tier-2c66d73d365081059a7be875c13fdd0c) by [Unito](https://www.unito.io) --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
@@ -353,8 +353,21 @@ 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<SubscriptionTier, string> = {
|
||||
STANDARD: 'standard',
|
||||
CREATOR: 'creator',
|
||||
PRO: 'pro',
|
||||
FOUNDERS_EDITION: 'founder'
|
||||
}
|
||||
|
||||
const DEFAULT_TIER_KEY = 'standard'
|
||||
|
||||
const { buildDocsUrl } = useExternalLink()
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -363,14 +376,20 @@ const {
|
||||
isCancelled,
|
||||
formattedRenewalDate,
|
||||
formattedEndDate,
|
||||
subscriptionTier,
|
||||
handleInvoiceHistory
|
||||
} = useSubscription()
|
||||
|
||||
const { show: showSubscriptionDialog } = useSubscriptionDialog()
|
||||
|
||||
// Tier data - hardcoded for Creator tier as requested
|
||||
const tierName = computed(() => t('subscription.tiers.creator.name'))
|
||||
const tierPrice = computed(() => t('subscription.tiers.creator.price'))
|
||||
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 benefits for v-for loop
|
||||
type BenefitType = 'metric' | 'feature'
|
||||
@@ -383,33 +402,34 @@ interface Benefit {
|
||||
}
|
||||
|
||||
const tierBenefits = computed(() => {
|
||||
const key = tierKey.value
|
||||
const baseBenefits: Benefit[] = [
|
||||
{
|
||||
key: 'monthlyCredits',
|
||||
type: 'metric',
|
||||
value: t('subscription.tiers.creator.benefits.monthlyCredits'),
|
||||
label: t('subscription.tiers.creator.benefits.monthlyCreditsLabel')
|
||||
value: t(`subscription.tiers.${key}.benefits.monthlyCredits`),
|
||||
label: t(`subscription.tiers.${key}.benefits.monthlyCreditsLabel`)
|
||||
},
|
||||
{
|
||||
key: 'maxDuration',
|
||||
type: 'metric',
|
||||
value: t('subscription.tiers.creator.benefits.maxDuration'),
|
||||
label: t('subscription.tiers.creator.benefits.maxDurationLabel')
|
||||
value: t(`subscription.tiers.${key}.benefits.maxDuration`),
|
||||
label: t(`subscription.tiers.${key}.benefits.maxDurationLabel`)
|
||||
},
|
||||
{
|
||||
key: 'gpu',
|
||||
type: 'feature',
|
||||
label: t('subscription.tiers.creator.benefits.gpuLabel')
|
||||
label: t(`subscription.tiers.${key}.benefits.gpuLabel`)
|
||||
},
|
||||
{
|
||||
key: 'addCredits',
|
||||
type: 'feature',
|
||||
label: t('subscription.tiers.creator.benefits.addCreditsLabel')
|
||||
label: t(`subscription.tiers.${key}.benefits.addCreditsLabel`)
|
||||
},
|
||||
{
|
||||
key: 'customLoRAs',
|
||||
type: 'feature',
|
||||
label: t('subscription.tiers.creator.benefits.customLoRAsLabel')
|
||||
label: t(`subscription.tiers.${key}.benefits.customLoRAsLabel`)
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -139,6 +139,7 @@ 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'
|
||||
@@ -155,8 +156,18 @@ const emit = defineEmits<{
|
||||
close: [subscribed: boolean]
|
||||
}>()
|
||||
|
||||
const { formattedMonthlyPrice, fetchStatus, isActiveSubscription } =
|
||||
useSubscription()
|
||||
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 { featureFlag } = useFeatureFlags()
|
||||
const subscriptionTiersEnabled = featureFlag(
|
||||
'subscription_tiers_enabled',
|
||||
|
||||
@@ -5,7 +5,6 @@ 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'
|
||||
@@ -14,18 +13,16 @@ 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 = {
|
||||
is_active: boolean
|
||||
subscription_id: string
|
||||
renewal_date: string | null
|
||||
end_date?: string | null
|
||||
}
|
||||
export type CloudSubscriptionStatusResponse = NonNullable<
|
||||
operations['GetCloudSubscriptionStatus']['responses']['200']['content']['application/json']
|
||||
>
|
||||
|
||||
function useSubscriptionInternal() {
|
||||
const subscriptionStatus = ref<CloudSubscriptionStatusResponse | null>(null)
|
||||
@@ -72,8 +69,8 @@ function useSubscriptionInternal() {
|
||||
})
|
||||
})
|
||||
|
||||
const formattedMonthlyPrice = computed(
|
||||
() => `$${MONTHLY_SUBSCRIPTION_PRICE.toFixed(0)}`
|
||||
const subscriptionTier = computed(
|
||||
() => subscriptionStatus.value?.subscription_tier ?? null
|
||||
)
|
||||
|
||||
const buildApiUrl = (path: string) => `${getComfyApiBaseUrl()}${path}`
|
||||
@@ -227,7 +224,7 @@ function useSubscriptionInternal() {
|
||||
isCancelled,
|
||||
formattedRenewalDate,
|
||||
formattedEndDate,
|
||||
formattedMonthlyPrice,
|
||||
subscriptionTier,
|
||||
|
||||
// Actions
|
||||
subscribe,
|
||||
|
||||
Reference in New Issue
Block a user