Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/components/topbar/CurrentUserButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@
</div>
</Button>

<Popover ref="popover" :show-arrow="false">
<Popover
ref="popover"
:show-arrow="false"
:pt="{
root: {
class: 'rounded-lg'
}
}"
>
<CurrentUserPopover @close="closePopover" />
</Popover>
</div>
Expand Down
32 changes: 32 additions & 0 deletions src/components/topbar/CurrentUserPopover.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,24 @@ const mockFetchStatus = vi.fn().mockResolvedValue(undefined)
vi.mock('@/platform/cloud/subscription/composables/useSubscription', () => ({
useSubscription: vi.fn(() => ({
isActiveSubscription: { value: true },
subscriptionTierName: { value: 'Creator' },
subscriptionTier: { value: 'CREATOR' },
fetchStatus: mockFetchStatus
}))
}))

// Mock the useSubscriptionDialog composable
const mockSubscriptionDialogShow = vi.fn()
vi.mock(
'@/platform/cloud/subscription/composables/useSubscriptionDialog',
() => ({
useSubscriptionDialog: vi.fn(() => ({
show: mockSubscriptionDialogShow,
hide: vi.fn()
}))
})
)

// Mock UserAvatar component
vi.mock('@/components/common/UserAvatar.vue', () => ({
default: {
Expand Down Expand Up @@ -272,4 +286,22 @@ describe('CurrentUserPopover', () => {
expect(wrapper.emitted('close')).toBeTruthy()
expect(wrapper.emitted('close')!.length).toBe(1)
})

it('opens subscription dialog and emits close event when plans & pricing item is clicked', async () => {
const wrapper = mountComponent()

const plansPricingItem = wrapper.find(
'[data-testid="plans-pricing-menu-item"]'
)
expect(plansPricingItem.exists()).toBe(true)

await plansPricingItem.trigger('click')

// Verify subscription dialog show was called
expect(mockSubscriptionDialogShow).toHaveBeenCalled()

// Verify close event was emitted
expect(wrapper.emitted('close')).toBeTruthy()
expect(wrapper.emitted('close')!.length).toBe(1)
})
})
83 changes: 51 additions & 32 deletions src/components/topbar/CurrentUserPopover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@
<p v-if="userEmail" class="my-0 truncate text-sm text-muted">
{{ userEmail }}
</p>
<p v-if="subscriptionTierName" class="my-0 truncate text-sm text-muted">
<span
v-if="subscriptionTierName"
class="my-0 text-xs text-foreground bg-secondary-background-hover rounded-full uppercase px-2 py-0.5 font-bold mt-2"
>
{{ subscriptionTierName }}
</p>
</span>
</div>

<!-- Credits Section -->
Expand All @@ -33,11 +36,16 @@
v-if="authStore.isFetchingBalance"
width="4rem"
height="1.25rem"
class="flex-1"
class="w-full"
/>
<span v-else class="text-base font-normal text-base-foreground flex-1">{{
<span v-else class="text-base font-semibold text-base-foreground">{{
formattedBalance
}}</span>
<i
v-if="flags.subscriptionTiersEnabled"
v-tooltip="{ value: $t('credits.unified.tooltip'), showDelay: 300 }"
class="icon-[lucide--circle-help] cursor-help text-base text-muted-foreground mr-auto"
/>
<Button
:label="$t('subscription.addCredits')"
severity="secondary"
Expand All @@ -58,24 +66,6 @@
/>
</div>

<!-- Credits info row -->
<div
v-if="flags.subscriptionTiersEnabled && isActiveSubscription"
class="flex items-center gap-2 px-4 py-0"
>
<i
v-tooltip="{
value: $t('credits.unified.tooltip'),
showDelay: 300,
hideDelay: 300
}"
class="icon-[lucide--circle-help] cursor-help text-xs text-muted-foreground"
/>
<span class="text-sm text-muted-foreground">{{
$t('credits.unified.message')
}}</span>
</div>

<Divider class="my-2 mx-0" />

<div
Expand All @@ -90,15 +80,32 @@
}}</span>
</div>

<div
class="flex items-center gap-2 px-4 py-2 cursor-pointer hover:bg-secondary-background-hover"
data-testid="plans-pricing-menu-item"
@click="handleOpenPlansAndPricing"
>
<i class="icon-[lucide--receipt-text] text-muted-foreground text-sm" />
<span class="text-sm text-base-foreground flex-1">{{
$t('subscription.plansAndPricing')
}}</span>
<span
v-if="canUpgrade"
class="text-xs font-bold text-base-background bg-base-foreground px-1.5 py-0.5 rounded-full"
>
{{ $t('subscription.upgrade') }}
</span>
</div>

<div
v-if="isActiveSubscription"
class="flex items-center gap-2 px-4 py-2 cursor-pointer hover:bg-secondary-background-hover"
data-testid="plan-credits-menu-item"
data-testid="manage-plan-menu-item"
@click="handleOpenPlanAndCreditsSettings"
>
<i class="icon-[lucide--receipt-text] text-muted-foreground text-sm" />
<i class="icon-[lucide--file-text] text-muted-foreground text-sm" />
<span class="text-sm text-base-foreground flex-1">{{
$t(planSettingsLabel)
$t('subscription.managePlan')
}}</span>
</div>

Expand All @@ -109,7 +116,7 @@
>
<i class="icon-[lucide--settings-2] text-muted-foreground text-sm" />
<span class="text-sm text-base-foreground flex-1">{{
$t('userSettings.title')
$t('userSettings.accountSettings')
}}</span>
</div>

Expand Down Expand Up @@ -143,6 +150,7 @@ import { useExternalLink } from '@/composables/useExternalLink'
import { useFeatureFlags } from '@/composables/useFeatureFlags'
import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
import { useSubscriptionDialog } from '@/platform/cloud/subscription/composables/useSubscriptionDialog'
import { isCloud } from '@/platform/distribution/types'
import { useTelemetry } from '@/platform/telemetry'
import { useDialogService } from '@/services/dialogService'
Expand All @@ -154,17 +162,18 @@ const emit = defineEmits<{

const { buildDocsUrl } = useExternalLink()

const planSettingsLabel = isCloud
? 'settingsCategories.PlanCredits'
: 'settingsCategories.Credits'

const { userDisplayName, userEmail, userPhotoUrl, handleSignOut } =
useCurrentUser()
const authActions = useFirebaseAuthActions()
const authStore = useFirebaseAuthStore()
const dialogService = useDialogService()
const { isActiveSubscription, subscriptionTierName, fetchStatus } =
useSubscription()
const {
isActiveSubscription,
subscriptionTierName,
subscriptionTier,
fetchStatus
} = useSubscription()
const subscriptionDialog = useSubscriptionDialog()
const { flags } = useFeatureFlags()
const { locale } = useI18n()

Expand All @@ -181,11 +190,21 @@ const formattedBalance = computed(() => {
})
})

const canUpgrade = computed(() => {
const tier = subscriptionTier.value
return tier === 'STANDARD' || tier === 'CREATOR'
})

const handleOpenUserSettings = () => {
dialogService.showSettingsDialog('user')
emit('close')
}

const handleOpenPlansAndPricing = () => {
subscriptionDialog.show()
emit('close')
}

const handleOpenPlanAndCreditsSettings = () => {
if (isCloud) {
dialogService.showSettingsDialog('subscription')
Expand Down
4 changes: 4 additions & 0 deletions src/locales/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -2018,6 +2018,9 @@
"contactUs": "Contact us",
"viewEnterprise": "View enterprise",
"partnerNodesCredits": "Partner nodes pricing",
"plansAndPricing": "Plans & pricing",
"managePlan": "Manage plan",
"upgrade": "UPGRADE",
"mostPopular": "Most popular",
"currentPlan": "Current Plan",
"subscribeTo": "Subscribe to {plan}",
Expand Down Expand Up @@ -2046,6 +2049,7 @@
},
"userSettings": {
"title": "My Account Settings",
"accountSettings": "Account settings",
"name": "Name",
"email": "Email",
"provider": "Sign-in Provider",
Expand Down
Loading