🎯 Exemples recommandés
Balanced sample collections from various categories for you to explore
Analyse d'Expérience Utilisateur FullStory
Exemples FullStory pour enregistrement de session, cartes thermiques, entonnoirs de conversion et analyse du comportement utilisateur
💻 FullStory Hello World javascript
🟢 simple
⭐
Configuration de base FullStory avec enregistrement de session et identification utilisateur
⏱️ 10 min
🏷️ frontend, monitoring, analytics, ux
Prerequisites:
JavaScript basics, HTML/CSS, Browser APIs
// FullStory Hello World - Basic User Experience Analytics
// Simple session recording and user tracking setup
// Initialize FullStory with your organization ID
window['_fs_debug'] = false
window['_fs_host'] = 'fullstory.com'
window['_fs_org'] = 'YOUR-ORG-ID'
window['_fs_namespace'] = 'FS'
;(function(m,n,e,t,l,o,g,y){
if (e in m) {if(m.console && m.console.log) { m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].');} return;}
g=m[e]=function(a,b,s){g.q?g.q.push([a,b,s]):g._api(a,b,s);};g.q=[];
o=n.createElement(t);o.async=1;o.src='https://'+_fs_host+'/s/fs.js';
y=n.getElementsByTagName(t)[0];y.parentNode.insertBefore(o,y);
g.identify=function(i,v,s){g(l,{uid:i},s);if(v)g(l,v,s)};g.setUserVars=function(v,s){g(l,v,s)};g.event=function(i,v,s){g('event',{n:i,p:v},s)};
g.shutdown=function(){g("rec",!1)};g.restart=function(){g("rec",!0)};
g.consent=function(a){g("consent",!a)};g.identifyAccount=function(i,v){o='account';v=v||{};v.acctId=i;g(o,v)};
g.clearUserCookie=function(){};
})(window,document,window['_fs_namespace'],'script','user');
// Identify the current user
FS.identify('user-123', {
displayName: 'John Doe',
email: '[email protected]',
plan: 'premium',
signUpDate: '2023-01-15',
lastLogin: new Date().toISOString()
});
// Track custom events
FS.event('User signed up', {
method: 'email',
referralSource: 'google',
userAgent: navigator.userAgent
});
// Track page views
FS.event('Page Viewed', {
page: window.location.pathname,
title: document.title,
referrer: document.referrer
});
console.log('FullStory initialized and recording!');
// Track form submissions
document.addEventListener('submit', function(event) {
const form = event.target;
if (form.tagName === 'FORM') {
FS.event('Form Submitted', {
formId: form.id,
formName: form.name,
action: form.action
});
}
});
// Track clicks on important elements
document.addEventListener('click', function(event) {
const element = event.target;
if (element.matches('button, a, input[type="button"], input[type="submit"]')) {
FS.event('Button Clicked', {
text: element.textContent || element.value,
className: element.className,
id: element.id
});
}
});
💻 Entonnoir de Conversion E-commerce typescript
🟡 intermediate
⭐⭐⭐
Suivre le parcours utilisateur à travers le processus de checkout avec analyse de conversion
⏱️ 30 min
🏷️ frontend, analytics, e-commerce, conversion
Prerequisites:
JavaScript/TypeScript, E-commerce concepts, Event tracking, FullStory API
// FullStory E-commerce Conversion Funnel Analysis
// Track user journey through checkout process and identify drop-off points
interface ConversionEvent {
step: string
userId: string
sessionId: string
timestamp: number
properties: Record<string, any>
}
interface FunnelStep {
name: string
stepOrder: number
eventTrigger: string
properties: string[]
}
class EcommerceFunnelTracker {
private funnelSteps: FunnelStep[] = [
{
name: 'Product View',
stepOrder: 1,
eventTrigger: 'Product Viewed',
properties: ['productId', 'productName', 'price', 'category']
},
{
name: 'Add to Cart',
stepOrder: 2,
eventTrigger: 'Product Added to Cart',
properties: ['productId', 'quantity', 'price']
},
{
name: 'View Cart',
stepOrder: 3,
eventTrigger: 'Cart Viewed',
properties: ['cartValue', 'itemCount']
},
{
name: 'Checkout Started',
stepOrder: 4,
eventTrigger: 'Checkout Started',
properties: ['cartValue', 'itemCount', 'paymentMethod']
},
{
name: 'Payment Info',
stepOrder: 5,
eventTrigger: 'Payment Info Entered',
properties: ['paymentMethod', 'billingAddress']
},
{
name: 'Order Completed',
stepOrder: 6,
eventTrigger: 'Order Completed',
properties: ['orderId', 'totalAmount', 'items']
}
]
private currentStep: number = 0
private userJourney: ConversionEvent[] = []
constructor() {
this.initializeTracking()
}
private initializeTracking() {
// Track product views
this.trackProductViews()
// Track cart interactions
this.trackCartInteractions()
// Track checkout process
this.trackCheckoutProcess()
// Track order completion
this.trackOrderCompletion()
}
private trackProductViews() {
// Track when product pages are viewed
const trackProductView = (productId: string, productName: string, price: number, category: string) => {
this.recordFunnelEvent('Product Viewed', {
productId,
productName,
price,
category,
timestamp: Date.now()
})
}
// Example: Call this when product page loads
// trackProductView('prod-123', 'Wireless Headphones', 99.99, 'Electronics')
}
private trackCartInteractions() {
// Track add to cart events
window.addEventListener('click', (event) => {
const target = event.target as HTMLElement
if (target.matches('[data-add-to-cart]')) {
const productId = target.getAttribute('data-product-id')
const productName = target.getAttribute('data-product-name')
const price = parseFloat(target.getAttribute('data-price') || '0')
const quantity = parseInt(target.getAttribute('data-quantity') || '1')
this.recordFunnelEvent('Product Added to Cart', {
productId,
productName,
price,
quantity,
timestamp: Date.now()
})
}
})
// Track cart view
const trackCartView = () => {
const cartItems = this.getCartItems()
const cartValue = cartItems.reduce((total, item) => total + (item.price * item.quantity), 0)
this.recordFunnelEvent('Cart Viewed', {
cartValue,
itemCount: cartItems.length,
items: cartItems,
timestamp: Date.now()
})
}
// Call trackCartView() when cart page is visited
}
private trackCheckoutProcess() {
// Track checkout start
const trackCheckoutStart = () => {
const cartItems = this.getCartItems()
const cartValue = cartItems.reduce((total, item) => total + (item.price * item.quantity), 0)
this.recordFunnelEvent('Checkout Started', {
cartValue,
itemCount: cartItems.length,
items: cartItems,
timestamp: Date.now()
})
}
// Track payment method selection
const trackPaymentMethod = (paymentMethod: string) => {
this.recordFunnelEvent('Payment Method Selected', {
paymentMethod,
timestamp: Date.now()
})
}
// Track shipping info entered
const trackShippingInfo = (shippingData: any) => {
this.recordFunnelEvent('Shipping Info Entered', {
shippingMethod: shippingData.method,
shippingAddress: shippingData.address,
timestamp: Date.now()
})
}
// Track payment info entered
const trackPaymentInfo = (paymentMethod: string) => {
this.recordFunnelEvent('Payment Info Entered', {
paymentMethod,
timestamp: Date.now()
})
}
}
private trackOrderCompletion() {
const trackOrderCompletion = (orderData: {
orderId: string
totalAmount: number
items: any[]
paymentMethod: string
shippingMethod: string
}) => {
this.recordFunnelEvent('Order Completed', {
orderId: orderData.orderId,
totalAmount: orderData.totalAmount,
items: orderData.items,
paymentMethod: orderData.paymentMethod,
shippingMethod: orderData.shippingMethod,
timestamp: Date.now()
})
// Track conversion value
this.recordConversionValue(orderData.totalAmount)
}
}
private recordFunnelEvent(eventName: string, properties: Record<string, any>) {
// Send to FullStory
FS.event(eventName, properties)
// Record in internal journey
const event: ConversionEvent = {
step: eventName,
userId: this.getCurrentUserId(),
sessionId: this.getCurrentSessionId(),
timestamp: properties.timestamp,
properties
}
this.userJourney.push(event)
this.updateCurrentStep(eventName)
console.log('Funnel event recorded:', event)
}
private updateCurrentStep(eventName: string) {
const stepIndex = this.funnelSteps.findIndex(step => step.eventTrigger === eventName)
if (stepIndex !== -1 && stepIndex > this.currentStep) {
this.currentStep = stepIndex
}
}
private recordConversionValue(value: number) {
FS.event('Conversion Value Recorded', {
value,
currency: 'USD',
timestamp: Date.now()
})
}
private getCartItems() {
// Implement cart retrieval logic
// This would typically get cart data from your state management or API
return [
{
productId: 'prod-123',
productName: 'Wireless Headphones',
price: 99.99,
quantity: 1
}
]
}
private getCurrentUserId(): string {
// Get current user ID from your authentication system
return 'user-123'
}
private getCurrentSessionId(): string {
// FullStory provides session ID
return (window as any).FS?.getCurrentSession?.()?.session?.id || 'unknown'
}
// Generate funnel analysis
public generateFunnelReport() {
const funnelData = this.analyzeFunnelPerformance()
FS.event('Funnel Analysis Generated', {
totalSteps: this.funnelSteps.length,
completionRate: funnelData.completionRate,
dropOffPoints: funnelData.dropOffPoints,
averageTime: funnelData.averageTime,
timestamp: Date.now()
})
return funnelData
}
private analyzeFunnelPerformance() {
const stepCounts: Record<string, number> = {}
const stepTimes: Record<string, number[]> = {}
// Count events per step and calculate times
this.funnelSteps.forEach(step => {
const stepEvents = this.userJourney.filter(event => event.step === step.eventTrigger)
stepCounts[step.name] = stepEvents.length
// Calculate time between steps
if (step.stepOrder > 1) {
const previousStep = this.funnelSteps.find(s => s.stepOrder === step.stepOrder - 1)
if (previousStep) {
const timeDiffs: number[] = []
stepEvents.forEach(currentEvent => {
const previousEvent = this.userJourney.find(
event => event.step === previousStep.eventTrigger &&
event.userId === currentEvent.userId &&
event.timestamp < currentEvent.timestamp
)
if (previousEvent) {
timeDiffs.push(currentEvent.timestamp - previousEvent.timestamp)
}
})
stepTimes[step.name] = timeDiffs
}
}
})
// Calculate drop-off points
const dropOffPoints: string[] = []
for (let i = 0; i < this.funnelSteps.length - 1; i++) {
const currentStepCount = stepCounts[this.funnelSteps[i].name] || 0
const nextStepCount = stepCounts[this.funnelSteps[i + 1].name] || 0
if (nextStepCount < currentStepCount) {
const dropOffRate = ((currentStepCount - nextStepCount) / currentStepCount) * 100
dropOffPoints.push(`${this.funnelSteps[i].name} → ${this.funnelSteps[i + 1].name}: ${dropOffRate.toFixed(1)}%`)
}
}
// Calculate overall completion rate
const firstStepCount = stepCounts[this.funnelSteps[0].name] || 1
const lastStepCount = stepCounts[this.funnelSteps[this.funnelSteps.length - 1].name] || 0
const completionRate = (lastStepCount / firstStepCount) * 100
// Calculate average time per step
const averageTimes: Record<string, number> = {}
Object.entries(stepTimes).forEach(([stepName, times]) => {
if (times.length > 0) {
averageTimes[stepName] = times.reduce((sum, time) => sum + time, 0) / times.length
}
})
return {
stepCounts,
dropOffPoints,
completionRate,
averageTime: averageTimes,
totalUsers: this.userJourney.filter((event, index, arr) =>
arr.findIndex(e => e.userId === event.userId) === index
).length
}
}
// Track user abandonment
public trackAbandonment() {
const abandonEvent = () => {
const currentStepName = this.funnelSteps[this.currentStep]?.name || 'Unknown'
FS.event('Funnel Abandoned', {
currentStep: currentStepName,
stepOrder: this.currentStep + 1,
totalSteps: this.funnelSteps.length,
journeyLength: this.userJourney.length,
timestamp: Date.now()
})
}
// Track abandonment on page unload
window.addEventListener('beforeunload', abandonEvent)
}
}
// Initialize the funnel tracker
const funnelTracker = new EcommerceFunnelTracker()
funnelTracker.trackAbandonment()
// Example usage
// funnelTracker.trackProductView('prod-123', 'Wireless Headphones', 99.99, 'Electronics')
// funnelTracker.generateFunnelReport()
export { EcommerceFunnelTracker, funnelTracker }
💻 Intégration React avec Hooks typescript
🟡 intermediate
⭐⭐⭐
Intégration React moderne avec hooks pour suivi FullStory et analyse du comportement utilisateur
⏱️ 35 min
🏷️ react, frontend, hooks, analytics, user-experience
Prerequisites:
React hooks, TypeScript, FullStory API, Event tracking
// FullStory React Integration with Hooks
// Modern React hooks for comprehensive user experience tracking
import React, { useEffect, useCallback, useState, useRef } from 'react'
// Type definitions
interface UserTraits {
displayName?: string
email?: string
plan?: string
signUpDate?: string
[key: string]: any
}
interface EventProperties {
[key: string]: any
}
// Custom hook for FullStory user identification
export const useFullStoryUser = (userId: string, traits: UserTraits = {}) => {
useEffect(() => {
if (typeof window !== 'undefined' && window.FS) {
window.FS.identify(userId, traits)
}
}, [userId, traits])
}
// Custom hook for FullStory event tracking
export const useFullStoryEvent = () => {
const trackEvent = useCallback((eventName: string, properties: EventProperties = {}) => {
if (typeof window !== 'undefined' && window.FS) {
window.FS.event(eventName, {
...properties,
timestamp: Date.now(),
url: window.location.href,
userAgent: navigator.userAgent
})
}
}, [])
return { trackEvent }
}
// Custom hook for tracking page views
export const useFullStoryPageView = (pageName?: string) => {
const { trackEvent } = useFullStoryEvent()
useEffect(() => {
const pageNameToUse = pageName || window.location.pathname
trackEvent('Page Viewed', {
page: pageNameToUse,
title: document.title,
referrer: document.referrer
})
}, [trackEvent, pageName])
}
// Custom hook for tracking form interactions
export const useFullStoryFormTracking = (formId: string) => {
const { trackEvent } = useFullStoryEvent()
const [formData, setFormData] = useState<Record<string, any>>({})
const trackFieldFocus = useCallback((fieldName: string, fieldType: string) => {
trackEvent('Form Field Focused', {
formId,
fieldName,
fieldType,
timestamp: Date.now()
})
}, [formId, trackEvent])
const trackFieldBlur = useCallback((fieldName: string, value: string) => {
setFormData(prev => ({ ...prev, [fieldName]: value }))
trackEvent('Form Field Blurred', {
formId,
fieldName,
hasValue: value.length > 0,
timestamp: Date.now()
})
}, [formId, trackEvent])
const trackFormSubmit = useCallback((data: Record<string, any>) => {
trackEvent('Form Submitted', {
formId,
fieldCount: Object.keys(data).length,
hasValidationErrors: Object.values(data).some(val => !val),
timestamp: Date.now()
})
}, [formId, trackEvent])
const trackValidationError = useCallback((fieldName: string, errorMessage: string) => {
trackEvent('Form Validation Error', {
formId,
fieldName,
errorMessage,
timestamp: Date.now()
})
}, [formId, trackEvent])
return {
formData,
trackFieldFocus,
trackFieldBlur,
trackFormSubmit,
trackValidationError
}
}
// Custom hook for tracking user engagement
export const useFullStoryEngagement = () => {
const { trackEvent } = useFullStoryEvent()
const [sessionStartTime] = useState(Date.now())
const [lastActivityTime, setLastActivityTime] = useState(Date.now())
const [totalActiveTime, setTotalActiveTime] = useState(0)
const trackClick = useCallback((element: HTMLElement, x: number, y: number) => {
trackEvent('Element Clicked', {
tagName: element.tagName,
className: element.className,
id: element.id,
textContent: element.textContent?.slice(0, 50),
x,
y,
timestamp: Date.now()
})
}, [trackEvent])
const trackScroll = useCallback((scrollPercentage: number, direction: 'up' | 'down') => {
trackEvent('Page Scrolled', {
scrollPercentage,
direction,
timestamp: Date.now()
})
}, [trackEvent])
const trackEngagementEnd = useCallback(() => {
const sessionDuration = Date.now() - sessionStartTime
trackEvent('Session Ended', {
totalDuration: sessionDuration,
activeTime: totalActiveTime,
lastActivity: Date.now() - lastActivityTime,
timestamp: Date.now()
})
}, [sessionStartTime, totalActiveTime, lastActivityTime, trackEvent])
// Update activity tracking
useEffect(() => {
const updateActivity = () => {
const now = Date.now()
const inactiveTime = now - lastActivityTime
if (inactiveTime < 30000) { // Consider active if less than 30 seconds inactive
setTotalActiveTime(prev => prev + Math.min(1000, inactiveTime))
}
setLastActivityTime(now)
}
// Track user interactions
const events = ['click', 'scroll', 'keydown', 'mousemove']
events.forEach(event => {
document.addEventListener(event, updateActivity, { passive: true })
})
// Track session end
window.addEventListener('beforeunload', trackEngagementEnd)
return () => {
events.forEach(event => {
document.removeEventListener(event, updateActivity)
})
window.removeEventListener('beforeunload', trackEngagementEnd)
}
}, [lastActivityTime, trackEngagementEnd])
return {
trackClick,
trackScroll,
trackEngagementEnd
}
}
// Custom hook for A/B testing integration
export const useFullStoryABTesting = (experimentName: string, variant?: string) => {
const { trackEvent } = useFullStoryEvent()
useEffect(() => {
if (variant) {
trackEvent('A/B Test Started', {
experimentName,
variant,
timestamp: Date.now()
})
// Set user properties for segmentation
if (window.FS) {
window.FS.setUserVars({
[`ab_${experimentName}`]: variant
})
}
}
}, [experimentName, variant, trackEvent])
const trackConversion = useCallback((conversionType: string, value?: number) => {
trackEvent('A/B Test Conversion', {
experimentName,
variant,
conversionType,
value,
timestamp: Date.now()
})
}, [experimentName, variant, trackEvent])
return { trackConversion }
}
// Higher-order component for automatic tracking
export const withFullStoryTracking = <P extends object>(
WrappedComponent: React.ComponentType<P>,
options: {
trackPageView?: boolean
trackClicks?: boolean
componentName?: string
} = {}
) => {
const HOC = (props: P) => {
const { trackEvent } = useFullStoryEvent()
const { trackClick } = useFullStoryEngagement()
const elementRef = useRef<HTMLDivElement>(null)
// Track page view
useFullStoryPageView(options.componentName)
// Track clicks on component
useEffect(() => {
if (options.trackClicks && elementRef.current) {
const handleClick = (event: MouseEvent) => {
const target = event.target as HTMLElement
trackClick(target, event.clientX, event.clientY)
}
const element = elementRef.current
element.addEventListener('click', handleClick)
return () => {
element.removeEventListener('click', handleClick)
}
}
}, [trackClick])
return (
<div ref={elementRef}>
<WrappedComponent {...props} />
</div>
)
}
HOC.displayName = `withFullStoryTracking(${WrappedComponent.displayName || WrappedComponent.name})`
return HOC
}
// Example usage components
// User identification component
const UserProfile: React.FC<{ userId: string; name: string; email: string }> = ({ userId, name, email }) => {
useFullStoryUser(userId, {
displayName: name,
email,
plan: 'premium',
lastLogin: new Date().toISOString()
})
return <div>Welcome, {name}!</div>
}
// Contact form with tracking
const ContactForm: React.FC = () => {
const { formData, trackFieldFocus, trackFieldBlur, trackFormSubmit, trackValidationError } = useFullStoryFormTracking('contact-form')
const { trackEvent } = useFullStoryEvent()
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault()
// Validate form
if (!formData.name || !formData.email) {
trackValidationError('name', !formData.name ? 'Name is required' : '')
trackValidationError('email', !formData.email ? 'Email is required' : '')
return
}
trackFormSubmit(formData)
trackEvent('Contact Form Submitted', {
name: formData.name,
email: formData.email,
message: formData.message?.length > 0
})
// Handle form submission...
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Name"
onFocus={() => trackFieldFocus('name', 'text')}
onBlur={(e) => trackFieldBlur('name', e.target.value)}
/>
<input
type="email"
placeholder="Email"
onFocus={() => trackFieldFocus('email', 'email')}
onBlur={(e) => trackFieldBlur('email', e.target.value)}
/>
<textarea
placeholder="Message"
onFocus={() => trackFieldFocus('message', 'textarea')}
onBlur={(e) => trackFieldBlur('message', e.target.value)}
/>
<button type="submit">Send</button>
</form>
)
}
// A/B testing example
const CheckoutButton: React.FC<{ variant: 'A' | 'B' }> = ({ variant }) => {
const { trackConversion } = useFullStoryABTesting('checkout_button_test', variant)
const handleClick = () => {
trackConversion('checkout_started', 1)
// Handle checkout logic...
}
return (
<button
onClick={handleClick}
style={{
backgroundColor: variant === 'A' ? '#007bff' : '#28a745',
color: 'white',
padding: '12px 24px',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
{variant === 'A' ? 'Buy Now' : 'Proceed to Checkout'}
</button>
)
}
// Wrap a component with tracking
const TrackedComponent = withFullStoryTracking(
() => <div>This component is automatically tracked</div>,
{
trackPageView: true,
trackClicks: true,
componentName: 'TrackedComponent'
}
)
export { UserProfile, ContactForm, CheckoutButton, TrackedComponent }
💻 Segmentation Avancée d'Utilisateurs typescript
🔴 complex
⭐⭐⭐⭐⭐
Créer des segments utilisateurs basés sur les comportements, démographie et schémas d'engagement
⏱️ 60 min
🏷️ frontend, analytics, segmentation, user-behavior
Prerequisites:
TypeScript, FullStory API, Data analysis, Behavioral analytics
// FullStory Advanced User Segmentation
// Dynamic user segmentation based on behavior patterns and characteristics
interface UserSegment {
name: string
id: string
criteria: SegmentCriteria
users: string[]
properties: SegmentProperties
}
interface SegmentCriteria {
demographics?: DemographicCriteria
behavior?: BehaviorCriteria
engagement?: EngagementCriteria
technical?: TechnicalCriteria
custom?: CustomCriteria
}
interface DemographicCriteria {
country?: string[]
language?: string[]
ageRange?: { min: number; max: number }
plan?: string[]
signUpDateRange?: { start: Date; end: Date }
}
interface BehaviorCriteria {
pageViews?: {
pages: string[]
count?: { min: number; max?: number }
timeframe?: number // days
}
events?: {
types: string[]
count?: { min: number; max?: number }
timeframe?: number
}
conversions?: {
types: string[]
totalValue?: { min: number; max?: number }
timeframe?: number
}
}
interface EngagementCriteria {
sessionDuration?: { min: number; max?: number }
sessionsPerWeek?: { min: number; max?: number }
lastActivity?: { days: number }
scrollDepth?: { percentage: number }
clicksPerSession?: { min: number; max?: number }
}
interface TechnicalCriteria {
browser?: string[]
device?: string[]
os?: string[]
screenResolution?: { min: { width: number; height: number }; max?: { width: number; height: number } }
connectionSpeed?: string[]
}
interface CustomCriteria {
[key: string]: any
}
interface SegmentProperties {
size: number
conversionRate: number
averageOrderValue: number
retentionRate: number
churnRisk: 'low' | 'medium' | 'high'
lifetimeValue: number
createdAt: Date
updatedAt: Date
}
class UserSegmentationEngine {
private segments: Map<string, UserSegment> = new Map()
private userProfiles: Map<string, UserProfile> = new Map()
private eventHistory: Map<string, UserEvent[]> = new Map()
constructor() {
this.initializeDefaultSegments()
this.startRealTimeSegmentation()
}
private initializeDefaultSegments() {
// Power users segment
this.createSegment({
name: 'Power Users',
id: 'power-users',
criteria: {
engagement: {
sessionDuration: { min: 600000 }, // 10 minutes
sessionsPerWeek: { min: 5 },
lastActivity: { days: 7 }
},
behavior: {
pageViews: {
count: { min: 50 },
timeframe: 30
},
events: {
types: ['conversion', 'feature_used'],
count: { min: 10 },
timeframe: 30
}
}
},
properties: {
size: 0,
conversionRate: 0,
averageOrderValue: 0,
retentionRate: 0,
churnRisk: 'low',
lifetimeValue: 0,
createdAt: new Date(),
updatedAt: new Date()
}
})
// At-risk users segment
this.createSegment({
name: 'At Risk Users',
id: 'at-risk-users',
criteria: {
engagement: {
sessionDuration: { max: 120000 }, // 2 minutes
sessionsPerWeek: { max: 2 },
lastActivity: { days: 14 }
},
behavior: {
events: {
types: ['error', 'frustration'],
count: { min: 3 },
timeframe: 14
}
}
},
properties: {
size: 0,
conversionRate: 0,
averageOrderValue: 0,
retentionRate: 0,
churnRisk: 'high',
lifetimeValue: 0,
createdAt: new Date(),
updatedAt: new Date()
}
})
// Mobile-first users segment
this.createSegment({
name: 'Mobile-First Users',
id: 'mobile-users',
criteria: {
technical: {
device: ['mobile', 'tablet']
},
behavior: {
pageViews: {
count: { min: 20 },
timeframe: 30
}
}
},
properties: {
size: 0,
conversionRate: 0,
averageOrderValue: 0,
retentionRate: 0,
churnRisk: 'medium',
lifetimeValue: 0,
createdAt: new Date(),
updatedAt: new Date()
}
})
}
createSegment(segmentData: Omit<UserSegment, 'users'>): UserSegment {
const segment: UserSegment = {
...segmentData,
users: []
}
this.segments.set(segment.id, segment)
this.updateSegmentInFullStory(segment)
FS.event('User Segment Created', {
segmentId: segment.id,
segmentName: segment.name,
criteria: segment.criteria,
timestamp: Date.now()
})
return segment
}
private updateSegmentInFullStory(segment: UserSegment) {
// Update FullStory with segment information
segment.users.forEach(userId => {
const userProfile = this.userProfiles.get(userId)
if (userProfile) {
FS.setUserVars({
userId,
segments: userProfile.segments.map(segId => this.segments.get(segId)?.name).filter(Boolean)
})
}
})
}
addUserToSegment(userId: string, segmentId: string): boolean {
const segment = this.segments.get(segmentId)
if (!segment) return false
if (!segment.users.includes(userId)) {
segment.users.push(userId)
const userProfile = this.userProfiles.get(userId) || {
id: userId,
segments: [],
properties: {},
events: []
}
if (!userProfile.segments.includes(segmentId)) {
userProfile.segments.push(segmentId)
this.userProfiles.set(userId, userProfile)
}
this.updateSegmentProperties(segment)
this.updateSegmentInFullStory(segment)
FS.event('User Added to Segment', {
userId,
segmentId,
segmentName: segment.name,
totalUsers: segment.users.length,
timestamp: Date.now()
})
return true
}
return false
}
removeUserFromSegment(userId: string, segmentId: string): boolean {
const segment = this.segments.get(segmentId)
if (!segment) return false
const userIndex = segment.users.indexOf(userId)
if (userIndex !== -1) {
segment.users.splice(userIndex, 1)
const userProfile = this.userProfiles.get(userId)
if (userProfile) {
const segmentIndex = userProfile.segments.indexOf(segmentId)
if (segmentIndex !== -1) {
userProfile.segments.splice(segmentIndex, 1)
}
}
this.updateSegmentProperties(segment)
this.updateSegmentInFullStory(segment)
FS.event('User Removed from Segment', {
userId,
segmentId,
segmentName: segment.name,
totalUsers: segment.users.length,
timestamp: Date.now()
})
return true
}
return false
}
private updateSegmentProperties(segment: UserSegment) {
segment.properties.size = segment.users.length
segment.properties.updatedAt = new Date()
// Calculate conversion rate
let conversions = 0
let totalOrderValue = 0
let activeUsers = 0
segment.users.forEach(userId => {
const userEvents = this.eventHistory.get(userId) || []
// Count conversions
const userConversions = userEvents.filter(event =>
event.type.includes('conversion') || event.type.includes('purchase')
).length
if (userConversions > 0) conversions++
// Calculate order value
userEvents.forEach(event => {
if (event.properties.value) {
totalOrderValue += event.properties.value
}
})
// Check if user is active (last 30 days)
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000)
if (userEvents.some(event => event.timestamp > thirtyDaysAgo)) {
activeUsers++
}
})
segment.properties.conversionRate = (conversions / segment.users.length) * 100
segment.properties.averageOrderValue = totalOrderValue / Math.max(1, conversions)
segment.properties.retentionRate = (activeUsers / segment.users.length) * 100
// Calculate lifetime value (simplified)
segment.properties.lifetimeValue = totalOrderValue / Math.max(1, segment.users.length)
// Update churn risk based on retention
if (segment.properties.retentionRate > 80) {
segment.properties.churnRisk = 'low'
} else if (segment.properties.retentionRate > 50) {
segment.properties.churnRisk = 'medium'
} else {
segment.properties.churnRisk = 'high'
}
}
private startRealTimeSegmentation() {
// Listen for FullStory events
if (typeof window !== 'undefined' && window.FS) {
// Hook into FullStory event system
const originalEvent = window.FS.event
window.FS.event = (eventName: string, eventProperties: any) => {
// Call original event
originalEvent(eventName, eventProperties)
// Process event for segmentation
this.processEventForSegmentation(eventName, eventProperties)
}
}
}
private processEventForSegmentation(eventName: string, properties: any) {
// Get user ID from event properties or session
const userId = properties.userId || this.getCurrentUserId()
if (!userId) return
// Record event
const event: UserEvent = {
type: eventName,
timestamp: Date.now(),
properties
}
const userEvents = this.eventHistory.get(userId) || []
userEvents.push(event)
this.eventHistory.set(userId, userEvents)
// Check if user should be added to any segments
this.evaluateUserForSegments(userId)
}
private evaluateUserForSegments(userId: string) {
const userEvents = this.eventHistory.get(userId) || []
const userProfile = this.userProfiles.get(userId) || {
id: userId,
segments: [],
properties: {},
events: userEvents
}
// Evaluate against all segments
this.segments.forEach((segment, segmentId) => {
const qualifies = this.evaluateUserAgainstSegment(userId, segment.criteria)
const currentlyInSegment = segment.users.includes(userId)
if (qualifies && !currentlyInSegment) {
this.addUserToSegment(userId, segmentId)
} else if (!qualifies && currentlyInSegment) {
this.removeUserFromSegment(userId, segmentId)
}
})
}
private evaluateUserAgainstSegment(userId: string, criteria: SegmentCriteria): boolean {
const userEvents = this.eventHistory.get(userId) || []
// Evaluate demographic criteria
if (criteria.demographics) {
// Implement demographic evaluation logic
// This would require access to user demographic data
}
// Evaluate behavior criteria
if (criteria.behavior) {
if (criteria.behavior.pageViews) {
const pageViewEvents = userEvents.filter(event => event.type === 'Page Viewed')
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000)
const recentPageViews = pageViewEvents.filter(event => event.timestamp > thirtyDaysAgo)
if (criteria.behavior.pageViews.count) {
const { min, max } = criteria.behavior.pageViews.count
if (recentPageViews.length < min) return false
if (max && recentPageViews.length > max) return false
}
}
if (criteria.behavior.events) {
const matchingEvents = userEvents.filter(event =>
criteria.behavior!.events!.types.includes(event.type)
)
if (criteria.behavior.events.count) {
const { min, max } = criteria.behavior.events.count
if (matchingEvents.length < min) return false
if (max && matchingEvents.length > max) return false
}
}
}
// Evaluate engagement criteria
if (criteria.engagement) {
// Implement engagement evaluation logic
// This would require session duration tracking, frequency analysis, etc.
}
return true
}
private getCurrentUserId(): string | null {
// Get current user ID from your authentication system
// This is a placeholder implementation
return localStorage.getItem('currentUserId')
}
// Get segment analytics
getSegmentAnalytics(segmentId: string): SegmentProperties | null {
const segment = this.segments.get(segmentId)
return segment ? segment.properties : null
}
// Get all segments
getAllSegments(): UserSegment[] {
return Array.from(this.segments.values())
}
// Export segment data
exportSegmentData(segmentId: string): any {
const segment = this.segments.get(segmentId)
if (!segment) return null
return {
segment: {
id: segment.id,
name: segment.name,
criteria: segment.criteria,
properties: segment.properties
},
users: segment.users.map(userId => ({
id: userId,
profile: this.userProfiles.get(userId),
events: this.eventHistory.get(userId) || []
}))
}
}
// Create segment from FullStory search
async createSegmentFromSearch(searchQuery: string, segmentName: string): Promise<UserSegment> {
// This would use FullStory's search API to find matching users
// For now, we'll simulate with a basic implementation
const segment = this.createSegment({
name: segmentName,
id: segmentName.toLowerCase().replace(/\s+/g, '-'),
criteria: {
custom: {
fullstorySearch: searchQuery
}
},
properties: {
size: 0,
conversionRate: 0,
averageOrderValue: 0,
retentionRate: 0,
churnRisk: 'medium',
lifetimeValue: 0,
createdAt: new Date(),
updatedAt: new Date()
}
})
FS.event('Segment Created from Search', {
segmentId: segment.id,
searchQuery,
timestamp: Date.now()
})
return segment
}
}
// Type definitions
interface UserProfile {
id: string
segments: string[]
properties: Record<string, any>
events: UserEvent[]
}
interface UserEvent {
type: string
timestamp: number
properties: Record<string, any>
}
// Initialize the segmentation engine
const segmentationEngine = new UserSegmentationEngine()
// Example usage
/*
// Create a custom segment
const highValueCustomers = segmentationEngine.createSegment({
name: 'High Value Customers',
id: 'high-value-customers',
criteria: {
behavior: {
conversions: {
types: ['purchase', 'upgrade'],
totalValue: { min: 1000 },
timeframe: 90
}
},
engagement: {
sessionsPerWeek: { min: 3 },
lastActivity: { days: 7 }
}
},
properties: {
size: 0,
conversionRate: 0,
averageOrderValue: 0,
retentionRate: 0,
churnRisk: 'low',
lifetimeValue: 0,
createdAt: new Date(),
updatedAt: new Date()
}
})
// Get segment analytics
const analytics = segmentationEngine.getSegmentAnalytics('high-value-customers')
console.log('High value customers:', analytics)
*/
export { UserSegmentationEngine, segmentationEngine }