The T1 Component Library includes a powerful interaction tracking system that monitors user behavior, provides real-time analytics, and supports both anonymous and authenticated users.
Overview
The tracking system captures component interactions throughout your application and provides:
- Real-time analytics with automatic statistics updates
- User segmentation (anonymous vs. registered users)
- Server-side persistence with MongoDB
- Dashboard integration for viewing analytics
- CSV export functionality for data analysis
Architecture
The tracking system consists of two main parts:
- Client-side Context (
InteractionContext.tsx) - Manages local state and sends events to the server
- Server-side Service (
tracking.service.ts) - Persists interactions and generates statistics
Getting Started
Setup
Wrap your application with the InteractionProvider:
import { InteractionProvider } from './context/InteractionContext';
function App() {
return (
<InteractionProvider>
{/* Your app components */}
</InteractionProvider>
);
}
The InteractionProvider should be placed inside AuthProvider and QueryClientProvider to access authentication state and React Query functionality.
Track Interactions
Use the useInteractions hook to track user interactions:
import { useInteractions } from './context/InteractionContext';
function MyButton() {
const { trackInteraction } = useInteractions();
const handleClick = () => {
trackInteraction('MyButton', 'click');
// Your button logic
};
return (
<button onClick={handleClick}>
Click me
</button>
);
}
Tracking API
The useInteractions hook provides the following:
| Property | Type | Description |
|---|
trackInteraction | (componentName: string, action: string) => void | Track a user interaction |
totalInteractions | number | Count of interactions in current session |
serverStats | ServerStats | undefined | Aggregated statistics from the server |
isLoadingServerStats | boolean | Loading state for initial stats fetch |
isRefetchingServerStats | boolean | Loading state for stats refresh |
Server Statistics
The serverStats object contains:
interface ServerStats {
totalInteracciones: number;
porComponente: Array<{ _id: string; count: number }>;
porAccion: Array<{ _id: { componente: string; accion: string }; count: number }>;
porTipoUsuario: Array<{ _id: string; count: number }>;
}
How It Works
Interaction Captured
When you call trackInteraction(), the event is immediately stored in local state and displayed in the session counter.
Server Transmission
The interaction is sent to the server via a React Query mutation, including the component name, action, timestamp, and user type.
User Type Detection
The system automatically determines if the user is ‘anonymous’ or ‘registered’ based on authentication state.
Server Persistence
The server saves the interaction to MongoDB with full metadata including timestamps and user information.
Statistics Update
After successful tracking, statistics are automatically refreshed to reflect the latest data.
Tracked Data
Each interaction record contains:
| Field | Type | Description |
|---|
nombre | string | Component name being tracked |
accion | string | Action performed (e.g., ‘click’, ‘hover’, ‘submit’) |
timestamp | Date | When the interaction occurred |
tipo_usuario | 'anonymous' | 'registered' | User authentication status |
usuario | string | undefined | User ID if authenticated |
Implementation Examples
import { useInteractions } from './context/InteractionContext';
function DownloadButton() {
const { trackInteraction } = useInteractions();
return (
<button
onClick={() => {
trackInteraction('DownloadButton', 'click');
// Handle download
}}
>
Download
</button>
);
}
function ContactForm() {
const { trackInteraction } = useInteractions();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
trackInteraction('ContactForm', 'submit');
// Process form
};
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
<button type="submit">Submit</button>
</form>
);
}
Component Visibility Tracking
import { useEffect } from 'react';
import { useInteractions } from './context/InteractionContext';
function Hero() {
const { trackInteraction } = useInteractions();
useEffect(() => {
trackInteraction('Hero', 'view');
}, []);
return <div>Hero Section</div>;
}
Multi-Action Component
function VideoPlayer() {
const { trackInteraction } = useInteractions();
return (
<div>
<video
onPlay={() => trackInteraction('VideoPlayer', 'play')}
onPause={() => trackInteraction('VideoPlayer', 'pause')}
onEnded={() => trackInteraction('VideoPlayer', 'complete')}
/>
</div>
);
}
Analytics Dashboard
Display Statistics
Create a dashboard to visualize interaction data:
import { useInteractions } from './context/InteractionContext';
function AnalyticsDashboard() {
const { serverStats, isLoadingServerStats } = useInteractions();
if (isLoadingServerStats) {
return <div>Loading analytics...</div>;
}
return (
<div>
<h2>Total Interactions: {serverStats?.totalInteracciones}</h2>
<section>
<h3>Top Components</h3>
<ul>
{serverStats?.porComponente.map((item) => (
<li key={item._id}>
{item._id}: {item.count} interactions
</li>
))}
</ul>
</section>
<section>
<h3>User Types</h3>
<ul>
{serverStats?.porTipoUsuario.map((item) => (
<li key={item._id}>
{item._id}: {item.count} interactions
</li>
))}
</ul>
</section>
</div>
);
}
Real-time Updates
The statistics automatically refresh on window focus:
// From InteractionContext.tsx:41-54
const {
data: serverStats,
isLoading: isLoadingServerStats,
isRefetching: isRefetchingServerStats,
} = useQuery({
queryKey: ['stats'],
queryFn: async () => {
const response = await api.getStats();
return response.data;
},
refetchOnWindowFocus: true,
staleTime: 3000,
});
Server-Side Features
Statistics Generation
The server provides aggregated statistics using MongoDB aggregation pipelines:
// From tracking.service.ts:49-72
getStats: async (): Promise<TrackingStats> => {
const [totalInteracciones, porComponente, porAccion, porTipoUsuario] =
await Promise.all([
Tracking.countDocuments(),
Tracking.aggregate([
{ $group: { _id: '$nombre', count: { $sum: 1 } } },
{ $sort: { count: -1 } },
{ $limit: 10 }
]),
Tracking.aggregate([
{
$group: {
_id: { componente: '$nombre', accion: '$accion' },
count: { $sum: 1 }
}
},
{ $sort: { '_id.componente': 1, count: -1 } }
]),
Tracking.aggregate([
{ $group: { _id: '$tipo_usuario', count: { $sum: 1 } } }
])
]);
return { totalInteracciones, porComponente, porAccion, porTipoUsuario };
}
Paginated Data Access
Retrieve paginated interaction records:
// From tracking.service.ts:74-108
getPaginated: async (page: number = 1, limit: number = 10) => {
const skip = (page - 1) * limit;
const [trackings, total] = await Promise.all([
Tracking.find()
.sort({ timestamp: -1 })
.skip(skip)
.limit(limit)
.populate('usuario', 'nombre'),
Tracking.countDocuments()
]);
const totalPages = Math.ceil(total / limit);
return {
data: trackings.map((tracking) => ({
id: tracking._id.toString(),
nombre_componente: tracking.nombre,
accion: tracking.accion,
timestamp: tracking.timestamp,
tipo_usuario: tracking.tipo_usuario,
nombre_usuario: tracking.usuario?.nombre || null
})),
pagination: {
page,
limit,
total,
totalPages,
hasNextPage: page < totalPages,
hasPrevPage: page > 1
}
};
}
Data Export
Export all tracking data for analysis:
// From tracking.service.ts:110-116
export: async (): Promise<ITracking[]> => {
const trackings = await Tracking.find()
.sort({ timestamp: -1 })
.populate('usuario', 'nombre email');
return trackings;
}
Best Practices
Track meaningful interactions - Focus on user actions that provide valuable insights rather than tracking everything.
- Use descriptive names - Component names and actions should be clear and consistent
- Track user flows - Monitor complete user journeys, not just individual clicks
- Respect privacy - Only track necessary data and comply with privacy regulations
- Monitor performance - Be mindful of tracking overhead in high-traffic components
- Analyze patterns - Regularly review analytics to improve UX
Common Tracking Patterns
E-commerce Actions
trackInteraction('ProductCard', 'view');
trackInteraction('ProductCard', 'add-to-cart');
trackInteraction('Checkout', 'initiate');
trackInteraction('Checkout', 'complete');
Content Engagement
trackInteraction('Article', 'view');
trackInteraction('Article', 'scroll-50');
trackInteraction('Article', 'scroll-100');
trackInteraction('ShareButton', 'click');
Navigation Patterns
trackInteraction('MainNav', 'menu-open');
trackInteraction('MainNav', 'products-click');
trackInteraction('SearchBar', 'search');
trackInteraction('Footer', 'newsletter-signup');
Error Handling
The tracking system includes built-in error handling:
// From InteractionContext.tsx:56-71
const trackMutation = useMutation({
mutationFn: (params: { nombre: string; accion: string }) => {
return api.trackInteraction({
nombre: params.nombre,
accion: params.accion,
tipo_usuario: isAuthenticated ? 'registered' : 'anonymous',
usuario: isAuthenticated ? user?.id : undefined,
});
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['stats'] });
},
onError: (error) => {
console.error('Error al enviar interacción:', error);
},
});
Failed tracking attempts are logged but don’t block the user experience. The app continues to function even if tracking fails.
Source Code References
- Interaction Context:
client/app/context/InteractionContext.tsx
- Tracking Service:
server/src/services/tracking.service.ts
- Tracking Model:
server/src/models/Tracking.model.ts
- Interaction Hook:
client/app/context/InteractionContext.tsx:102-108