chore: add resume capability to onboarding flow
This commit is contained in:
parent
84235860de
commit
0e72fb2eb6
8 changed files with 337 additions and 117 deletions
|
|
@ -41,10 +41,7 @@ export class OnboardingManager {
|
|||
await this.windowManager.createOrShowOnboardingWindow();
|
||||
|
||||
// Track onboarding started event
|
||||
this.onboardingService.trackEvent("onboarding_started", {
|
||||
version: 1,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
this.onboardingService.trackOnboardingStarted(process.platform);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -93,11 +90,11 @@ export class OnboardingManager {
|
|||
|
||||
// Track abandonment event
|
||||
const currentState = await this.onboardingService.getOnboardingState();
|
||||
this.onboardingService.trackEvent("onboarding_abandoned", {
|
||||
last_screen:
|
||||
currentState?.skippedScreens?.[currentState.skippedScreens.length - 1],
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
const lastScreen =
|
||||
currentState?.lastVisitedScreen ||
|
||||
currentState?.skippedScreens?.[currentState.skippedScreens.length - 1] ||
|
||||
"unknown";
|
||||
this.onboardingService.trackOnboardingAbandoned(lastScreen);
|
||||
|
||||
// Close the onboarding window
|
||||
const onboardingWindow = this.windowManager.getOnboardingWindow();
|
||||
|
|
@ -169,11 +166,4 @@ export class OnboardingManager {
|
|||
getFeatureFlags(): any {
|
||||
return this.onboardingService.getFeatureFlags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Track an onboarding event
|
||||
*/
|
||||
trackEvent(eventName: string, properties?: Record<string, any>): void {
|
||||
this.onboardingService.trackEvent(eventName, properties);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,12 +46,24 @@ export function App() {
|
|||
const [discoveryDetails, setDiscoveryDetails] = useState<string>("");
|
||||
|
||||
// Hooks
|
||||
const { state, isLoading, savePreferences, completeOnboarding, trackEvent } =
|
||||
const { state, isLoading, savePreferences, completeOnboarding } =
|
||||
useOnboardingState();
|
||||
|
||||
// tRPC queries
|
||||
const featureFlagsQuery = api.onboarding.getFeatureFlags.useQuery();
|
||||
const skippedScreensQuery = api.onboarding.getSkippedScreens.useQuery();
|
||||
|
||||
// Telemetry mutations
|
||||
const trackOnboardingStarted =
|
||||
api.onboarding.trackOnboardingStarted.useMutation();
|
||||
const trackOnboardingScreenViewed =
|
||||
api.onboarding.trackOnboardingScreenViewed.useMutation();
|
||||
const trackOnboardingFeaturesSelected =
|
||||
api.onboarding.trackOnboardingFeaturesSelected.useMutation();
|
||||
const trackOnboardingDiscoverySelected =
|
||||
api.onboarding.trackOnboardingDiscoverySelected.useMutation();
|
||||
const trackOnboardingModelSelected =
|
||||
api.onboarding.trackOnboardingModelSelected.useMutation();
|
||||
const utils = api.useUtils();
|
||||
|
||||
// Screen order - can be modified based on feature flags
|
||||
|
|
@ -155,10 +167,9 @@ export function App() {
|
|||
}
|
||||
|
||||
// Track onboarding started event (T034)
|
||||
trackEvent("onboarding_started", {
|
||||
trackOnboardingStarted.mutate({
|
||||
platform: platformResult,
|
||||
resumed: !!state?.lastVisitedScreen,
|
||||
// Enum values are strings at runtime, safe for telemetry
|
||||
resumedFrom: state?.lastVisitedScreen,
|
||||
});
|
||||
};
|
||||
|
|
@ -166,7 +177,7 @@ export function App() {
|
|||
initialize();
|
||||
}, [
|
||||
checkPermissionsWithResult,
|
||||
trackEvent,
|
||||
trackOnboardingStarted,
|
||||
utils,
|
||||
state?.lastVisitedScreen,
|
||||
getActiveScreens,
|
||||
|
|
@ -185,12 +196,17 @@ export function App() {
|
|||
|
||||
// Track screen views (T035)
|
||||
useEffect(() => {
|
||||
trackEvent("onboarding_screen_viewed", {
|
||||
screen: currentScreen, // OnboardingScreen enum, string value at runtime
|
||||
trackOnboardingScreenViewed.mutate({
|
||||
screen: currentScreen,
|
||||
index: getCurrentScreenIndex(),
|
||||
total: getTotalScreens(),
|
||||
});
|
||||
}, [currentScreen, trackEvent, getCurrentScreenIndex, getTotalScreens]);
|
||||
}, [
|
||||
currentScreen,
|
||||
trackOnboardingScreenViewed,
|
||||
getCurrentScreenIndex,
|
||||
getTotalScreens,
|
||||
]);
|
||||
|
||||
// Navigation functions (T028 - Back navigation)
|
||||
const navigateBack = useCallback(() => {
|
||||
|
|
@ -234,7 +250,7 @@ export function App() {
|
|||
|
||||
// Handle feature interests selection (T036)
|
||||
const handleFeatureInterests = async (interests: FeatureInterest[]) => {
|
||||
trackEvent("onboarding_features_selected", {
|
||||
trackOnboardingFeaturesSelected.mutate({
|
||||
features: interests,
|
||||
count: interests.length,
|
||||
});
|
||||
|
|
@ -247,7 +263,7 @@ export function App() {
|
|||
source: DiscoverySource,
|
||||
details?: string,
|
||||
) => {
|
||||
trackEvent("onboarding_discovery_selected", {
|
||||
trackOnboardingDiscoverySelected.mutate({
|
||||
source,
|
||||
details,
|
||||
});
|
||||
|
|
@ -261,7 +277,7 @@ export function App() {
|
|||
modelType: ModelType,
|
||||
recommendationFollowed: boolean,
|
||||
) => {
|
||||
trackEvent("onboarding_model_selected", {
|
||||
trackOnboardingModelSelected.mutate({
|
||||
model_type: modelType,
|
||||
recommendation_followed: recommendationFollowed,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ interface UseOnboardingStateReturn {
|
|||
error: Error | null;
|
||||
savePreferences: (preferences: OnboardingPreferences) => Promise<void>;
|
||||
completeOnboarding: (finalState: OnboardingState) => Promise<void>;
|
||||
trackEvent: (eventName: string, properties?: Record<string, any>) => void;
|
||||
resetOnboarding: () => Promise<void>;
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +31,8 @@ export function useOnboardingState(): UseOnboardingStateReturn {
|
|||
const getStateQuery = api.onboarding.getState.useQuery();
|
||||
const savePreferencesMutation = api.onboarding.savePreferences.useMutation();
|
||||
const completeMutation = api.onboarding.complete.useMutation();
|
||||
const trackEventMutation = api.onboarding.trackEvent.useMutation();
|
||||
const trackOnboardingCompleted =
|
||||
api.onboarding.trackOnboardingCompleted.useMutation();
|
||||
const resetMutation = api.onboarding.reset.useMutation();
|
||||
|
||||
// Load initial state
|
||||
|
|
@ -90,18 +90,6 @@ export function useOnboardingState(): UseOnboardingStateReturn {
|
|||
[savePreferencesMutation],
|
||||
);
|
||||
|
||||
// Track analytics event
|
||||
const trackEvent = useCallback(
|
||||
(eventName: string, properties?: Record<string, any>) => {
|
||||
// Fire and forget - we don't wait for the result
|
||||
trackEventMutation.mutate({
|
||||
eventName,
|
||||
properties: properties || {},
|
||||
});
|
||||
},
|
||||
[trackEventMutation],
|
||||
);
|
||||
|
||||
// Complete onboarding
|
||||
const completeOnboarding = useCallback(
|
||||
async (finalState: OnboardingState) => {
|
||||
|
|
@ -113,11 +101,14 @@ export function useOnboardingState(): UseOnboardingStateReturn {
|
|||
}
|
||||
|
||||
// Track completion event
|
||||
trackEvent("onboarding_completed", {
|
||||
features_selected: finalState.featureInterests,
|
||||
trackOnboardingCompleted.mutate({
|
||||
version: finalState.completedVersion,
|
||||
features_selected: finalState.featureInterests || [],
|
||||
discovery_source: finalState.discoverySource,
|
||||
model_type: finalState.selectedModelType,
|
||||
recommendation_followed: finalState.modelRecommendation?.followed,
|
||||
recommendation_followed:
|
||||
finalState.modelRecommendation?.followed || false,
|
||||
skipped_screens: finalState.skippedScreens,
|
||||
});
|
||||
|
||||
// Handle relaunch if needed
|
||||
|
|
@ -135,7 +126,7 @@ export function useOnboardingState(): UseOnboardingStateReturn {
|
|||
throw err;
|
||||
}
|
||||
},
|
||||
[completeMutation, trackEvent],
|
||||
[completeMutation, trackOnboardingCompleted],
|
||||
);
|
||||
|
||||
// Reset onboarding (for testing)
|
||||
|
|
@ -158,7 +149,6 @@ export function useOnboardingState(): UseOnboardingStateReturn {
|
|||
error,
|
||||
savePreferences,
|
||||
completeOnboarding,
|
||||
trackEvent,
|
||||
resetOnboarding,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,16 +268,16 @@ export class OnboardingService {
|
|||
|
||||
await this.saveOnboardingState(completeState);
|
||||
|
||||
// Track completion event if telemetry is enabled
|
||||
if (this.telemetryService.isEnabled()) {
|
||||
this.telemetryService.track("onboarding_completed", {
|
||||
version: completeState.completedVersion,
|
||||
features: completeState.featureInterests,
|
||||
model: completeState.selectedModelType,
|
||||
followed_recommendation: completeState.modelRecommendation?.followed,
|
||||
skipped_screens: completeState.skippedScreens,
|
||||
});
|
||||
}
|
||||
// Track completion event
|
||||
this.telemetryService.trackOnboardingCompleted({
|
||||
version: completeState.completedVersion,
|
||||
features_selected: completeState.featureInterests || [],
|
||||
discovery_source: completeState.discoverySource,
|
||||
model_type: completeState.selectedModelType,
|
||||
recommendation_followed:
|
||||
completeState.modelRecommendation?.followed || false,
|
||||
skipped_screens: completeState.skippedScreens,
|
||||
});
|
||||
|
||||
logger.main.info("Onboarding completed successfully");
|
||||
} catch (error) {
|
||||
|
|
@ -508,15 +508,24 @@ export class OnboardingService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Track onboarding event
|
||||
* Track onboarding started event
|
||||
*/
|
||||
trackEvent(eventName: string, properties?: Record<string, any>): void {
|
||||
if (this.telemetryService.isEnabled()) {
|
||||
this.telemetryService.track(eventName, {
|
||||
...properties,
|
||||
onboarding_session: this.currentState,
|
||||
});
|
||||
}
|
||||
trackOnboardingStarted(platform: string): void {
|
||||
this.telemetryService.trackOnboardingStarted({
|
||||
platform,
|
||||
resumed: !!this.currentState?.lastVisitedScreen,
|
||||
resumedFrom: this.currentState?.lastVisitedScreen,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Track onboarding abandoned event
|
||||
*/
|
||||
trackOnboardingAbandoned(lastScreen: string): void {
|
||||
this.telemetryService.trackOnboardingAbandoned({
|
||||
last_screen: lastScreen,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,6 +4,15 @@ import * as si from "systeminformation";
|
|||
import { app } from "electron";
|
||||
import { logger } from "../main/logger";
|
||||
import type { SettingsService } from "./settings-service";
|
||||
import type {
|
||||
OnboardingStartedEvent,
|
||||
OnboardingScreenViewedEvent,
|
||||
OnboardingFeaturesSelectedEvent,
|
||||
OnboardingDiscoverySelectedEvent,
|
||||
OnboardingModelSelectedEvent,
|
||||
OnboardingCompletedEvent,
|
||||
OnboardingAbandonedEvent,
|
||||
} from "../types/telemetry-events";
|
||||
|
||||
export interface TranscriptionMetrics {
|
||||
session_id?: string;
|
||||
|
|
@ -252,30 +261,102 @@ export class TelemetryService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic track method for any analytics event
|
||||
* PostHog SDK handles queueing, retries, and batching automatically
|
||||
*/
|
||||
track(eventName: string, properties?: Record<string, any>): void {
|
||||
if (!this.posthog || !this.enabled) {
|
||||
return;
|
||||
}
|
||||
// ============================================================================
|
||||
// Onboarding Events
|
||||
// ============================================================================
|
||||
|
||||
trackOnboardingStarted(props: OnboardingStartedEvent): void {
|
||||
if (!this.posthog || !this.enabled) return;
|
||||
|
||||
this.posthog.capture({
|
||||
distinctId: this.machineId,
|
||||
event: eventName,
|
||||
properties: {
|
||||
...properties,
|
||||
...this.persistedProperties,
|
||||
},
|
||||
event: "onboarding_started",
|
||||
properties: { ...props, ...this.persistedProperties },
|
||||
});
|
||||
|
||||
logger.main.debug("Tracked event:", {
|
||||
event: eventName,
|
||||
properties: properties,
|
||||
});
|
||||
logger.main.debug("Tracked onboarding started", props);
|
||||
}
|
||||
|
||||
trackOnboardingScreenViewed(props: OnboardingScreenViewedEvent): void {
|
||||
if (!this.posthog || !this.enabled) return;
|
||||
|
||||
this.posthog.capture({
|
||||
distinctId: this.machineId,
|
||||
event: "onboarding_screen_viewed",
|
||||
properties: { ...props, ...this.persistedProperties },
|
||||
});
|
||||
|
||||
logger.main.debug("Tracked onboarding screen viewed", props);
|
||||
}
|
||||
|
||||
trackOnboardingFeaturesSelected(
|
||||
props: OnboardingFeaturesSelectedEvent,
|
||||
): void {
|
||||
if (!this.posthog || !this.enabled) return;
|
||||
|
||||
this.posthog.capture({
|
||||
distinctId: this.machineId,
|
||||
event: "onboarding_features_selected",
|
||||
properties: { ...props, ...this.persistedProperties },
|
||||
});
|
||||
|
||||
logger.main.debug("Tracked onboarding features selected", props);
|
||||
}
|
||||
|
||||
trackOnboardingDiscoverySelected(
|
||||
props: OnboardingDiscoverySelectedEvent,
|
||||
): void {
|
||||
if (!this.posthog || !this.enabled) return;
|
||||
|
||||
this.posthog.capture({
|
||||
distinctId: this.machineId,
|
||||
event: "onboarding_discovery_selected",
|
||||
properties: { ...props, ...this.persistedProperties },
|
||||
});
|
||||
|
||||
logger.main.debug("Tracked onboarding discovery selected", props);
|
||||
}
|
||||
|
||||
trackOnboardingModelSelected(props: OnboardingModelSelectedEvent): void {
|
||||
if (!this.posthog || !this.enabled) return;
|
||||
|
||||
this.posthog.capture({
|
||||
distinctId: this.machineId,
|
||||
event: "onboarding_model_selected",
|
||||
properties: { ...props, ...this.persistedProperties },
|
||||
});
|
||||
|
||||
logger.main.debug("Tracked onboarding model selected", props);
|
||||
}
|
||||
|
||||
trackOnboardingCompleted(props: OnboardingCompletedEvent): void {
|
||||
if (!this.posthog || !this.enabled) return;
|
||||
|
||||
this.posthog.capture({
|
||||
distinctId: this.machineId,
|
||||
event: "onboarding_completed",
|
||||
properties: { ...props, ...this.persistedProperties },
|
||||
});
|
||||
|
||||
logger.main.debug("Tracked onboarding completed", props);
|
||||
}
|
||||
|
||||
trackOnboardingAbandoned(props: OnboardingAbandonedEvent): void {
|
||||
if (!this.posthog || !this.enabled) return;
|
||||
|
||||
this.posthog.capture({
|
||||
distinctId: this.machineId,
|
||||
event: "onboarding_abandoned",
|
||||
properties: { ...props, ...this.persistedProperties },
|
||||
});
|
||||
|
||||
logger.main.debug("Tracked onboarding abandoned", props);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Transcription Events
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Get system information for model recommendations
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { ServiceManager } from "../../main/managers/service-manager";
|
|||
import {
|
||||
OnboardingPreferencesSchema,
|
||||
OnboardingStateSchema,
|
||||
AnalyticsEventSchema,
|
||||
ModelTypeSchema,
|
||||
FeatureInterestSchema,
|
||||
DiscoverySourceSchema,
|
||||
|
|
@ -200,42 +199,106 @@ export const onboardingRouter = createRouter({
|
|||
),
|
||||
|
||||
/**
|
||||
* Track analytics event
|
||||
* Track onboarding started event
|
||||
*/
|
||||
trackEvent: procedure
|
||||
.input(AnalyticsEventSchema)
|
||||
.mutation(
|
||||
async ({ input }): Promise<{ tracked: boolean; reason?: string }> => {
|
||||
try {
|
||||
const serviceManager = ServiceManager.getInstance();
|
||||
if (!serviceManager) {
|
||||
return { tracked: false, reason: "ServiceManager not available" };
|
||||
}
|
||||
const onboardingService = serviceManager.getOnboardingService();
|
||||
const settingsService = serviceManager.getSettingsService();
|
||||
trackOnboardingStarted: procedure
|
||||
.input(
|
||||
z.object({
|
||||
platform: z.string(),
|
||||
resumed: z.boolean(),
|
||||
resumedFrom: z.string().optional(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input }): Promise<void> => {
|
||||
const serviceManager = ServiceManager.getInstance();
|
||||
const telemetryService = serviceManager?.getService("telemetryService");
|
||||
telemetryService?.trackOnboardingStarted(input);
|
||||
}),
|
||||
|
||||
if (!onboardingService || !settingsService) {
|
||||
return { tracked: false, reason: "Services not available" };
|
||||
}
|
||||
/**
|
||||
* Track onboarding screen viewed event
|
||||
*/
|
||||
trackOnboardingScreenViewed: procedure
|
||||
.input(
|
||||
z.object({
|
||||
screen: z.string(),
|
||||
index: z.number(),
|
||||
total: z.number(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input }): Promise<void> => {
|
||||
const serviceManager = ServiceManager.getInstance();
|
||||
const telemetryService = serviceManager?.getService("telemetryService");
|
||||
telemetryService?.trackOnboardingScreenViewed(input);
|
||||
}),
|
||||
|
||||
// Check if telemetry is enabled
|
||||
const telemetrySettings =
|
||||
await settingsService.getTelemetrySettings();
|
||||
if (telemetrySettings?.enabled === false) {
|
||||
return { tracked: false, reason: "telemetry_disabled" };
|
||||
}
|
||||
/**
|
||||
* Track onboarding features selected event
|
||||
*/
|
||||
trackOnboardingFeaturesSelected: procedure
|
||||
.input(
|
||||
z.object({
|
||||
features: z.array(z.string()),
|
||||
count: z.number(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input }): Promise<void> => {
|
||||
const serviceManager = ServiceManager.getInstance();
|
||||
const telemetryService = serviceManager?.getService("telemetryService");
|
||||
telemetryService?.trackOnboardingFeaturesSelected(input);
|
||||
}),
|
||||
|
||||
// Track the event
|
||||
onboardingService.trackEvent(input.eventName, input.properties);
|
||||
logger.main.debug("Tracked onboarding event:", input);
|
||||
/**
|
||||
* Track onboarding discovery selected event
|
||||
*/
|
||||
trackOnboardingDiscoverySelected: procedure
|
||||
.input(
|
||||
z.object({
|
||||
source: z.string(),
|
||||
details: z.string().optional(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input }): Promise<void> => {
|
||||
const serviceManager = ServiceManager.getInstance();
|
||||
const telemetryService = serviceManager?.getService("telemetryService");
|
||||
telemetryService?.trackOnboardingDiscoverySelected(input);
|
||||
}),
|
||||
|
||||
return { tracked: true };
|
||||
} catch (error) {
|
||||
logger.main.error("Failed to track onboarding event:", error);
|
||||
return { tracked: false, reason: "error" };
|
||||
}
|
||||
},
|
||||
),
|
||||
/**
|
||||
* Track onboarding model selected event
|
||||
*/
|
||||
trackOnboardingModelSelected: procedure
|
||||
.input(
|
||||
z.object({
|
||||
model_type: z.string(),
|
||||
recommendation_followed: z.boolean(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input }): Promise<void> => {
|
||||
const serviceManager = ServiceManager.getInstance();
|
||||
const telemetryService = serviceManager?.getService("telemetryService");
|
||||
telemetryService?.trackOnboardingModelSelected(input);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Track onboarding completed event
|
||||
*/
|
||||
trackOnboardingCompleted: procedure
|
||||
.input(
|
||||
z.object({
|
||||
version: z.number(),
|
||||
features_selected: z.array(z.string()),
|
||||
discovery_source: z.string().optional(),
|
||||
model_type: z.string(),
|
||||
recommendation_followed: z.boolean(),
|
||||
skipped_screens: z.array(z.string()).optional(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input }): Promise<void> => {
|
||||
const serviceManager = ServiceManager.getInstance();
|
||||
const telemetryService = serviceManager?.getService("telemetryService");
|
||||
telemetryService?.trackOnboardingCompleted(input);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Complete onboarding and save final state
|
||||
|
|
|
|||
|
|
@ -158,11 +158,6 @@ export const OnboardingPreferencesSchema = z.object({
|
|||
followedRecommendation: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const AnalyticsEventSchema = z.object({
|
||||
eventName: z.string(),
|
||||
properties: z.record(z.any()),
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Type Guards
|
||||
// ============================================================================
|
||||
|
|
|
|||
76
apps/desktop/src/types/telemetry-events.ts
Normal file
76
apps/desktop/src/types/telemetry-events.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* Telemetry Event Type Definitions
|
||||
*
|
||||
* Each event tracked in the application should have a corresponding interface here.
|
||||
* These interfaces ensure type safety when calling telemetry methods.
|
||||
*
|
||||
* Naming conventions:
|
||||
* - Event names: snake_case with domain prefix (e.g., onboarding_started)
|
||||
* - Properties: snake_case for consistency
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// Onboarding Events
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Fired when user begins onboarding flow
|
||||
*/
|
||||
export interface OnboardingStartedEvent {
|
||||
platform: string;
|
||||
resumed: boolean;
|
||||
resumedFrom?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when user views an onboarding screen
|
||||
*/
|
||||
export interface OnboardingScreenViewedEvent {
|
||||
screen: string;
|
||||
index: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when user selects feature interests
|
||||
*/
|
||||
export interface OnboardingFeaturesSelectedEvent {
|
||||
features: string[];
|
||||
count: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when user selects how they discovered the app
|
||||
*/
|
||||
export interface OnboardingDiscoverySelectedEvent {
|
||||
source: string;
|
||||
details?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when user selects their preferred model type
|
||||
*/
|
||||
export interface OnboardingModelSelectedEvent {
|
||||
model_type: string;
|
||||
recommendation_followed: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when user completes the onboarding flow
|
||||
*/
|
||||
export interface OnboardingCompletedEvent {
|
||||
version: number;
|
||||
features_selected: string[];
|
||||
discovery_source?: string;
|
||||
model_type: string;
|
||||
recommendation_followed: boolean;
|
||||
skipped_screens?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when user abandons the onboarding flow
|
||||
*/
|
||||
export interface OnboardingAbandonedEvent {
|
||||
last_screen: string;
|
||||
timestamp: string;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue