mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-06 21:50:05 +00:00
update user profile dropdown (#6475)
## Summary update user profile dropdown ## Screenshots 1. Subscribe to run button and Unsubscribe user panel <img width="433" height="480" alt="image" src="https://github.com/user-attachments/assets/bb859481-6405-44df-85ec-9935599c4be0" /> 2. Subscribed User: <img width="395" height="479" alt="image" src="https://github.com/user-attachments/assets/683de2c0-8090-4e9a-ac4e-d211fcee8921" /> 3. OSS: <img width="392" height="480" alt="image" src="https://github.com/user-attachments/assets/7d684c1a-8dee-48dd-8e7f-3c98bd98104d" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6475-update-user-profile-dropdown-29d6d73d365081ff9e14f9355a9a3bb7) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
@@ -79,9 +79,11 @@ vi.mock('@/stores/firebaseAuthStore', () => ({
|
||||
}))
|
||||
|
||||
// Mock the useSubscription composable
|
||||
const mockFetchStatus = vi.fn().mockResolvedValue(undefined)
|
||||
vi.mock('@/platform/cloud/subscription/composables/useSubscription', () => ({
|
||||
useSubscription: vi.fn(() => ({
|
||||
isActiveSubscription: vi.fn().mockReturnValue(true)
|
||||
isActiveSubscription: { value: true },
|
||||
fetchStatus: mockFetchStatus
|
||||
}))
|
||||
}))
|
||||
|
||||
@@ -105,6 +107,15 @@ vi.mock('@/components/common/UserCredit.vue', () => ({
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('@/platform/cloud/subscription/components/SubscribeButton.vue', () => ({
|
||||
default: {
|
||||
name: 'SubscribeButtonMock',
|
||||
render() {
|
||||
return h('div', 'Subscribe Button')
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
describe('CurrentUserPopover', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
@@ -137,9 +148,9 @@ describe('CurrentUserPopover', () => {
|
||||
it('renders logout button with correct props', () => {
|
||||
const wrapper = mountComponent()
|
||||
|
||||
// Find all buttons and get the logout button (second one)
|
||||
// Find all buttons and get the logout button (last button)
|
||||
const buttons = wrapper.findAllComponents(Button)
|
||||
const logoutButton = buttons[1]
|
||||
const logoutButton = buttons[4]
|
||||
|
||||
// Check that logout button has correct props
|
||||
expect(logoutButton.props('label')).toBe('Log Out')
|
||||
@@ -149,9 +160,9 @@ describe('CurrentUserPopover', () => {
|
||||
it('opens user settings and emits close event when settings button is clicked', async () => {
|
||||
const wrapper = mountComponent()
|
||||
|
||||
// Find all buttons and get the settings button (first one)
|
||||
// Find all buttons and get the settings button (third button)
|
||||
const buttons = wrapper.findAllComponents(Button)
|
||||
const settingsButton = buttons[0]
|
||||
const settingsButton = buttons[2]
|
||||
|
||||
// Click the settings button
|
||||
await settingsButton.trigger('click')
|
||||
@@ -167,9 +178,9 @@ describe('CurrentUserPopover', () => {
|
||||
it('calls logout function and emits close event when logout button is clicked', async () => {
|
||||
const wrapper = mountComponent()
|
||||
|
||||
// Find all buttons and get the logout button (second one)
|
||||
// Find all buttons and get the logout button (last button)
|
||||
const buttons = wrapper.findAllComponents(Button)
|
||||
const logoutButton = buttons[1]
|
||||
const logoutButton = buttons[4]
|
||||
|
||||
// Click the logout button
|
||||
await logoutButton.trigger('click')
|
||||
@@ -185,16 +196,16 @@ describe('CurrentUserPopover', () => {
|
||||
it('opens API pricing docs and emits close event when API pricing button is clicked', async () => {
|
||||
const wrapper = mountComponent()
|
||||
|
||||
// Find all buttons and get the API pricing button (third one now)
|
||||
// Find all buttons and get the Partner Nodes info button (first one)
|
||||
const buttons = wrapper.findAllComponents(Button)
|
||||
const apiPricingButton = buttons[2]
|
||||
const partnerNodesButton = buttons[0]
|
||||
|
||||
// Click the API pricing button
|
||||
await apiPricingButton.trigger('click')
|
||||
// Click the Partner Nodes button
|
||||
await partnerNodesButton.trigger('click')
|
||||
|
||||
// Verify window.open was called with the correct URL
|
||||
expect(window.open).toHaveBeenCalledWith(
|
||||
'https://docs.comfy.org/tutorials/api-nodes/pricing',
|
||||
'https://docs.comfy.org/tutorials/api-nodes/overview#api-nodes',
|
||||
'_blank'
|
||||
)
|
||||
|
||||
@@ -206,9 +217,9 @@ describe('CurrentUserPopover', () => {
|
||||
it('opens top-up dialog and emits close event when top-up button is clicked', async () => {
|
||||
const wrapper = mountComponent()
|
||||
|
||||
// Find all buttons and get the top-up button (last one)
|
||||
// Find all buttons and get the top-up button (second one)
|
||||
const buttons = wrapper.findAllComponents(Button)
|
||||
const topUpButton = buttons[buttons.length - 1]
|
||||
const topUpButton = buttons[1]
|
||||
|
||||
// Click the top-up button
|
||||
await topUpButton.trigger('click')
|
||||
|
||||
@@ -23,6 +23,38 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isActiveSubscription" class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<UserCredit text-class="text-2xl" />
|
||||
<Button
|
||||
:label="$t('subscription.partnerNodesCredits')"
|
||||
severity="secondary"
|
||||
text
|
||||
size="small"
|
||||
class="pl-6 p-0 h-auto justify-start"
|
||||
:pt="{
|
||||
root: {
|
||||
class: 'hover:bg-transparent active:bg-transparent'
|
||||
}
|
||||
}"
|
||||
@click="handleOpenPartnerNodesInfo"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
:label="$t('credits.topUp.topUp')"
|
||||
severity="secondary"
|
||||
size="small"
|
||||
@click="handleTopUp"
|
||||
/>
|
||||
</div>
|
||||
<SubscribeButton
|
||||
v-else
|
||||
:label="$t('subscription.subscribeToComfyCloud')"
|
||||
size="small"
|
||||
variant="gradient"
|
||||
@subscribed="handleSubscribed"
|
||||
/>
|
||||
|
||||
<Divider class="my-2" />
|
||||
|
||||
<Button
|
||||
@@ -35,6 +67,17 @@
|
||||
@click="handleOpenUserSettings"
|
||||
/>
|
||||
|
||||
<Button
|
||||
v-if="isActiveSubscription"
|
||||
class="justify-start"
|
||||
:label="$t(planSettingsLabel)"
|
||||
icon="pi pi-receipt"
|
||||
text
|
||||
fluid
|
||||
severity="secondary"
|
||||
@click="handleOpenPlanAndCreditsSettings"
|
||||
/>
|
||||
|
||||
<Divider class="my-2" />
|
||||
|
||||
<Button
|
||||
@@ -46,34 +89,6 @@
|
||||
severity="secondary"
|
||||
@click="handleLogout"
|
||||
/>
|
||||
|
||||
<Divider class="my-2" />
|
||||
|
||||
<Button
|
||||
class="justify-start"
|
||||
:label="$t('credits.apiPricing')"
|
||||
icon="pi pi-external-link"
|
||||
text
|
||||
fluid
|
||||
severity="secondary"
|
||||
@click="handleOpenApiPricing"
|
||||
/>
|
||||
|
||||
<Divider class="my-2" />
|
||||
|
||||
<div class="flex w-full flex-col gap-2 p-2">
|
||||
<div class="text-sm text-muted">
|
||||
{{ $t('credits.yourCreditBalance') }}
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<UserCredit text-class="text-2xl" />
|
||||
<Button
|
||||
v-if="isActiveSubscription"
|
||||
:label="$t('credits.topUp.topUp')"
|
||||
@click="handleTopUp"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -86,37 +101,60 @@ import UserAvatar from '@/components/common/UserAvatar.vue'
|
||||
import UserCredit from '@/components/common/UserCredit.vue'
|
||||
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: []
|
||||
}>()
|
||||
|
||||
const planSettingsLabel = isCloud
|
||||
? 'settingsCategories.PlanCredits'
|
||||
: 'settingsCategories.Credits'
|
||||
|
||||
const { userDisplayName, userEmail, userPhotoUrl, handleSignOut } =
|
||||
useCurrentUser()
|
||||
const authActions = useFirebaseAuthActions()
|
||||
const dialogService = useDialogService()
|
||||
const { isActiveSubscription } = useSubscription()
|
||||
const { isActiveSubscription, fetchStatus } = useSubscription()
|
||||
|
||||
const handleOpenUserSettings = () => {
|
||||
dialogService.showSettingsDialog('user')
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const handleOpenPlanAndCreditsSettings = () => {
|
||||
if (isCloud) {
|
||||
dialogService.showSettingsDialog('subscription')
|
||||
} else {
|
||||
dialogService.showSettingsDialog('credits')
|
||||
}
|
||||
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const handleTopUp = () => {
|
||||
dialogService.showTopUpCreditsDialog()
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const handleOpenPartnerNodesInfo = () => {
|
||||
window.open(
|
||||
'https://docs.comfy.org/tutorials/api-nodes/overview#api-nodes',
|
||||
'_blank'
|
||||
)
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const handleLogout = async () => {
|
||||
await handleSignOut()
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const handleOpenApiPricing = () => {
|
||||
window.open('https://docs.comfy.org/tutorials/api-nodes/pricing', '_blank')
|
||||
emit('close')
|
||||
const handleSubscribed = async () => {
|
||||
await fetchStatus()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
Reference in New Issue
Block a user