mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 10:59:53 +00:00
Refactor(cloud)/yearly credits monthly (#7584)
## Summary Add yearly total credits vs monthly. Also pulled out numerical values from the main.json to avoid translation issues and used n() for better currency support on prices. ## Changes - **What**: PricingTable.vue, /en/main.json - **Breaking**: <!-- Any breaking changes (if none, remove this line) --> - **Dependencies**: <!-- New dependencies (if none, remove this line) --> ## Review Focus <!-- Critical design decisions or edge cases that need attention --> <!-- If this PR fixes an issue, uncomment and update the line below --> <!-- Fixes #ISSUE_NUMBER --> ## Screenshots (if applicable) <img width="2321" height="1538" alt="image" src="https://github.com/user-attachments/assets/8c7b3eed-bfd8-4188-914f-3bfa5397a84f" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7584-Refactor-cloud-yearly-credits-monthly-2cc6d73d365081b28afbec7f9d22546f) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -1938,7 +1938,7 @@
|
||||
"viewMoreDetails": "View more details",
|
||||
"learnMore": "Learn more",
|
||||
"billedMonthly": "Billed monthly",
|
||||
"billedAnnually": "{total} Billed annually",
|
||||
"billedYearly": "{total} Billed yearly",
|
||||
"monthly": "Monthly",
|
||||
"yearly": "Yearly",
|
||||
"messageSupport": "Message support",
|
||||
@@ -1950,72 +1950,16 @@
|
||||
"yearlyDiscount": "20% DISCOUNT",
|
||||
"tiers": {
|
||||
"founder": {
|
||||
"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"
|
||||
}
|
||||
"name": "Founder's Edition"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
"name": "Standard"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
"name": "Creator"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
"name": "Pro"
|
||||
}
|
||||
},
|
||||
"required": {
|
||||
@@ -2036,6 +1980,7 @@
|
||||
"currentPlan": "Current Plan",
|
||||
"subscribeTo": "Subscribe to {plan}",
|
||||
"monthlyCreditsLabel": "Monthly credits",
|
||||
"yearlyCreditsLabel": "Total yearly credits",
|
||||
"maxDurationLabel": "Max duration of each workflow run",
|
||||
"gpuLabel": "RTX 6000 Pro (96GB VRAM)",
|
||||
"addCreditsLabel": "Add more credits whenever",
|
||||
@@ -2047,11 +1992,6 @@
|
||||
"upgradePlan": "Upgrade Plan",
|
||||
"upgradeTo": "Upgrade to {plan}",
|
||||
"changeTo": "Change to {plan}",
|
||||
"credits": {
|
||||
"standard": "4,200",
|
||||
"creator": "7,400",
|
||||
"pro": "21,100"
|
||||
},
|
||||
"maxDuration": {
|
||||
"standard": "30 min",
|
||||
"creator": "30 min",
|
||||
@@ -2478,4 +2418,4 @@
|
||||
"recentReleases": "Recent releases",
|
||||
"helpCenterMenu": "Help Center Menu"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
v-show="currentBillingCycle === 'yearly'"
|
||||
class="line-through text-2xl text-muted-foreground"
|
||||
>
|
||||
${{ tier.price.monthly }}
|
||||
${{ tier.pricing.monthly }}
|
||||
</span>
|
||||
${{ getPrice(tier) }}
|
||||
</span>
|
||||
@@ -87,8 +87,8 @@
|
||||
<span class="text-sm text-muted-foreground">
|
||||
{{
|
||||
currentBillingCycle === 'yearly'
|
||||
? t('subscription.billedAnnually', {
|
||||
total: tier.price.annualTotal
|
||||
? t('subscription.billedYearly', {
|
||||
total: `$${getAnnualTotal(tier)}`
|
||||
})
|
||||
: t('subscription.billedMonthly')
|
||||
}}
|
||||
@@ -102,14 +102,18 @@
|
||||
<span
|
||||
class="font-inter text-sm font-normal leading-normal text-foreground"
|
||||
>
|
||||
{{ t('subscription.monthlyCreditsLabel') }}
|
||||
{{
|
||||
currentBillingCycle === 'yearly'
|
||||
? t('subscription.yearlyCreditsLabel')
|
||||
: t('subscription.monthlyCreditsLabel')
|
||||
}}
|
||||
</span>
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<i class="icon-[lucide--component] text-amber-400 text-sm" />
|
||||
<span
|
||||
class="font-inter text-sm font-bold leading-normal text-base-foreground"
|
||||
>
|
||||
{{ tier.credits }}
|
||||
{{ n(getCreditsDisplay(tier)) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -171,7 +175,7 @@
|
||||
<span
|
||||
class="font-inter text-sm font-bold leading-normal text-base-foreground"
|
||||
>
|
||||
{{ tier.videoEstimate }}
|
||||
{{ n(tier.pricing.videoEstimate) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -242,6 +246,7 @@ import Popover from 'primevue/popover'
|
||||
import SelectButton from 'primevue/selectbutton'
|
||||
import type { ToggleButtonPassThroughMethodOptions } from 'primevue/togglebutton'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
@@ -271,15 +276,26 @@ interface BillingCycleOption {
|
||||
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 {
|
||||
id: SubscriptionTier
|
||||
key: TierKey
|
||||
name: string
|
||||
price: Record<BillingCycle, string> & { annualTotal: string }
|
||||
credits: string
|
||||
pricing: TierPricing
|
||||
maxDuration: string
|
||||
customLoRAs: boolean
|
||||
videoEstimate: string
|
||||
isPopular?: boolean
|
||||
}
|
||||
|
||||
@@ -300,49 +316,32 @@ const tiers: PricingTierConfig[] = [
|
||||
id: 'STANDARD',
|
||||
key: 'standard',
|
||||
name: t('subscription.tiers.standard.name'),
|
||||
price: {
|
||||
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'),
|
||||
pricing: TIER_PRICING.standard,
|
||||
maxDuration: t('subscription.maxDuration.standard'),
|
||||
customLoRAs: false,
|
||||
videoEstimate: t('subscription.tiers.standard.benefits.videoEstimate'),
|
||||
isPopular: false
|
||||
},
|
||||
{
|
||||
id: 'CREATOR',
|
||||
key: 'creator',
|
||||
name: t('subscription.tiers.creator.name'),
|
||||
price: {
|
||||
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'),
|
||||
pricing: TIER_PRICING.creator,
|
||||
maxDuration: t('subscription.maxDuration.creator'),
|
||||
customLoRAs: true,
|
||||
videoEstimate: t('subscription.tiers.creator.benefits.videoEstimate'),
|
||||
isPopular: true
|
||||
},
|
||||
{
|
||||
id: 'PRO',
|
||||
key: 'pro',
|
||||
name: t('subscription.tiers.pro.name'),
|
||||
price: {
|
||||
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'),
|
||||
pricing: TIER_PRICING.pro,
|
||||
maxDuration: t('subscription.maxDuration.pro'),
|
||||
customLoRAs: true,
|
||||
videoEstimate: t('subscription.tiers.pro.benefits.videoEstimate'),
|
||||
isPopular: false
|
||||
}
|
||||
]
|
||||
|
||||
const { n } = useI18n()
|
||||
const { getAuthHeader } = useFirebaseAuthStore()
|
||||
const { isActiveSubscription, subscriptionTier } = useSubscription()
|
||||
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-primary-foreground'
|
||||
|
||||
const getPrice = (tier: PricingTierConfig): string =>
|
||||
tier.price[currentBillingCycle.value]
|
||||
const getPrice = (tier: PricingTierConfig): number =>
|
||||
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 authHeader = await getAuthHeader()
|
||||
|
||||
Reference in New Issue
Block a user