From c1979ee074fcd7dd24b266ad6cbfeeeb086cfef3 Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Tue, 9 Dec 2025 20:30:56 -0800 Subject: [PATCH] style: redesign TopUpCredits dialog (#7305) Redesigned the TopUpCredits dialog to match Figma design specifications with proper layout, typography, colors and selection states. Updated dialog to use workflow-aware messaging, removed header, applied design system tokens, and integrated subscription renewal dates. Modified credit packages to use clean USD amounts with realistic video estimates and fixed button disabled states to show blue with 30% opacity per Figma design. | Before | After | | --------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | Screenshot from 2025-12-09
18-08-21 | Screenshot from 2025-12-09
18-06-23 | --- src/components/common/UserCredit.vue | 24 +++++++- .../content/TopUpCreditsDialogContent.vue | 61 +++++++++++-------- .../content/credit/CreditTopUpOption.vue | 24 ++++---- src/locales/en/main.json | 4 +- src/services/dialogService.ts | 5 +- .../content/credit/CreditTopUpOption.test.ts | 12 ++-- 6 files changed, 79 insertions(+), 51 deletions(-) diff --git a/src/components/common/UserCredit.vue b/src/components/common/UserCredit.vue index e3032fc4f36..04424baf0a5 100644 --- a/src/components/common/UserCredit.vue +++ b/src/components/common/UserCredit.vue @@ -7,7 +7,12 @@
- + @@ -32,8 +39,9 @@ import { formatCreditsFromCents } from '@/base/credits/comfyCredits' import { useFeatureFlags } from '@/composables/useFeatureFlags' import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore' -const { textClass } = defineProps<{ +const { textClass, showCreditsOnly } = defineProps<{ textClass?: string + showCreditsOnly?: boolean }>() const authStore = useFirebaseAuthStore() @@ -50,4 +58,14 @@ const formattedBalance = computed(() => { }) return `${amount} ${t('credits.credits')}` }) + +const formattedCreditsOnly = computed(() => { + // Backend returns cents despite the *_micros naming convention. + const cents = authStore.balance?.amount_micros ?? 0 + const amount = formatCreditsFromCents({ + cents, + locale: locale.value + }) + return amount +}) diff --git a/src/components/dialog/content/TopUpCreditsDialogContent.vue b/src/components/dialog/content/TopUpCreditsDialogContent.vue index 12ef94ca015..3ce3484f72c 100644 --- a/src/components/dialog/content/TopUpCreditsDialogContent.vue +++ b/src/components/dialog/content/TopUpCreditsDialogContent.vue @@ -1,35 +1,43 @@ @@ -38,6 +36,10 @@ defineEmits<{ const { locale } = useI18n() const formattedCredits = computed(() => { - return formatCredits({ value: credits, locale: locale.value }) + return formatCredits({ + value: credits, + locale: locale.value, + numberOptions: { minimumFractionDigits: 0, maximumFractionDigits: 0 } + }) }) diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 168fac99ea9..4ea871ca021 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -1836,6 +1836,8 @@ "seeDetails": "See details", "topUp": "Top Up", "addMoreCredits": "Add more credits", + "addMoreCreditsToRun": "Add more credits to run", + "insufficientWorkflowMessage": "You don't have enough credits to run this workflow.", "creditsDescription": "Credits are used to run workflows or partner nodes.", "howManyCredits": "How many credits would you like to add?", "videosEstimate": "~{count} videos*", @@ -1858,7 +1860,7 @@ "accountInitialized": "Account initialized", "unified": { "message": "Credits have been unified", - "tooltip": "We've unified payments across Comfy. Everything now runs on Comfy Credits:\n- Partner Nodes (formerly API nodes)\n- Cloud workflows\n\nYour existing Partner node balance has been converted into credits.\nLearn more here." + "tooltip": "We've unified payments across Comfy. Everything now runs on Comfy Credits:\n- Partner Nodes (formerly API nodes)\n- Cloud workflows\n\nYour existing Partner node balance has been converted into credits." } }, "subscription": { diff --git a/src/services/dialogService.ts b/src/services/dialogService.ts index 13e7eebf635..23f1832a80c 100644 --- a/src/services/dialogService.ts +++ b/src/services/dialogService.ts @@ -386,11 +386,12 @@ export const useDialogService = () => { return dialogStore.showDialog({ key: 'top-up-credits', component: TopUpCreditsDialogContent, - headerComponent: ComfyOrgHeader, props: options, dialogComponentProps: { + headless: true, pt: { - header: { class: 'p-3!' } + header: { class: 'p-0! hidden' }, + content: { class: 'p-0! m-0!' } } } }) diff --git a/tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts b/tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts index 72ad9ecd7e4..7faf432e729 100644 --- a/tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts +++ b/tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts @@ -32,16 +32,12 @@ describe('CreditTopUpOption', () => { expect(wrapper.text()).toContain('~500 videos*') }) - it('applies selected styling when selected', () => { - const wrapper = mountOption({ selected: true }) - expect(wrapper.find('div').classes()).toContain('bg-surface-secondary') - expect(wrapper.find('div').classes()).toContain('border-primary') - }) - it('applies unselected styling when not selected', () => { const wrapper = mountOption({ selected: false }) - expect(wrapper.find('div').classes()).toContain('bg-surface-tertiary') - expect(wrapper.find('div').classes()).toContain('border-border-primary') + expect(wrapper.find('div').classes()).toContain( + 'bg-component-node-disabled' + ) + expect(wrapper.find('div').classes()).toContain('border-transparent') }) it('emits select event when clicked', async () => {