[backport cloud/1.35] feat(cloud): yearly pricing (#7581)

Backport of #7572 to `cloud/1.35`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7581-backport-cloud-1-35-feat-cloud-yearly-pricing-2cc6d73d3650814296f1c41377746400)
by [Unito](https://www.unito.io)

Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com>
This commit is contained in:
Comfy Org PR Bot
2025-12-17 13:25:14 +09:00
committed by GitHub
parent 626a7123fe
commit 6eca4aae86
5 changed files with 314 additions and 197 deletions

View File

@@ -235,7 +235,7 @@
--brand-yellow: var(--color-electric-400); --brand-yellow: var(--color-electric-400);
--brand-blue: var(--color-sapphire-700); --brand-blue: var(--color-sapphire-700);
--secondary-background: var(--color-smoke-200); --secondary-background: var(--color-smoke-200);
--secondary-background-hover: var(--color-smoke-200); --secondary-background-hover: var(--color-smoke-400);
--secondary-background-selected: var(--color-smoke-600); --secondary-background-selected: var(--color-smoke-600);
--base-background: var(--color-white); --base-background: var(--color-white);
--primary-background: var(--color-azure-400); --primary-background: var(--color-azure-400);

View File

@@ -1895,7 +1895,7 @@
"comfyCloudLogo": "Comfy Cloud Logo", "comfyCloudLogo": "Comfy Cloud Logo",
"beta": "BETA", "beta": "BETA",
"perMonth": "/ month", "perMonth": "/ month",
"usdPerMonth": "USD / month", "usdPerMonth": "USD / mo",
"renewsDate": "Renews {date}", "renewsDate": "Renews {date}",
"expiresDate": "Expires {date}", "expiresDate": "Expires {date}",
"manageSubscription": "Manage subscription", "manageSubscription": "Manage subscription",
@@ -1917,16 +1917,21 @@
"yourPlanIncludes": "Your plan includes:", "yourPlanIncludes": "Your plan includes:",
"viewMoreDetails": "View more details", "viewMoreDetails": "View more details",
"learnMore": "Learn more", "learnMore": "Learn more",
"billedMonthly": "Billed monthly",
"billedAnnually": "{total} Billed annually",
"monthly": "Monthly",
"yearly": "Yearly",
"messageSupport": "Message support", "messageSupport": "Message support",
"invoiceHistory": "Invoice history", "invoiceHistory": "Invoice history",
"benefits": { "benefits": {
"benefit1": "$10 in monthly credits for Partner Nodes — top up when needed", "benefit1": "$10 in monthly credits for Partner Nodes — top up when needed",
"benefit2": "Up to 30 min runtime per job" "benefit2": "Up to 30 min runtime per job"
}, },
"yearlyDiscount": "20% DISCOUNT",
"tiers": { "tiers": {
"founder": { "founder": {
"name": "Founder's Edition", "name": "Founder's Edition",
"price": "20.00", "price": "20",
"benefits": { "benefits": {
"monthlyCredits": "5,460", "monthlyCredits": "5,460",
"monthlyCreditsLabel": "monthly credits", "monthlyCreditsLabel": "monthly credits",
@@ -1939,7 +1944,11 @@
}, },
"standard": { "standard": {
"name": "Standard", "name": "Standard",
"price": "20.00", "price": {
"monthly": "20",
"yearly": "16",
"annualTotal": "$192"
},
"benefits": { "benefits": {
"monthlyCredits": "4,200", "monthlyCredits": "4,200",
"monthlyCreditsLabel": "monthly credits", "monthlyCreditsLabel": "monthly credits",
@@ -1953,7 +1962,12 @@
}, },
"creator": { "creator": {
"name": "Creator", "name": "Creator",
"price": "35.00", "price": {
"monthly": "35",
"yearly": "28",
"annualTotal": "$336"
},
"benefits": { "benefits": {
"monthlyCredits": "7,400", "monthlyCredits": "7,400",
"monthlyCreditsLabel": "monthly credits", "monthlyCreditsLabel": "monthly credits",
@@ -1967,7 +1981,11 @@
}, },
"pro": { "pro": {
"name": "Pro", "name": "Pro",
"price": "100.00", "price": {
"monthly": "100",
"yearly": "80",
"annualTotal": "$960"
},
"benefits": { "benefits": {
"monthlyCredits": "21,100", "monthlyCredits": "21,100",
"monthlyCreditsLabel": "monthly credits", "monthlyCreditsLabel": "monthly credits",
@@ -1998,7 +2016,7 @@
"description": "Choose the best plan for you", "description": "Choose the best plan for you",
"haveQuestions": "Have questions or wondering about enterprise?", "haveQuestions": "Have questions or wondering about enterprise?",
"contactUs": "Contact us", "contactUs": "Contact us",
"viewEnterprise": "view enterprise", "viewEnterprise": "View enterprise",
"partnerNodesCredits": "Partner nodes pricing", "partnerNodesCredits": "Partner nodes pricing",
"mostPopular": "Most popular", "mostPopular": "Most popular",
"currentPlan": "Current Plan", "currentPlan": "Current Plan",

View File

@@ -1,171 +1,246 @@
<template> <template>
<div class="flex flex-row items-stretch gap-6"> <div class="flex flex-col gap-8">
<div <div class="flex justify-center">
v-for="tier in tiers" <SelectButton
:key="tier.id" v-model="currentBillingCycle"
class="flex-1 flex flex-col rounded-2xl border border-interface-stroke bg-interface-panel-surface shadow-[0_0_12px_rgba(0,0,0,0.1)]" :options="billingCycleOptions"
> option-label="label"
<div class="flex flex-col gap-6 p-8"> option-value="value"
<div class="flex flex-row items-center gap-2"> :allow-empty="false"
<span unstyled
class="font-inter text-base font-bold leading-normal text-base-foreground" :pt="{
> root: {
{{ tier.name }} class: 'flex gap-1 bg-secondary-background rounded-lg p-1.5'
</span> },
<div pcToggleButton: {
v-if="tier.isPopular" root: ({ context }: ToggleButtonPassThroughMethodOptions) => ({
class="rounded-full bg-background px-1 text-xs font-semibold uppercase tracking-wide text-foreground h-[13px] leading-[13px]" class: [
> 'w-36 h-8 rounded-md transition-colors cursor-pointer border-none outline-none ring-0 text-sm font-medium flex items-center justify-center',
{{ t('subscription.mostPopular') }} context.active
</div> ? 'bg-base-foreground text-base-background'
</div> : 'bg-transparent text-muted-foreground hover:bg-secondary-background-hover'
<div class="flex flex-row items-baseline gap-2"> ]
<span }),
class="font-inter text-[32px] font-semibold leading-normal text-base-foreground" label: { class: 'flex items-center gap-2 ' }
> }
${{ tier.price }} }"
</span> >
<span <template #option="{ option }">
class="font-inter text-base font-normal leading-normal text-base-foreground" <div class="flex items-center gap-2">
> <span>{{ option.label }}</span>
{{ t('subscription.usdPerMonth') }} <div
</span> v-if="option.value === 'yearly'"
</div> class="bg-primary-background text-white text-[11px] px-1 py-0.5 rounded-full flex items-center font-bold"
</div>
<div class="flex flex-col gap-4 px-8 pb-0 flex-1">
<div class="flex flex-row items-center justify-between">
<span
class="font-inter text-sm font-normal leading-normal text-muted-foreground"
>
{{ 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 }} -20%
</span> </div>
</div> </div>
</div> </template>
</SelectButton>
<div class="flex flex-row items-center justify-between"> </div>
<span class="text-sm font-normal text-muted-foreground"> <div class="flex flex-col xl:flex-row items-stretch gap-6">
{{ t('subscription.maxDurationLabel') }} <div
</span> v-for="tier in tiers"
<span :key="tier.id"
class="font-inter text-sm font-bold leading-normal text-base-foreground" :class="
> cn(
{{ tier.maxDuration }} 'flex-1 flex flex-col rounded-2xl border border-border-default bg-base-background shadow-[0_0_12px_rgba(0,0,0,0.1)]',
</span> tier.isPopular ? 'border-muted-foreground' : ''
</div> )
"
<div class="flex flex-row items-center justify-between"> >
<span class="text-sm font-normal text-muted-foreground"> <div class="p-8 pb-0 flex flex-col gap-8">
{{ t('subscription.gpuLabel') }} <div class="flex flex-row items-center gap-2 justify-between">
</span> <span
<i class="pi pi-check text-xs text-success-foreground" /> class="font-inter text-base font-bold leading-normal text-base-foreground"
</div> >
{{ tier.name }}
<div class="flex flex-row items-center justify-between"> </span>
<span class="text-sm font-normal text-muted-foreground"> <div
{{ t('subscription.addCreditsLabel') }} v-if="tier.isPopular"
</span> class="rounded-full bg-base-foreground px-1.5 text-[11px] font-bold uppercase text-base-background h-5 tracking-tight flex items-center"
<i class="pi pi-check text-xs text-success-foreground" /> >
</div> {{ t('subscription.mostPopular') }}
</div>
<div class="flex flex-row items-center justify-between"> </div>
<span class="text-sm font-normal text-muted-foreground"> <div class="flex flex-col">
{{ t('subscription.customLoRAsLabel') }}
</span>
<i
v-if="tier.customLoRAs"
class="pi pi-check text-xs text-success-foreground"
/>
<i v-else class="pi pi-times text-xs text-muted-foreground" />
</div>
<div class="flex flex-col gap-2">
<div class="flex flex-row items-start justify-between">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<span class="text-sm font-normal text-muted-foreground"> <div class="flex flex-row items-baseline gap-2">
{{ t('subscription.videoEstimateLabel') }}
</span>
<div class="flex flex-row items-center gap-2 opacity-50">
<i
class="pi pi-question-circle text-xs text-muted-foreground"
/>
<span <span
class="text-sm font-normal text-muted-foreground cursor-pointer hover:text-base-foreground" class="font-inter text-[32px] font-semibold leading-normal text-base-foreground"
@click="togglePopover"
> >
{{ t('subscription.videoEstimateHelp') }} <span
v-show="currentBillingCycle === 'yearly'"
class="line-through text-2xl text-muted-foreground"
>
${{ tier.price.monthly }}
</span>
${{ getPrice(tier) }}
</span>
<span
class="font-inter text-xl leading-normal text-base-foreground"
>
{{ t('subscription.usdPerMonth') }}
</span>
</div>
<div class="flex items-center gap-2">
<span class="text-sm text-muted-foreground">
{{
currentBillingCycle === 'yearly'
? t('subscription.billedAnnually', {
total: tier.price.annualTotal
})
: t('subscription.billedMonthly')
}}
</span>
</div>
</div>
</div>
<div class="flex flex-col gap-4 pb-0 flex-1">
<div class="flex flex-row items-center justify-between">
<span
class="font-inter text-sm font-normal leading-normal text-foreground"
>
{{ 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 }}
</span>
</div>
</div>
<div class="flex flex-row items-center justify-between">
<span class="text-sm font-normal text-foreground">
{{ t('subscription.maxDurationLabel') }}
</span>
<span
class="font-inter text-sm font-bold leading-normal text-base-foreground"
>
{{ tier.maxDuration }}
</span>
</div>
<div class="flex flex-row items-center justify-between">
<span class="text-sm font-normal text-foreground">
{{ t('subscription.gpuLabel') }}
</span>
<i class="pi pi-check text-xs text-success-foreground" />
</div>
<div class="flex flex-row items-center justify-between">
<span class="text-sm font-normal text-foreground">
{{ t('subscription.addCreditsLabel') }}
</span>
<i class="pi pi-check text-xs text-success-foreground" />
</div>
<div class="flex flex-row items-center justify-between">
<span class="text-sm font-normal text-foreground">
{{ t('subscription.customLoRAsLabel') }}
</span>
<i
v-if="tier.customLoRAs"
class="pi pi-check text-xs text-success-foreground"
/>
<i v-else class="pi pi-times text-xs text-foreground" />
</div>
<div class="flex flex-col gap-2">
<div class="flex flex-row items-start justify-between">
<div class="flex flex-col gap-2">
<span class="text-sm font-normal text-foreground">
{{ t('subscription.videoEstimateLabel') }}
</span>
<div class="flex flex-row items-center gap-2 group pt-2">
<i
class="pi pi-question-circle text-xs text-muted-foreground group-hover:text-base-foreground"
/>
<span
class="text-sm font-normal text-muted-foreground cursor-pointer group-hover:text-base-foreground"
@click="togglePopover"
>
{{ t('subscription.videoEstimateHelp') }}
</span>
</div>
</div>
<span
class="font-inter text-sm font-bold leading-normal text-base-foreground"
>
{{ tier.videoEstimate }}
</span> </span>
</div> </div>
</div> </div>
<span
class="font-inter text-sm font-bold leading-normal text-base-foreground"
>
{{ tier.videoEstimate }}
</span>
</div> </div>
</div> </div>
</div> <div class="flex flex-col p-8">
<Button
<div class="flex flex-col p-8"> :label="getButtonLabel(tier)"
<Button :severity="getButtonSeverity(tier)"
:label="getButtonLabel(tier)" :disabled="isLoading || isCurrentPlan(tier.key)"
:severity="getButtonSeverity(tier)" :loading="loadingTier === tier.key"
:disabled="isLoading || isCurrentPlan(tier.key)" :class="
:loading="loadingTier === tier.key" cn(
class="h-10 w-full" 'h-10 w-full',
:pt="{ tier.key === 'creator'
label: { ? 'bg-base-foreground border-transparent hover:bg-inverted-background-hover'
class: getButtonTextClass(tier) : 'bg-secondary-background border-transparent hover:bg-secondary-background-hover focus:bg-secondary-background-selected'
} )
}" "
@click="() => handleSubscribe(tier.key)" :pt="{
/> label: {
class: getButtonTextClass(tier)
}
}"
@click="() => handleSubscribe(tier.key)"
/>
</div>
</div> </div>
</div> </div>
<!-- Video Estimate Help Popover -->
<Popover
ref="popover"
append-to="body"
:auto-z-index="true"
:base-z-index="1000"
:dismissable="true"
:close-on-escape="true"
unstyled
:pt="{
root: {
class:
'rounded-lg border border-interface-stroke bg-interface-panel-surface shadow-lg p-4 max-w-xs'
}
}"
>
<div class="flex flex-col gap-2">
<p class="text-sm text-base-foreground">
{{ t('subscription.videoEstimateExplanation') }}
</p>
<a
href="https://cloud.comfy.org/?template=video_wan2_2_14B_fun_camera"
target="_blank"
rel="noopener noreferrer"
class="text-sm text-azure-600 hover:text-azure-400 underline"
>
{{ t('subscription.videoEstimateTryTemplate') }}
</a>
</div>
</Popover>
</div> </div>
<!-- Video Estimate Help Popover -->
<Popover
ref="popover"
append-to="body"
:auto-z-index="true"
:base-z-index="1000"
:dismissable="true"
:close-on-escape="true"
unstyled
:pt="{
root: {
class:
'rounded-lg border border-interface-stroke bg-interface-panel-surface shadow-lg p-4 max-w-xs'
}
}"
>
<div class="flex flex-col gap-2">
<p class="text-sm text-base-foreground">
{{ t('subscription.videoEstimateExplanation') }}
</p>
<a
href="https://cloud.comfy.org/?template=video_wan2_2_14B_fun_camera"
target="_blank"
rel="noopener noreferrer"
class="text-sm text-azure-600 hover:text-azure-400 underline"
>
{{ t('subscription.videoEstimateTryTemplate') }}
</a>
</div>
</Popover>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { cn } from '@comfyorg/tailwind-utils'
import Button from 'primevue/button' import Button from 'primevue/button'
import Popover from 'primevue/popover' import Popover from 'primevue/popover'
import SelectButton from 'primevue/selectbutton'
import type { ToggleButtonPassThroughMethodOptions } from 'primevue/togglebutton'
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions' import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
@@ -182,12 +257,25 @@ import type { components } from '@/types/comfyRegistryTypes'
type SubscriptionTier = components['schemas']['SubscriptionTier'] type SubscriptionTier = components['schemas']['SubscriptionTier']
type TierKey = 'standard' | 'creator' | 'pro' type TierKey = 'standard' | 'creator' | 'pro'
type CheckoutTier = TierKey | `${TierKey}-yearly`
type BillingCycle = 'monthly' | 'yearly'
const getCheckoutTier = (
tierKey: TierKey,
billingCycle: BillingCycle
): CheckoutTier => (billingCycle === 'yearly' ? `${tierKey}-yearly` : tierKey)
interface BillingCycleOption {
label: string
value: BillingCycle
}
interface PricingTierConfig { interface PricingTierConfig {
id: SubscriptionTier id: SubscriptionTier
key: TierKey key: TierKey
name: string name: string
price: string price: Record<BillingCycle, string> & { annualTotal: string }
credits: string credits: string
maxDuration: string maxDuration: string
customLoRAs: boolean customLoRAs: boolean
@@ -195,6 +283,11 @@ interface PricingTierConfig {
isPopular?: boolean isPopular?: boolean
} }
const billingCycleOptions: BillingCycleOption[] = [
{ label: t('subscription.yearly'), value: 'yearly' },
{ label: t('subscription.monthly'), value: 'monthly' }
]
const TIER_TO_KEY: Record<SubscriptionTier, TierKey> = { const TIER_TO_KEY: Record<SubscriptionTier, TierKey> = {
STANDARD: 'standard', STANDARD: 'standard',
CREATOR: 'creator', CREATOR: 'creator',
@@ -207,7 +300,11 @@ const tiers: PricingTierConfig[] = [
id: 'STANDARD', id: 'STANDARD',
key: 'standard', key: 'standard',
name: t('subscription.tiers.standard.name'), name: t('subscription.tiers.standard.name'),
price: t('subscription.tiers.standard.price'), 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'), credits: t('subscription.credits.standard'),
maxDuration: t('subscription.maxDuration.standard'), maxDuration: t('subscription.maxDuration.standard'),
customLoRAs: false, customLoRAs: false,
@@ -218,7 +315,11 @@ const tiers: PricingTierConfig[] = [
id: 'CREATOR', id: 'CREATOR',
key: 'creator', key: 'creator',
name: t('subscription.tiers.creator.name'), name: t('subscription.tiers.creator.name'),
price: t('subscription.tiers.creator.price'), 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'), credits: t('subscription.credits.creator'),
maxDuration: t('subscription.maxDuration.creator'), maxDuration: t('subscription.maxDuration.creator'),
customLoRAs: true, customLoRAs: true,
@@ -229,7 +330,11 @@ const tiers: PricingTierConfig[] = [
id: 'PRO', id: 'PRO',
key: 'pro', key: 'pro',
name: t('subscription.tiers.pro.name'), name: t('subscription.tiers.pro.name'),
price: t('subscription.tiers.pro.price'), 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'), credits: t('subscription.credits.pro'),
maxDuration: t('subscription.maxDuration.pro'), maxDuration: t('subscription.maxDuration.pro'),
customLoRAs: true, customLoRAs: true,
@@ -246,6 +351,7 @@ const { wrapWithErrorHandlingAsync } = useErrorHandling()
const isLoading = ref(false) const isLoading = ref(false)
const loadingTier = ref<TierKey | null>(null) const loadingTier = ref<TierKey | null>(null)
const popover = ref() const popover = ref()
const currentBillingCycle = ref<BillingCycle>('yearly')
const currentTierKey = computed<TierKey | null>(() => const currentTierKey = computed<TierKey | null>(() =>
subscriptionTier.value ? TIER_TO_KEY[subscriptionTier.value] : null subscriptionTier.value ? TIER_TO_KEY[subscriptionTier.value] : null
@@ -274,17 +380,21 @@ const getButtonSeverity = (tier: PricingTierConfig): 'primary' | 'secondary' =>
const getButtonTextClass = (tier: PricingTierConfig): string => const getButtonTextClass = (tier: PricingTierConfig): string =>
tier.key === 'creator' tier.key === 'creator'
? 'font-inter text-sm font-bold leading-normal text-white' ? '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 =>
tier.price[currentBillingCycle.value]
const initiateCheckout = async (tierKey: TierKey) => { const initiateCheckout = async (tierKey: TierKey) => {
const authHeader = await getAuthHeader() const authHeader = await getAuthHeader()
if (!authHeader) { if (!authHeader) {
throw new FirebaseAuthStoreError(t('toastMessages.userNotAuthenticated')) throw new FirebaseAuthStoreError(t('toastMessages.userNotAuthenticated'))
} }
const checkoutTier = getCheckoutTier(tierKey, currentBillingCycle.value)
const response = await fetch( const response = await fetch(
`${getComfyApiBaseUrl()}/customers/cloud-subscription-checkout/${tierKey}`, `${getComfyApiBaseUrl()}/customers/cloud-subscription-checkout/${checkoutTier}`,
{ {
method: 'POST', method: 'POST',
headers: { ...authHeader, 'Content-Type': 'application/json' } headers: { ...authHeader, 'Content-Type': 'application/json' }

View File

@@ -1,45 +1,33 @@
<template> <template>
<div <div
v-if="showCustomPricingTable" v-if="showCustomPricingTable"
class="flex flex-col gap-6 rounded-[24px] border border-interface-stroke bg-[var(--p-dialog-background)] p-4 shadow-[0_25px_80px_rgba(5,6,12,0.45)] md:p-6" class="relative flex flex-col p-4 pt-8 md:p-16 !overflow-y-auto h-full gap-8"
> >
<div <Button
class="flex flex-col gap-6 md:flex-row md:items-start md:justify-between" :pt="{
> icon: { class: 'text-xl' }
<div class="flex flex-col gap-2 text-left md:max-w-2xl"> }"
<div icon="pi pi-times"
class="inline-flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.3em] text-text-secondary" text
> rounded
{{ $t('subscription.required.title') }} class="shrink-0 text-text-secondary hover:bg-white/10 absolute right-2.5 top-2.5"
<CloudBadge :aria-label="$t('g.close')"
reverse-order @click="handleClose"
no-padding />
background-color="var(--p-dialog-background)" <div class="text-center">
use-subscription <h2 class="text-xl lg:text-2xl text-muted-foreground m-0">
/> {{ $t('subscription.description') }}
</div> </h2>
<div class="text-3xl font-semibold leading-tight md:text-4xl">
{{ $t('subscription.description') }}
</div>
</div>
<Button
icon="pi pi-times"
text
rounded
class="h-10 w-10 shrink-0 text-text-secondary hover:bg-white/10"
:aria-label="$t('g.close')"
@click="handleClose"
/>
</div> </div>
<PricingTable class="flex-1" /> <PricingTable class="flex-1" />
<!-- Contact and Enterprise Links --> <!-- Contact and Enterprise Links -->
<div class="flex flex-col items-center"> <div class="flex flex-col items-center gap-2">
<p class="text-sm text-text-secondary"> <p class="text-sm text-text-secondary m-0">
{{ $t('subscription.haveQuestions') }} {{ $t('subscription.haveQuestions') }}
</p> </p>
<div class="flex items-center gap-2"> <div class="flex items-center gap-1.5">
<Button <Button
:label="$t('subscription.contactUs')" :label="$t('subscription.contactUs')"
text text
@@ -95,7 +83,7 @@
<div> <div>
<div class="flex flex-col gap-6"> <div class="flex flex-col gap-6">
<div class="inline-flex items-center gap-2"> <div class="inline-flex items-center gap-2">
<div class="text-sm text-muted text-text-primary"> <div class="text-sm text-text-primary">
{{ $t('subscription.required.title') }} {{ $t('subscription.required.title') }}
</div> </div>
<CloudBadge <CloudBadge

View File

@@ -23,13 +23,14 @@ export const useSubscriptionDialog = () => {
onClose: hide onClose: hide
}, },
dialogComponentProps: { dialogComponentProps: {
style: 'width: min(1200px, 95vw); max-height: 90vh;', style: 'width: min(1328px, 95vw); max-height: 90vh;',
pt: { pt: {
root: { root: {
class: '!rounded-[32px] overflow-visible' class: 'rounded-2xl bg-transparent'
}, },
content: { content: {
class: '!p-0 bg-transparent' class:
'!p-0 rounded-2xl border border-border-default bg-base-background/60 backdrop-blur-md shadow-[0_25px_80px_rgba(5,6,12,0.45)]'
} }
} }
} }