338 lines
9.1 KiB
TypeScript
338 lines
9.1 KiB
TypeScript
|
|
/**
|
||
|
|
* Subscription Analytics Tracking
|
||
|
|
*
|
||
|
|
* This module provides conversion tracking for the subscription funnel.
|
||
|
|
* Events are sent to your analytics provider (e.g., Segment, Mixpanel, Google Analytics).
|
||
|
|
*
|
||
|
|
* Integration: Replace the `track()` function implementation with your analytics SDK.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import type { SubscriptionTier } from '../api';
|
||
|
|
|
||
|
|
// Event type definitions
|
||
|
|
export interface SubscriptionEvent {
|
||
|
|
event: string;
|
||
|
|
properties: Record<string, any>;
|
||
|
|
timestamp: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Event names
|
||
|
|
export const SUBSCRIPTION_EVENTS = {
|
||
|
|
// Page views
|
||
|
|
SUBSCRIPTION_PAGE_VIEWED: 'subscription_page_viewed',
|
||
|
|
PRICING_PAGE_VIEWED: 'pricing_page_viewed',
|
||
|
|
COMPARISON_TABLE_VIEWED: 'comparison_table_viewed',
|
||
|
|
|
||
|
|
// Interactions
|
||
|
|
BILLING_CYCLE_TOGGLED: 'billing_cycle_toggled',
|
||
|
|
FEATURE_LIST_EXPANDED: 'feature_list_expanded',
|
||
|
|
FEATURE_LIST_COLLAPSED: 'feature_list_collapsed',
|
||
|
|
COMPARISON_CATEGORY_EXPANDED: 'comparison_category_expanded',
|
||
|
|
ROI_CALCULATOR_OPENED: 'roi_calculator_opened',
|
||
|
|
ROI_CALCULATED: 'roi_calculated',
|
||
|
|
USAGE_METRIC_VIEWED: 'usage_metric_viewed',
|
||
|
|
|
||
|
|
// CTAs
|
||
|
|
UPGRADE_CTA_CLICKED: 'upgrade_cta_clicked',
|
||
|
|
PLAN_CARD_CLICKED: 'plan_card_clicked',
|
||
|
|
CONTACT_SALES_CLICKED: 'contact_sales_clicked',
|
||
|
|
START_TRIAL_CLICKED: 'start_trial_clicked',
|
||
|
|
|
||
|
|
// Conversions
|
||
|
|
PLAN_SELECTED: 'plan_selected',
|
||
|
|
UPGRADE_INITIATED: 'upgrade_initiated',
|
||
|
|
UPGRADE_COMPLETED: 'upgrade_completed',
|
||
|
|
DOWNGRADE_INITIATED: 'downgrade_initiated',
|
||
|
|
|
||
|
|
// Feature discovery
|
||
|
|
FEATURE_PREVIEW_VIEWED: 'feature_preview_viewed',
|
||
|
|
LOCKED_FEATURE_CLICKED: 'locked_feature_clicked',
|
||
|
|
|
||
|
|
// Warnings & notifications
|
||
|
|
USAGE_LIMIT_WARNING_SHOWN: 'usage_limit_warning_shown',
|
||
|
|
USAGE_LIMIT_REACHED: 'usage_limit_reached',
|
||
|
|
BREACH_PREDICTION_SHOWN: 'breach_prediction_shown'
|
||
|
|
} as const;
|
||
|
|
|
||
|
|
// Analytics provider adapter (replace with your actual analytics SDK)
|
||
|
|
const track = (event: string, properties: Record<string, any> = {}) => {
|
||
|
|
const timestamp = Date.now();
|
||
|
|
|
||
|
|
// Add common properties to all events
|
||
|
|
const enrichedProperties = {
|
||
|
|
...properties,
|
||
|
|
timestamp,
|
||
|
|
url: window.location.href,
|
||
|
|
userAgent: navigator.userAgent,
|
||
|
|
};
|
||
|
|
|
||
|
|
// TODO: Replace with your analytics SDK
|
||
|
|
// Examples:
|
||
|
|
// - Segment: analytics.track(event, enrichedProperties);
|
||
|
|
// - Mixpanel: mixpanel.track(event, enrichedProperties);
|
||
|
|
// - Google Analytics: gtag('event', event, enrichedProperties);
|
||
|
|
|
||
|
|
// For now, log to console in development
|
||
|
|
if (process.env.NODE_ENV === 'development') {
|
||
|
|
console.log('[Analytics]', event, enrichedProperties);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Store in localStorage for debugging
|
||
|
|
try {
|
||
|
|
const events = JSON.parse(localStorage.getItem('subscription_events') || '[]');
|
||
|
|
events.push({ event, properties: enrichedProperties, timestamp });
|
||
|
|
// Keep only last 100 events
|
||
|
|
localStorage.setItem('subscription_events', JSON.stringify(events.slice(-100)));
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Failed to store analytics event:', error);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Convenience tracking functions
|
||
|
|
|
||
|
|
export const trackSubscriptionPageViewed = (currentTier?: SubscriptionTier) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.SUBSCRIPTION_PAGE_VIEWED, {
|
||
|
|
current_tier: currentTier,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackPricingPageViewed = (source?: string) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.PRICING_PAGE_VIEWED, {
|
||
|
|
source,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackBillingCycleToggled = (from: 'monthly' | 'yearly', to: 'monthly' | 'yearly') => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.BILLING_CYCLE_TOGGLED, {
|
||
|
|
from,
|
||
|
|
to,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackFeatureListExpanded = (tier: SubscriptionTier, featureCount: number) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.FEATURE_LIST_EXPANDED, {
|
||
|
|
tier,
|
||
|
|
feature_count: featureCount,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackFeatureListCollapsed = (tier: SubscriptionTier, viewDurationSeconds: number) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.FEATURE_LIST_COLLAPSED, {
|
||
|
|
tier,
|
||
|
|
view_duration_seconds: viewDurationSeconds,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackComparisonTableViewed = (durationSeconds?: number) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.COMPARISON_TABLE_VIEWED, {
|
||
|
|
duration_seconds: durationSeconds,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackComparisonCategoryExpanded = (category: string) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.COMPARISON_CATEGORY_EXPANDED, {
|
||
|
|
category,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackROICalculatorOpened = (currentTier: SubscriptionTier, targetTier: SubscriptionTier) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.ROI_CALCULATOR_OPENED, {
|
||
|
|
current_tier: currentTier,
|
||
|
|
target_tier: targetTier,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackROICalculated = (
|
||
|
|
currentTier: SubscriptionTier,
|
||
|
|
targetTier: SubscriptionTier,
|
||
|
|
metrics: {
|
||
|
|
dailySales: number;
|
||
|
|
wastePercentage: number;
|
||
|
|
employees: number;
|
||
|
|
},
|
||
|
|
results: {
|
||
|
|
monthlySavings: number;
|
||
|
|
paybackPeriodDays: number;
|
||
|
|
annualROI: number;
|
||
|
|
}
|
||
|
|
) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.ROI_CALCULATED, {
|
||
|
|
current_tier: currentTier,
|
||
|
|
target_tier: targetTier,
|
||
|
|
input_daily_sales: metrics.dailySales,
|
||
|
|
input_waste_percentage: metrics.wastePercentage,
|
||
|
|
input_employees: metrics.employees,
|
||
|
|
result_monthly_savings: results.monthlySavings,
|
||
|
|
result_payback_period_days: results.paybackPeriodDays,
|
||
|
|
result_annual_roi: results.annualROI,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackUsageMetricViewed = (
|
||
|
|
metric: string,
|
||
|
|
currentUsage: number,
|
||
|
|
limit: number | null,
|
||
|
|
percentage: number,
|
||
|
|
daysUntilBreach?: number | null
|
||
|
|
) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.USAGE_METRIC_VIEWED, {
|
||
|
|
metric,
|
||
|
|
current_usage: currentUsage,
|
||
|
|
limit,
|
||
|
|
usage_percentage: percentage,
|
||
|
|
days_until_breach: daysUntilBreach,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackUpgradeCTAClicked = (
|
||
|
|
currentTier: SubscriptionTier,
|
||
|
|
targetTier: SubscriptionTier,
|
||
|
|
source: string, // e.g., 'usage_warning', 'pricing_card', 'roi_calculator'
|
||
|
|
ctaText?: string
|
||
|
|
) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.UPGRADE_CTA_CLICKED, {
|
||
|
|
current_tier: currentTier,
|
||
|
|
target_tier: targetTier,
|
||
|
|
source,
|
||
|
|
cta_text: ctaText,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackPlanCardClicked = (tier: SubscriptionTier, currentTier?: SubscriptionTier) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.PLAN_CARD_CLICKED, {
|
||
|
|
tier,
|
||
|
|
current_tier: currentTier,
|
||
|
|
is_upgrade: currentTier && tier > currentTier,
|
||
|
|
is_downgrade: currentTier && tier < currentTier,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackContactSalesClicked = (tier: SubscriptionTier = 'enterprise') => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.CONTACT_SALES_CLICKED, {
|
||
|
|
tier,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackStartTrialClicked = (tier: SubscriptionTier) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.START_TRIAL_CLICKED, {
|
||
|
|
tier,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackPlanSelected = (tier: SubscriptionTier, billingCycle: 'monthly' | 'yearly') => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.PLAN_SELECTED, {
|
||
|
|
tier,
|
||
|
|
billing_cycle: billingCycle,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackUpgradeInitiated = (
|
||
|
|
fromTier: SubscriptionTier,
|
||
|
|
toTier: SubscriptionTier,
|
||
|
|
billingCycle: 'monthly' | 'yearly',
|
||
|
|
source?: string
|
||
|
|
) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.UPGRADE_INITIATED, {
|
||
|
|
from_tier: fromTier,
|
||
|
|
to_tier: toTier,
|
||
|
|
billing_cycle: billingCycle,
|
||
|
|
source,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackUpgradeCompleted = (
|
||
|
|
fromTier: SubscriptionTier,
|
||
|
|
toTier: SubscriptionTier,
|
||
|
|
billingCycle: 'monthly' | 'yearly',
|
||
|
|
revenue: number,
|
||
|
|
timeSincePageView?: number // milliseconds
|
||
|
|
) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.UPGRADE_COMPLETED, {
|
||
|
|
from_tier: fromTier,
|
||
|
|
to_tier: toTier,
|
||
|
|
billing_cycle: billingCycle,
|
||
|
|
revenue,
|
||
|
|
time_since_page_view_ms: timeSincePageView,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackFeaturePreviewViewed = (feature: string, tier: SubscriptionTier) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.FEATURE_PREVIEW_VIEWED, {
|
||
|
|
feature,
|
||
|
|
required_tier: tier,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackLockedFeatureClicked = (
|
||
|
|
feature: string,
|
||
|
|
currentTier: SubscriptionTier,
|
||
|
|
requiredTier: SubscriptionTier
|
||
|
|
) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.LOCKED_FEATURE_CLICKED, {
|
||
|
|
feature,
|
||
|
|
current_tier: currentTier,
|
||
|
|
required_tier: requiredTier,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackUsageLimitWarningShown = (
|
||
|
|
metric: string,
|
||
|
|
currentUsage: number,
|
||
|
|
limit: number,
|
||
|
|
percentage: number
|
||
|
|
) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.USAGE_LIMIT_WARNING_SHOWN, {
|
||
|
|
metric,
|
||
|
|
current_usage: currentUsage,
|
||
|
|
limit,
|
||
|
|
usage_percentage: percentage,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackUsageLimitReached = (metric: string, limit: number) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.USAGE_LIMIT_REACHED, {
|
||
|
|
metric,
|
||
|
|
limit,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const trackBreachPredictionShown = (
|
||
|
|
metric: string,
|
||
|
|
currentUsage: number,
|
||
|
|
limit: number,
|
||
|
|
daysUntilBreach: number
|
||
|
|
) => {
|
||
|
|
track(SUBSCRIPTION_EVENTS.BREACH_PREDICTION_SHOWN, {
|
||
|
|
metric,
|
||
|
|
current_usage: currentUsage,
|
||
|
|
limit,
|
||
|
|
days_until_breach: daysUntilBreach,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
// Utility to get stored events (for debugging)
|
||
|
|
export const getStoredEvents = (): SubscriptionEvent[] => {
|
||
|
|
try {
|
||
|
|
return JSON.parse(localStorage.getItem('subscription_events') || '[]');
|
||
|
|
} catch {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Clear stored events
|
||
|
|
export const clearStoredEvents = () => {
|
||
|
|
localStorage.removeItem('subscription_events');
|
||
|
|
};
|
||
|
|
|
||
|
|
// Generate conversion funnel report
|
||
|
|
export const generateConversionFunnelReport = (): Record<string, number> => {
|
||
|
|
const events = getStoredEvents();
|
||
|
|
const funnel: Record<string, number> = {};
|
||
|
|
|
||
|
|
Object.values(SUBSCRIPTION_EVENTS).forEach(eventName => {
|
||
|
|
funnel[eventName] = events.filter(e => e.event === eventName).length;
|
||
|
|
});
|
||
|
|
|
||
|
|
return funnel;
|
||
|
|
};
|