feat: add Free subscription tier support (#8864)

## Summary

Add frontend support for a Free subscription tier — login/signup page
restructuring, telemetry instrumentation, and tier-aware billing gating.

## Changes

- **What**: 
- Restructure login/signup pages: OAuth buttons promoted as primary
sign-in method, email login available via progressive disclosure
- Add Free tier badge on Google sign-up button with dynamic credit count
from remote config
- Add `FREE` subscription tier to type system (tier pricing, tier rank,
registry types)
  - Add `isFreeTier` computed to `useSubscription()`
- Disable credit top-up for Free tier users (dialogService,
purchaseCredits, popover CTA)
- Show subscription/upgrade dialog instead of top-up dialog when Free
tier user hits out-of-credits
- Add funnel telemetry: `trackLoginOpened`, enrich `trackSignupOpened`
with `free_tier_badge_shown`, track email toggle clicks

## Review Focus

- Tier gating logic: Free tier users should see "Upgrade" instead of
"Add Credits" and never reach the top-up flow
- Telemetry event design for Mixpanel funnel analysis
- Progressive disclosure UX on login/signup pages

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8864-feat-add-Free-subscription-tier-support-3076d73d36508133b84ec5f0a67ccb03)
by [Unito](https://www.unito.io)
This commit is contained in:
Hunter
2026-02-24 23:28:51 -05:00
committed by GitHub
parent aee207f16c
commit 8c3738fb77
39 changed files with 720 additions and 221 deletions

View File

@@ -15,6 +15,7 @@ import type {
PageViewMetadata,
PageVisibilityMetadata,
SettingChangedMetadata,
SubscriptionMetadata,
SurveyResponses,
TabCountMetadata,
TelemetryDispatcher,
@@ -65,8 +66,11 @@ export class TelemetryRegistry implements TelemetryDispatcher {
this.dispatch((provider) => provider.trackUserLoggedIn?.())
}
trackSubscription(event: 'modal_opened' | 'subscribe_clicked'): void {
this.dispatch((provider) => provider.trackSubscription?.(event))
trackSubscription(
event: 'modal_opened' | 'subscribe_clicked',
metadata?: SubscriptionMetadata
): void {
this.dispatch((provider) => provider.trackSubscription?.(event, metadata))
}
trackBeginCheckout(metadata: BeginCheckoutMetadata): void {

View File

@@ -35,6 +35,7 @@ import type {
PageVisibilityMetadata,
RunButtonProperties,
SettingChangedMetadata,
SubscriptionMetadata,
SurveyResponses,
TabCountMetadata,
TelemetryEventName,
@@ -222,13 +223,16 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
this.trackEvent(TelemetryEvents.USER_LOGGED_IN)
}
trackSubscription(event: 'modal_opened' | 'subscribe_clicked'): void {
trackSubscription(
event: 'modal_opened' | 'subscribe_clicked',
metadata?: SubscriptionMetadata
): void {
const eventName =
event === 'modal_opened'
? TelemetryEvents.SUBSCRIPTION_REQUIRED_MODAL_OPENED
: TelemetryEvents.SUBSCRIBE_NOW_BUTTON_CLICKED
this.trackEvent(eventName)
this.trackEvent(eventName, metadata)
}
trackAddApiCreditButtonClicked(): void {

View File

@@ -12,6 +12,7 @@
* 3. Check dist/assets/*.js files contain no tracking code
*/
import type { SubscriptionDialogReason } from '@/platform/cloud/subscription/composables/useSubscriptionDialog'
import type { TierKey } from '@/platform/cloud/subscription/constants/tierPricing'
import type { BillingCycle } from '@/platform/cloud/subscription/utils/subscriptionTierRank'
import type { AuditLog } from '@/services/customerEventsService'
@@ -301,6 +302,11 @@ export interface CheckoutAttributionMetadata {
wbraid?: string
}
export interface SubscriptionMetadata {
current_tier?: string
reason?: SubscriptionDialogReason
}
export interface BeginCheckoutMetadata
extends Record<string, unknown>, CheckoutAttributionMetadata {
user_id: string
@@ -321,7 +327,10 @@ export interface TelemetryProvider {
trackUserLoggedIn?(): void
// Subscription flow events
trackSubscription?(event: 'modal_opened' | 'subscribe_clicked'): void
trackSubscription?(
event: 'modal_opened' | 'subscribe_clicked',
metadata?: SubscriptionMetadata
): void
trackBeginCheckout?(metadata: BeginCheckoutMetadata): void
trackMonthlySubscriptionSucceeded?(): void
trackMonthlySubscriptionCancelled?(): void
@@ -512,3 +521,4 @@ export type TelemetryEventProperties =
| HelpCenterClosedMetadata
| WorkflowCreatedMetadata
| EnterLinearMetadata
| SubscriptionMetadata