mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 03:01:54 +00:00
[backport cloud/1.34] feat: update subscription panel with tier-based design and improved UX (#7312)
Backport of #7307 to `cloud/1.34` Automatically created by backport workflow. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7312-backport-cloud-1-34-feat-update-subscription-panel-with-tier-based-design-and-improved-2c56d73d365081e28400d30170266e85) by [Unito](https://www.unito.io) Co-authored-by: Christian Byrne <cbyrne@comfy.org> Co-authored-by: Alexander Brown <drjkl@comfy.org>
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<TabPanel value="Credits" class="credits-container h-full">
|
<TabPanel value="Credits" class="credits-container h-full">
|
||||||
|
<!-- Legacy Design -->
|
||||||
<div class="flex h-full flex-col">
|
<div class="flex h-full flex-col">
|
||||||
<h2 class="mb-2 text-2xl font-bold">
|
<h2 class="mb-2 text-2xl font-bold">
|
||||||
{{ $t('credits.credits') }}
|
{{ $t('credits.credits') }}
|
||||||
@@ -1869,7 +1869,7 @@
|
|||||||
"comfyCloud": "Comfy Cloud",
|
"comfyCloud": "Comfy Cloud",
|
||||||
"comfyCloudLogo": "Comfy Cloud Logo",
|
"comfyCloudLogo": "Comfy Cloud Logo",
|
||||||
"beta": "BETA",
|
"beta": "BETA",
|
||||||
"perMonth": "USD / month",
|
"perMonth": "/ month",
|
||||||
"renewsDate": "Renews {date}",
|
"renewsDate": "Renews {date}",
|
||||||
"refreshesOn": "Refreshes to ${monthlyCreditBonusUsd} on {date}",
|
"refreshesOn": "Refreshes to ${monthlyCreditBonusUsd} on {date}",
|
||||||
"expiresDate": "Expires {date}",
|
"expiresDate": "Expires {date}",
|
||||||
@@ -1884,6 +1884,10 @@
|
|||||||
"monthlyBonusDescription": "Monthly credit bonus",
|
"monthlyBonusDescription": "Monthly credit bonus",
|
||||||
"prepaidDescription": "Pre-paid credits",
|
"prepaidDescription": "Pre-paid credits",
|
||||||
"prepaidCreditsInfo": "Pre-paid credits expire after 1 year from purchase date.",
|
"prepaidCreditsInfo": "Pre-paid credits expire after 1 year from purchase date.",
|
||||||
|
"creditsRemainingThisMonth": "Credits remaining for this month",
|
||||||
|
"creditsYouveAdded": "Credits you've added",
|
||||||
|
"monthlyCreditsInfo": "These credits refresh monthly and don't roll over",
|
||||||
|
"viewMoreDetailsPlans": "View more details about plans & pricing",
|
||||||
"nextBillingCycle": "next billing cycle",
|
"nextBillingCycle": "next billing cycle",
|
||||||
"yourPlanIncludes": "Your plan includes:",
|
"yourPlanIncludes": "Your plan includes:",
|
||||||
"viewMoreDetails": "View more details",
|
"viewMoreDetails": "View more details",
|
||||||
@@ -1894,6 +1898,55 @@
|
|||||||
"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"
|
||||||
},
|
},
|
||||||
|
"tiers": {
|
||||||
|
"founder": {
|
||||||
|
"name": "Founder's Edition Standard",
|
||||||
|
"price": "20.00",
|
||||||
|
"benefits": {
|
||||||
|
"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 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": {
|
||||||
|
"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 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": {
|
"required": {
|
||||||
"title": "Subscribe to",
|
"title": "Subscribe to",
|
||||||
"waitingForSubscription": "Complete your subscription in the new tab. We'll automatically detect when you're done!",
|
"waitingForSubscription": "Complete your subscription in the new tab. We'll automatically detect when you're done!",
|
||||||
|
|||||||
@@ -1,42 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-start gap-1 self-stretch">
|
<div class="flex flex-col items-start gap-0 self-stretch">
|
||||||
<div class="flex items-start gap-2">
|
<div class="flex items-center gap-2 py-2">
|
||||||
<i class="pi pi-check mt-1 text-xs text-text-primary" />
|
<i class="pi pi-check text-xs text-text-primary" />
|
||||||
<span class="text-sm text-text-primary">
|
<span class="text-sm text-text-primary">
|
||||||
{{ $t('subscription.benefits.benefit1') }}
|
{{ $t('subscription.benefits.benefit1') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-start gap-2">
|
<div class="flex items-center gap-2 py-2">
|
||||||
<i class="pi pi-check mt-1 text-xs text-text-primary" />
|
<i class="pi pi-check text-xs text-text-primary" />
|
||||||
<span class="text-sm text-text-primary">
|
<span class="text-sm text-text-primary">
|
||||||
{{ $t('subscription.benefits.benefit2') }}
|
{{ $t('subscription.benefits.benefit2') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
|
||||||
:label="$t('subscription.viewMoreDetails')"
|
|
||||||
text
|
|
||||||
icon="pi pi-external-link"
|
|
||||||
icon-pos="left"
|
|
||||||
class="flex h-8 min-h-6 py-2 px-0 items-center gap-2 rounded text-text-secondary"
|
|
||||||
:pt="{
|
|
||||||
icon: {
|
|
||||||
class: 'text-xs text-text-secondary'
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
class: 'text-sm text-text-secondary'
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
@click="handleViewMoreDetails"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts"></script>
|
||||||
import Button from 'primevue/button'
|
|
||||||
|
|
||||||
const handleViewMoreDetails = () => {
|
|
||||||
window.open('https://www.comfy.org/cloud/pricing', '_blank')
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -19,9 +19,12 @@
|
|||||||
<div class="rounded-2xl border border-interface-stroke p-6">
|
<div class="rounded-2xl border border-interface-stroke p-6">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div>
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class="text-sm font-bold text-text-primary">
|
||||||
|
{{ tierName }}
|
||||||
|
</div>
|
||||||
<div class="flex items-baseline gap-1 font-inter font-semibold">
|
<div class="flex items-baseline gap-1 font-inter font-semibold">
|
||||||
<span class="text-2xl">{{ formattedMonthlyPrice }}</span>
|
<span class="text-2xl">${{ tierPrice }}</span>
|
||||||
<span class="text-base">{{
|
<span class="text-base">{{
|
||||||
$t('subscription.perMonth')
|
$t('subscription.perMonth')
|
||||||
}}</span>
|
}}</span>
|
||||||
@@ -59,7 +62,7 @@
|
|||||||
class: 'text-text-primary'
|
class: 'text-text-primary'
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@click="manageSubscription"
|
@click="showSubscriptionDialog"
|
||||||
/>
|
/>
|
||||||
<SubscribeButton
|
<SubscribeButton
|
||||||
v-else
|
v-else
|
||||||
@@ -75,17 +78,6 @@
|
|||||||
<div class="grid grid-cols-1 gap-6 pt-9 lg:grid-cols-2">
|
<div class="grid grid-cols-1 gap-6 pt-9 lg:grid-cols-2">
|
||||||
<div class="flex flex-col flex-1">
|
<div class="flex flex-col flex-1">
|
||||||
<div class="flex flex-col gap-3">
|
<div class="flex flex-col gap-3">
|
||||||
<div class="flex flex-col">
|
|
||||||
<div class="text-sm">
|
|
||||||
{{ $t('subscription.partnerNodesBalance') }}
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<div class="text-sm text-muted">
|
|
||||||
{{ $t('subscription.partnerNodesDescription') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
@@ -112,7 +104,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="text-sm text-text-secondary">
|
<div class="text-sm text-muted">
|
||||||
{{ $t('subscription.totalCredits') }}
|
{{ $t('subscription.totalCredits') }}
|
||||||
</div>
|
</div>
|
||||||
<Skeleton
|
<Skeleton
|
||||||
@@ -133,12 +125,18 @@
|
|||||||
width="3rem"
|
width="3rem"
|
||||||
height="1rem"
|
height="1rem"
|
||||||
/>
|
/>
|
||||||
<div v-else class="text-sm text-text-secondary font-bold">
|
<div
|
||||||
|
v-else
|
||||||
|
class="text-sm font-bold w-12 shrink-0 text-left text-muted"
|
||||||
|
>
|
||||||
{{ monthlyBonusCredits }}
|
{{ monthlyBonusCredits }}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1 min-w-0">
|
||||||
<div class="text-sm text-text-secondary">
|
<div
|
||||||
{{ $t('subscription.monthlyBonusDescription') }}
|
class="text-sm truncate text-muted"
|
||||||
|
:title="$t('subscription.creditsRemainingThisMonth')"
|
||||||
|
>
|
||||||
|
{{ $t('subscription.creditsRemainingThisMonth') }}
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
v-tooltip="refreshTooltip"
|
v-tooltip="refreshTooltip"
|
||||||
@@ -146,7 +144,7 @@
|
|||||||
text
|
text
|
||||||
rounded
|
rounded
|
||||||
size="small"
|
size="small"
|
||||||
class="h-4 w-4"
|
class="h-4 w-4 shrink-0"
|
||||||
:pt="{
|
:pt="{
|
||||||
icon: {
|
icon: {
|
||||||
class: 'text-text-secondary text-xs'
|
class: 'text-text-secondary text-xs'
|
||||||
@@ -161,12 +159,18 @@
|
|||||||
width="3rem"
|
width="3rem"
|
||||||
height="1rem"
|
height="1rem"
|
||||||
/>
|
/>
|
||||||
<div v-else class="text-sm text-text-secondary font-bold">
|
<div
|
||||||
|
v-else
|
||||||
|
class="text-sm font-bold w-12 shrink-0 text-left text-muted"
|
||||||
|
>
|
||||||
{{ prepaidCredits }}
|
{{ prepaidCredits }}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1 min-w-0">
|
||||||
<div class="text-sm text-text-secondary">
|
<div
|
||||||
{{ $t('subscription.prepaidDescription') }}
|
class="text-sm truncate text-muted"
|
||||||
|
:title="$t('subscription.creditsYouveAdded')"
|
||||||
|
>
|
||||||
|
{{ $t('subscription.creditsYouveAdded') }}
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
v-tooltip="$t('subscription.prepaidCreditsInfo')"
|
v-tooltip="$t('subscription.prepaidCreditsInfo')"
|
||||||
@@ -174,7 +178,7 @@
|
|||||||
text
|
text
|
||||||
rounded
|
rounded
|
||||||
size="small"
|
size="small"
|
||||||
class="h-4 w-4"
|
class="h-4 w-4 shrink-0"
|
||||||
:pt="{
|
:pt="{
|
||||||
icon: {
|
icon: {
|
||||||
class: 'text-text-secondary text-xs'
|
class: 'text-text-secondary text-xs'
|
||||||
@@ -190,8 +194,7 @@
|
|||||||
href="https://platform.comfy.org/profile/usage"
|
href="https://platform.comfy.org/profile/usage"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="text-sm text-text-secondary underline hover:text-text-secondary"
|
class="text-sm underline text-center text-muted"
|
||||||
style="text-decoration: underline"
|
|
||||||
>
|
>
|
||||||
{{ $t('subscription.viewUsageHistory') }}
|
{{ $t('subscription.viewUsageHistory') }}
|
||||||
</a>
|
</a>
|
||||||
@@ -216,14 +219,47 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2 flex-1">
|
<div class="flex flex-col gap-2 flex-1">
|
||||||
<div class="text-sm">
|
<div class="text-sm text-text-primary">
|
||||||
{{ $t('subscription.yourPlanIncludes') }}
|
{{ $t('subscription.yourPlanIncludes') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SubscriptionBenefits />
|
<div class="flex flex-col gap-0">
|
||||||
|
<div
|
||||||
|
v-for="benefit in tierBenefits"
|
||||||
|
:key="benefit.key"
|
||||||
|
class="flex items-center gap-2 py-2"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
v-if="benefit.type === 'feature'"
|
||||||
|
class="pi pi-check text-xs text-text-primary"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
v-else-if="benefit.type === 'metric' && benefit.value"
|
||||||
|
class="text-sm font-normal whitespace-nowrap text-text-primary"
|
||||||
|
>
|
||||||
|
{{ benefit.value }}
|
||||||
|
</span>
|
||||||
|
<span class="text-sm text-muted">
|
||||||
|
{{ benefit.label }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- View More Details - Outside main content -->
|
||||||
|
<div class="flex items-center gap-2 py-4">
|
||||||
|
<i class="pi pi-external-link text-muted"></i>
|
||||||
|
<a
|
||||||
|
href="https://www.comfy.org/cloud/pricing"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="text-sm underline hover:opacity-80 text-muted"
|
||||||
|
>
|
||||||
|
{{ $t('subscription.viewMoreDetailsPlans') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -307,28 +343,79 @@
|
|||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import Skeleton from 'primevue/skeleton'
|
import Skeleton from 'primevue/skeleton'
|
||||||
import TabPanel from 'primevue/tabpanel'
|
import TabPanel from 'primevue/tabpanel'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import CloudBadge from '@/components/topbar/CloudBadge.vue'
|
import CloudBadge from '@/components/topbar/CloudBadge.vue'
|
||||||
import { useExternalLink } from '@/composables/useExternalLink'
|
import { useExternalLink } from '@/composables/useExternalLink'
|
||||||
import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'
|
import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'
|
||||||
import SubscriptionBenefits from '@/platform/cloud/subscription/components/SubscriptionBenefits.vue'
|
|
||||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||||
import { useSubscriptionActions } from '@/platform/cloud/subscription/composables/useSubscriptionActions'
|
import { useSubscriptionActions } from '@/platform/cloud/subscription/composables/useSubscriptionActions'
|
||||||
import { useSubscriptionCredits } from '@/platform/cloud/subscription/composables/useSubscriptionCredits'
|
import { useSubscriptionCredits } from '@/platform/cloud/subscription/composables/useSubscriptionCredits'
|
||||||
|
import { useSubscriptionDialog } from '@/platform/cloud/subscription/composables/useSubscriptionDialog'
|
||||||
import { cn } from '@/utils/tailwindUtil'
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
|
|
||||||
const { buildDocsUrl } = useExternalLink()
|
const { buildDocsUrl } = useExternalLink()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isActiveSubscription,
|
isActiveSubscription,
|
||||||
isCancelled,
|
isCancelled,
|
||||||
formattedRenewalDate,
|
formattedRenewalDate,
|
||||||
formattedEndDate,
|
formattedEndDate,
|
||||||
formattedMonthlyPrice,
|
|
||||||
manageSubscription,
|
|
||||||
handleInvoiceHistory
|
handleInvoiceHistory
|
||||||
} = useSubscription()
|
} = 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'))
|
||||||
|
|
||||||
|
// Tier benefits for v-for loop
|
||||||
|
type BenefitType = 'metric' | 'feature'
|
||||||
|
|
||||||
|
interface Benefit {
|
||||||
|
key: string
|
||||||
|
type: BenefitType
|
||||||
|
label: string
|
||||||
|
value?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const tierBenefits = computed(() => {
|
||||||
|
const baseBenefits: Benefit[] = [
|
||||||
|
{
|
||||||
|
key: 'monthlyCredits',
|
||||||
|
type: 'metric',
|
||||||
|
value: t('subscription.tiers.creator.benefits.monthlyCredits'),
|
||||||
|
label: t('subscription.tiers.creator.benefits.monthlyCreditsLabel')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'maxDuration',
|
||||||
|
type: 'metric',
|
||||||
|
value: t('subscription.tiers.creator.benefits.maxDuration'),
|
||||||
|
label: t('subscription.tiers.creator.benefits.maxDurationLabel')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'gpu',
|
||||||
|
type: 'feature',
|
||||||
|
label: t('subscription.tiers.creator.benefits.gpuLabel')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'addCredits',
|
||||||
|
type: 'feature',
|
||||||
|
label: t('subscription.tiers.creator.benefits.addCreditsLabel')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'customLoRAs',
|
||||||
|
type: 'feature',
|
||||||
|
label: t('subscription.tiers.creator.benefits.customLoRAsLabel')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return baseBenefits
|
||||||
|
})
|
||||||
|
|
||||||
const { totalCredits, monthlyBonusCredits, prepaidCredits, isLoadingBalance } =
|
const { totalCredits, monthlyBonusCredits, prepaidCredits, isLoadingBalance } =
|
||||||
useSubscriptionCredits()
|
useSubscriptionCredits()
|
||||||
|
|
||||||
|
|||||||
@@ -9,16 +9,20 @@ import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
|||||||
*/
|
*/
|
||||||
export function useSubscriptionCredits() {
|
export function useSubscriptionCredits() {
|
||||||
const authStore = useFirebaseAuthStore()
|
const authStore = useFirebaseAuthStore()
|
||||||
const { t, locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
|
|
||||||
const formatBalance = (maybeCents?: number) => {
|
const formatBalance = (maybeCents?: number) => {
|
||||||
// Backend returns cents despite the *_micros naming convention.
|
// Backend returns cents despite the *_micros naming convention.
|
||||||
const cents = maybeCents ?? 0
|
const cents = maybeCents ?? 0
|
||||||
const amount = formatCreditsFromCents({
|
const amount = formatCreditsFromCents({
|
||||||
cents,
|
cents,
|
||||||
locale: locale.value
|
locale: locale.value,
|
||||||
|
numberOptions: {
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
maximumFractionDigits: 0
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return `${amount} ${t('credits.credits')}`
|
return amount
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalCredits = computed(() =>
|
const totalCredits = computed(() =>
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export function useSettingUI(
|
|||||||
children: []
|
children: []
|
||||||
},
|
},
|
||||||
component: defineAsyncComponent(
|
component: defineAsyncComponent(
|
||||||
() => import('@/components/dialog/content/setting/CreditsPanel.vue')
|
() => import('@/components/dialog/content/setting/LegacyCreditsPanel.vue')
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,22 +84,22 @@ describe('useSubscriptionCredits', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('totalCredits', () => {
|
describe('totalCredits', () => {
|
||||||
it('should return "0.00 Credits" when balance is null', () => {
|
it('should return "0" when balance is null', () => {
|
||||||
authStore.balance = null
|
authStore.balance = null
|
||||||
const { totalCredits } = useSubscriptionCredits()
|
const { totalCredits } = useSubscriptionCredits()
|
||||||
expect(totalCredits.value).toBe('0.00 Credits')
|
expect(totalCredits.value).toBe('0')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return "0.00 Credits" when amount_micros is missing', () => {
|
it('should return "0" when amount_micros is missing', () => {
|
||||||
authStore.balance = {} as GetCustomerBalanceResponse
|
authStore.balance = {} as GetCustomerBalanceResponse
|
||||||
const { totalCredits } = useSubscriptionCredits()
|
const { totalCredits } = useSubscriptionCredits()
|
||||||
expect(totalCredits.value).toBe('0.00 Credits')
|
expect(totalCredits.value).toBe('0')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should format amount_micros correctly', () => {
|
it('should format amount_micros correctly', () => {
|
||||||
authStore.balance = { amount_micros: 100 } as GetCustomerBalanceResponse
|
authStore.balance = { amount_micros: 100 } as GetCustomerBalanceResponse
|
||||||
const { totalCredits } = useSubscriptionCredits()
|
const { totalCredits } = useSubscriptionCredits()
|
||||||
expect(totalCredits.value).toBe('211.00 Credits')
|
expect(totalCredits.value).toBe('211')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle formatting errors by throwing', async () => {
|
it('should handle formatting errors by throwing', async () => {
|
||||||
@@ -116,10 +116,10 @@ describe('useSubscriptionCredits', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('monthlyBonusCredits', () => {
|
describe('monthlyBonusCredits', () => {
|
||||||
it('should return "0.00 Credits" when cloud_credit_balance_micros is missing', () => {
|
it('should return "0" when cloud_credit_balance_micros is missing', () => {
|
||||||
authStore.balance = {} as GetCustomerBalanceResponse
|
authStore.balance = {} as GetCustomerBalanceResponse
|
||||||
const { monthlyBonusCredits } = useSubscriptionCredits()
|
const { monthlyBonusCredits } = useSubscriptionCredits()
|
||||||
expect(monthlyBonusCredits.value).toBe('0.00 Credits')
|
expect(monthlyBonusCredits.value).toBe('0')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should format cloud_credit_balance_micros correctly', () => {
|
it('should format cloud_credit_balance_micros correctly', () => {
|
||||||
@@ -127,15 +127,15 @@ describe('useSubscriptionCredits', () => {
|
|||||||
cloud_credit_balance_micros: 200
|
cloud_credit_balance_micros: 200
|
||||||
} as GetCustomerBalanceResponse
|
} as GetCustomerBalanceResponse
|
||||||
const { monthlyBonusCredits } = useSubscriptionCredits()
|
const { monthlyBonusCredits } = useSubscriptionCredits()
|
||||||
expect(monthlyBonusCredits.value).toBe('422.00 Credits')
|
expect(monthlyBonusCredits.value).toBe('422')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('prepaidCredits', () => {
|
describe('prepaidCredits', () => {
|
||||||
it('should return "0.00 Credits" when prepaid_balance_micros is missing', () => {
|
it('should return "0" when prepaid_balance_micros is missing', () => {
|
||||||
authStore.balance = {} as GetCustomerBalanceResponse
|
authStore.balance = {} as GetCustomerBalanceResponse
|
||||||
const { prepaidCredits } = useSubscriptionCredits()
|
const { prepaidCredits } = useSubscriptionCredits()
|
||||||
expect(prepaidCredits.value).toBe('0.00 Credits')
|
expect(prepaidCredits.value).toBe('0')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should format prepaid_balance_micros correctly', () => {
|
it('should format prepaid_balance_micros correctly', () => {
|
||||||
@@ -143,7 +143,7 @@ describe('useSubscriptionCredits', () => {
|
|||||||
prepaid_balance_micros: 300
|
prepaid_balance_micros: 300
|
||||||
} as GetCustomerBalanceResponse
|
} as GetCustomerBalanceResponse
|
||||||
const { prepaidCredits } = useSubscriptionCredits()
|
const { prepaidCredits } = useSubscriptionCredits()
|
||||||
expect(prepaidCredits.value).toBe('633.00 Credits')
|
expect(prepaidCredits.value).toBe('633')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user