From 356ebe538f48ae6af88225f3177b5343b2bba6bd 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 e3032fc4f..04424baf0 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 12ef94ca0..3ce3484f7 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 36251da0e..225491897 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -1847,6 +1847,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*", @@ -1869,7 +1871,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 13e7eebf6..23f1832a8 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 72ad9ecd7..7faf432e7 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 () => {