🎯 Рекомендуемые коллекции
Балансированные коллекции примеров кода из различных категорий, которые вы можете исследовать
Аналитика Пользовательского Опыта FullStory
Примеры FullStory для записи сессий, тепловых карт, воронок конверсии и анализа поведения пользователей
💻 FullStory Hello World javascript
🟢 simple
⭐
Базовая настройка FullStory с записью сессий и идентификацией пользователей
⏱️ 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
});
}
});
💻 Воронка Конверсии E-commerce typescript
🟡 intermediate
⭐⭐⭐
Отслеживание пути пользователя через процесс оформления заказа с анализом конверсии
⏱️ 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 }
💻 Интеграция React с Hooks typescript
🟡 intermediate
⭐⭐⭐
Современная интеграция React с hooks для отслеживания FullStory и анализа поведения пользователей
⏱️ 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 }
💻 Продвинутая Сегментация Пользователей typescript
🔴 complex
⭐⭐⭐⭐⭐
Создание сегментов пользователей на основе поведения, демографии и паттернов вовлеченности
⏱️ 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 }