mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 03:01:54 +00:00
[backport cloud/1.35] Refactor(cloud)/yearly credits monthly (#7592)
Backport of #7584 to `cloud/1.35` Automatically created by backport workflow. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7592-backport-cloud-1-35-Refactor-cloud-yearly-credits-monthly-2cc6d73d36508117b100f88f8888f787) by [Unito](https://www.unito.io) Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com>
This commit is contained in:
@@ -1918,7 +1918,7 @@
|
|||||||
"viewMoreDetails": "View more details",
|
"viewMoreDetails": "View more details",
|
||||||
"learnMore": "Learn more",
|
"learnMore": "Learn more",
|
||||||
"billedMonthly": "Billed monthly",
|
"billedMonthly": "Billed monthly",
|
||||||
"billedAnnually": "{total} Billed annually",
|
"billedYearly": "{total} Billed yearly",
|
||||||
"monthly": "Monthly",
|
"monthly": "Monthly",
|
||||||
"yearly": "Yearly",
|
"yearly": "Yearly",
|
||||||
"messageSupport": "Message support",
|
"messageSupport": "Message support",
|
||||||
@@ -1930,72 +1930,16 @@
|
|||||||
"yearlyDiscount": "20% DISCOUNT",
|
"yearlyDiscount": "20% DISCOUNT",
|
||||||
"tiers": {
|
"tiers": {
|
||||||
"founder": {
|
"founder": {
|
||||||
"name": "Founder's Edition",
|
"name": "Founder's Edition"
|
||||||
"price": "20",
|
|
||||||
"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": {
|
"standard": {
|
||||||
"name": "Standard",
|
"name": "Standard"
|
||||||
"price": {
|
|
||||||
"monthly": "20",
|
|
||||||
"yearly": "16",
|
|
||||||
"annualTotal": "$192"
|
|
||||||
},
|
|
||||||
"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",
|
|
||||||
"videoEstimate": "120"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"creator": {
|
"creator": {
|
||||||
"name": "Creator",
|
"name": "Creator"
|
||||||
"price": {
|
|
||||||
"monthly": "35",
|
|
||||||
"yearly": "28",
|
|
||||||
"annualTotal": "$336"
|
|
||||||
},
|
|
||||||
|
|
||||||
"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",
|
|
||||||
"videoEstimate": "288"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"pro": {
|
"pro": {
|
||||||
"name": "Pro",
|
"name": "Pro"
|
||||||
"price": {
|
|
||||||
"monthly": "100",
|
|
||||||
"yearly": "80",
|
|
||||||
"annualTotal": "$960"
|
|
||||||
},
|
|
||||||
"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",
|
|
||||||
"videoEstimate": "815"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": {
|
"required": {
|
||||||
@@ -2025,6 +1969,7 @@
|
|||||||
"currentPlan": "Current Plan",
|
"currentPlan": "Current Plan",
|
||||||
"subscribeTo": "Subscribe to {plan}",
|
"subscribeTo": "Subscribe to {plan}",
|
||||||
"monthlyCreditsLabel": "Monthly credits",
|
"monthlyCreditsLabel": "Monthly credits",
|
||||||
|
"yearlyCreditsLabel": "Total yearly credits",
|
||||||
"maxDurationLabel": "Max duration of each workflow run",
|
"maxDurationLabel": "Max duration of each workflow run",
|
||||||
"gpuLabel": "RTX 6000 Pro (96GB VRAM)",
|
"gpuLabel": "RTX 6000 Pro (96GB VRAM)",
|
||||||
"addCreditsLabel": "Add more credits whenever",
|
"addCreditsLabel": "Add more credits whenever",
|
||||||
@@ -2036,11 +1981,6 @@
|
|||||||
"upgradePlan": "Upgrade Plan",
|
"upgradePlan": "Upgrade Plan",
|
||||||
"upgradeTo": "Upgrade to {plan}",
|
"upgradeTo": "Upgrade to {plan}",
|
||||||
"changeTo": "Change to {plan}",
|
"changeTo": "Change to {plan}",
|
||||||
"credits": {
|
|
||||||
"standard": "4,200",
|
|
||||||
"creator": "7,400",
|
|
||||||
"pro": "21,100"
|
|
||||||
},
|
|
||||||
"maxDuration": {
|
"maxDuration": {
|
||||||
"standard": "30 min",
|
"standard": "30 min",
|
||||||
"creator": "30 min",
|
"creator": "30 min",
|
||||||
@@ -2466,4 +2406,4 @@
|
|||||||
"recentReleases": "Recent releases",
|
"recentReleases": "Recent releases",
|
||||||
"helpCenterMenu": "Help Center Menu"
|
"helpCenterMenu": "Help Center Menu"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
v-show="currentBillingCycle === 'yearly'"
|
v-show="currentBillingCycle === 'yearly'"
|
||||||
class="line-through text-2xl text-muted-foreground"
|
class="line-through text-2xl text-muted-foreground"
|
||||||
>
|
>
|
||||||
${{ tier.price.monthly }}
|
${{ tier.pricing.monthly }}
|
||||||
</span>
|
</span>
|
||||||
${{ getPrice(tier) }}
|
${{ getPrice(tier) }}
|
||||||
</span>
|
</span>
|
||||||
@@ -87,8 +87,8 @@
|
|||||||
<span class="text-sm text-muted-foreground">
|
<span class="text-sm text-muted-foreground">
|
||||||
{{
|
{{
|
||||||
currentBillingCycle === 'yearly'
|
currentBillingCycle === 'yearly'
|
||||||
? t('subscription.billedAnnually', {
|
? t('subscription.billedYearly', {
|
||||||
total: tier.price.annualTotal
|
total: `$${getAnnualTotal(tier)}`
|
||||||
})
|
})
|
||||||
: t('subscription.billedMonthly')
|
: t('subscription.billedMonthly')
|
||||||
}}
|
}}
|
||||||
@@ -102,14 +102,18 @@
|
|||||||
<span
|
<span
|
||||||
class="font-inter text-sm font-normal leading-normal text-foreground"
|
class="font-inter text-sm font-normal leading-normal text-foreground"
|
||||||
>
|
>
|
||||||
{{ t('subscription.monthlyCreditsLabel') }}
|
{{
|
||||||
|
currentBillingCycle === 'yearly'
|
||||||
|
? t('subscription.yearlyCreditsLabel')
|
||||||
|
: t('subscription.monthlyCreditsLabel')
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
<div class="flex flex-row items-center gap-1">
|
<div class="flex flex-row items-center gap-1">
|
||||||
<i class="icon-[lucide--component] text-amber-400 text-sm" />
|
<i class="icon-[lucide--component] text-amber-400 text-sm" />
|
||||||
<span
|
<span
|
||||||
class="font-inter text-sm font-bold leading-normal text-base-foreground"
|
class="font-inter text-sm font-bold leading-normal text-base-foreground"
|
||||||
>
|
>
|
||||||
{{ tier.credits }}
|
{{ n(getCreditsDisplay(tier)) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -171,7 +175,7 @@
|
|||||||
<span
|
<span
|
||||||
class="font-inter text-sm font-bold leading-normal text-base-foreground"
|
class="font-inter text-sm font-bold leading-normal text-base-foreground"
|
||||||
>
|
>
|
||||||
{{ tier.videoEstimate }}
|
{{ n(tier.pricing.videoEstimate) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -242,6 +246,7 @@ import Popover from 'primevue/popover'
|
|||||||
import SelectButton from 'primevue/selectbutton'
|
import SelectButton from 'primevue/selectbutton'
|
||||||
import type { ToggleButtonPassThroughMethodOptions } from 'primevue/togglebutton'
|
import type { ToggleButtonPassThroughMethodOptions } from 'primevue/togglebutton'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||||
@@ -271,15 +276,26 @@ interface BillingCycleOption {
|
|||||||
value: BillingCycle
|
value: BillingCycle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TierPricing {
|
||||||
|
monthly: number
|
||||||
|
yearly: number
|
||||||
|
credits: number
|
||||||
|
videoEstimate: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const TIER_PRICING: Record<TierKey, TierPricing> = {
|
||||||
|
standard: { monthly: 20, yearly: 16, credits: 4200, videoEstimate: 164 },
|
||||||
|
creator: { monthly: 35, yearly: 28, credits: 7400, videoEstimate: 288 },
|
||||||
|
pro: { monthly: 100, yearly: 80, credits: 21100, videoEstimate: 821 }
|
||||||
|
} as const
|
||||||
|
|
||||||
interface PricingTierConfig {
|
interface PricingTierConfig {
|
||||||
id: SubscriptionTier
|
id: SubscriptionTier
|
||||||
key: TierKey
|
key: TierKey
|
||||||
name: string
|
name: string
|
||||||
price: Record<BillingCycle, string> & { annualTotal: string }
|
pricing: TierPricing
|
||||||
credits: string
|
|
||||||
maxDuration: string
|
maxDuration: string
|
||||||
customLoRAs: boolean
|
customLoRAs: boolean
|
||||||
videoEstimate: string
|
|
||||||
isPopular?: boolean
|
isPopular?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,49 +316,32 @@ const tiers: PricingTierConfig[] = [
|
|||||||
id: 'STANDARD',
|
id: 'STANDARD',
|
||||||
key: 'standard',
|
key: 'standard',
|
||||||
name: t('subscription.tiers.standard.name'),
|
name: t('subscription.tiers.standard.name'),
|
||||||
price: {
|
pricing: TIER_PRICING.standard,
|
||||||
monthly: t('subscription.tiers.standard.price.monthly'),
|
|
||||||
yearly: t('subscription.tiers.standard.price.yearly'),
|
|
||||||
annualTotal: t('subscription.tiers.standard.price.annualTotal')
|
|
||||||
},
|
|
||||||
credits: t('subscription.credits.standard'),
|
|
||||||
maxDuration: t('subscription.maxDuration.standard'),
|
maxDuration: t('subscription.maxDuration.standard'),
|
||||||
customLoRAs: false,
|
customLoRAs: false,
|
||||||
videoEstimate: t('subscription.tiers.standard.benefits.videoEstimate'),
|
|
||||||
isPopular: false
|
isPopular: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'CREATOR',
|
id: 'CREATOR',
|
||||||
key: 'creator',
|
key: 'creator',
|
||||||
name: t('subscription.tiers.creator.name'),
|
name: t('subscription.tiers.creator.name'),
|
||||||
price: {
|
pricing: TIER_PRICING.creator,
|
||||||
monthly: t('subscription.tiers.creator.price.monthly'),
|
|
||||||
yearly: t('subscription.tiers.creator.price.yearly'),
|
|
||||||
annualTotal: t('subscription.tiers.creator.price.annualTotal')
|
|
||||||
},
|
|
||||||
credits: t('subscription.credits.creator'),
|
|
||||||
maxDuration: t('subscription.maxDuration.creator'),
|
maxDuration: t('subscription.maxDuration.creator'),
|
||||||
customLoRAs: true,
|
customLoRAs: true,
|
||||||
videoEstimate: t('subscription.tiers.creator.benefits.videoEstimate'),
|
|
||||||
isPopular: true
|
isPopular: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'PRO',
|
id: 'PRO',
|
||||||
key: 'pro',
|
key: 'pro',
|
||||||
name: t('subscription.tiers.pro.name'),
|
name: t('subscription.tiers.pro.name'),
|
||||||
price: {
|
pricing: TIER_PRICING.pro,
|
||||||
monthly: t('subscription.tiers.pro.price.monthly'),
|
|
||||||
yearly: t('subscription.tiers.pro.price.yearly'),
|
|
||||||
annualTotal: t('subscription.tiers.pro.price.annualTotal')
|
|
||||||
},
|
|
||||||
credits: t('subscription.credits.pro'),
|
|
||||||
maxDuration: t('subscription.maxDuration.pro'),
|
maxDuration: t('subscription.maxDuration.pro'),
|
||||||
customLoRAs: true,
|
customLoRAs: true,
|
||||||
videoEstimate: t('subscription.tiers.pro.benefits.videoEstimate'),
|
|
||||||
isPopular: false
|
isPopular: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const { n } = useI18n()
|
||||||
const { getAuthHeader } = useFirebaseAuthStore()
|
const { getAuthHeader } = useFirebaseAuthStore()
|
||||||
const { isActiveSubscription, subscriptionTier } = useSubscription()
|
const { isActiveSubscription, subscriptionTier } = useSubscription()
|
||||||
const { accessBillingPortal, reportError } = useFirebaseAuthActions()
|
const { accessBillingPortal, reportError } = useFirebaseAuthActions()
|
||||||
@@ -383,8 +382,14 @@ const getButtonTextClass = (tier: PricingTierConfig): string =>
|
|||||||
? 'font-inter text-sm font-bold leading-normal text-base-background'
|
? 'font-inter text-sm font-bold leading-normal text-base-background'
|
||||||
: 'font-inter text-sm font-bold leading-normal text-primary-foreground'
|
: 'font-inter text-sm font-bold leading-normal text-primary-foreground'
|
||||||
|
|
||||||
const getPrice = (tier: PricingTierConfig): string =>
|
const getPrice = (tier: PricingTierConfig): number =>
|
||||||
tier.price[currentBillingCycle.value]
|
tier.pricing[currentBillingCycle.value]
|
||||||
|
|
||||||
|
const getAnnualTotal = (tier: PricingTierConfig): number =>
|
||||||
|
tier.pricing.yearly * 12
|
||||||
|
|
||||||
|
const getCreditsDisplay = (tier: PricingTierConfig): number =>
|
||||||
|
tier.pricing.credits * (currentBillingCycle.value === 'yearly' ? 12 : 1)
|
||||||
|
|
||||||
const initiateCheckout = async (tierKey: TierKey) => {
|
const initiateCheckout = async (tierKey: TierKey) => {
|
||||||
const authHeader = await getAuthHeader()
|
const authHeader = await getAuthHeader()
|
||||||
|
|||||||
Reference in New Issue
Block a user