📚 Add Tracking documentation

This commit is contained in:
Daniel Dudzic 2025-06-06 14:34:55 +02:00
parent ea3e64bcb9
commit 0ba6de569b
No known key found for this signature in database
GPG key ID: 31B40D33E3465483

View file

@ -0,0 +1,393 @@
# Tracking System Architecture
## Overview
The tracking system provides comprehensive analytics for user interactions across onboarding, settings, and other flows. It features source-based field filtering, multi-funnel support, and extensible adapter architecture to prevent tracking loops and enable granular event control.
It monitors WordPress data stores rather than adding code to frontend components, ensuring comprehensive coverage of all state changes regardless of their source (user actions, API responses, system updates) while maintaining clean separation of concerns.
## Architecture Components
### 1. Registry System (`src/services/tracking/registry.js`)
The registry manages funnel registration and coordinates multiple tracking concerns without conflicts.
#### Key Features
- Registers tracking funnels with their configurations
- Maps stores to funnels to prevent duplicate subscriptions
- Provides centralized funnel validation and status monitoring
- Enables dynamic funnel activation based on conditions
#### Usage
```javascript
import { registerTrackingFunnel } from '../services/tracking/registry';
registerTrackingFunnel('onboarding', {
stores: ['wc/paypal/onboarding', 'wc/paypal/common'],
trackingCondition: {
store: 'wc/paypal/common',
selector: 'merchant',
field: 'isConnected',
expectedValue: false
},
fieldConfigs: {
'wc/paypal/onboarding': [
createFieldTrackingConfig('step', 'persistent', {
rules: { allowedSources: ['user', 'system'] }
})
]
},
debug: false
});
```
### 2. Subscription Manager (`src/services/tracking/subscription-manager.js`)
Creates unified subscriptions to WordPress data stores and routes changes to relevant funnels.
#### Key Features
- Prevents subscription conflicts when multiple funnels track the same store
- Routes store changes to relevant funnels based on configurations
- Manages funnel lifecycle (activation/deactivation based on conditions)
- Provides atomic state snapshots for consistent condition evaluation
#### How It Works
```javascript
// Single subscription handles multiple funnels
const subscription = wp.data.subscribe(() => {
const registrations = this.storeRegistrations[storeName] || [];
registrations.forEach(registration => {
this.processFunnelForStore(storeName, registration, select, store);
});
});
```
### 3. Source-Based Field Filtering
**Why Source Tracking?** Redux store subscriptions and internal system updates were creating noise in analytics by triggering tracking events for non-user actions.
#### Implementation
Field-level source rules define which change sources should trigger tracking events.
```javascript
// Field rules configuration
fieldRules: {
step: { allowedSources: ['user', 'system'] }, // Track all changes
isCasualSeller: { allowedSources: ['user'] }, // Only user changes
products: { allowedSources: ['user', 'system'] } // Track all changes
}
// Usage in components
const { setIsCasualSeller } = useOnboardingHooks();
setIsCasualSeller(true, 'user'); // Will be tracked
setIsCasualSeller(false, 'subscription'); // Will be filtered out
```
#### Source Types
- `'user'` - Direct user interactions (form inputs, button clicks)
- `'system'` - System-initiated changes (auto-progression, defaults)
- `'subscription'` - Changes from store subscriptions (filtered out)
- `'api'` - Changes from API responses
### 4. Adapter Pattern (`src/services/tracking/adapters/`)
Supports multiple tracking backends through a consistent interface.
#### Available Adapters
##### WooCommerce Tracks Adapter
```javascript
// src/services/tracking/adapters/woocommerce-tracks.js
class WooCommerceTracksAdapter {
sendEvent(eventName, properties) {
if (window.wcTracks?.recordEvent) {
window.wcTracks.recordEvent(eventName, properties);
}
}
}
```
##### Console Logger Adapter
```javascript
// src/services/tracking/adapters/console-logger.js
class ConsoleLoggerAdapter {
sendEvent(eventName, properties) {
console.log(`🎯 ${eventName}`, properties);
}
}
```
#### Creating Custom Adapters
```javascript
class CustomAnalyticsAdapter {
sendEvent(eventName, properties) {
// Your custom implementation
customAnalytics.track(eventName, properties);
}
}
// Register the adapter
trackingService.addAdapter(new CustomAnalyticsAdapter());
```
### 5. Translation Functions (`src/services/tracking/funnels/`)
Transform generic field changes into meaningful business events with rich context.
#### Example Translation
```javascript
// src/services/tracking/funnels/onboarding.js
export const translations = {
isCasualSeller: (oldValue, newValue, metadata, trackingService) => {
const accountType = newValue === true ? 'personal' : 'business';
const eventData = {
account_type: accountType,
step: metadata.step || 'unknown',
products: metadata.products || [],
timestamp: Date.now()
};
trackingService.sendToAdapters('ppcp_onboarding_account_type_select', eventData);
},
step: (oldValue, newValue, metadata, trackingService) => {
if (newValue > oldValue) {
const eventData = {
from_step: oldValue,
to_step: newValue,
account_type: metadata.isCasualSeller ? 'personal' : 'business'
};
trackingService.sendToAdapters('ppcp_onboarding_step_forward', eventData);
}
}
};
```
## Configuration
### Field Configuration Helpers
The system provides helper functions to reduce boilerplate when configuring field tracking.
```javascript
import {
createFieldTrackingConfig,
createBooleanFieldConfig,
createArrayFieldConfig,
createNestedFieldConfig
} from '../services/tracking/utils';
// Basic field configuration
createFieldTrackingConfig('step', 'persistent', {
rules: { allowedSources: ['user', 'system'] }
});
// Boolean field with user-only tracking
createBooleanFieldConfig('isCasualSeller', 'persistent', ['user']);
// Array field tracking
createArrayFieldConfig('products', 'persistent', ['user', 'system']);
// Nested field tracking
createNestedFieldConfig('merchant', 'persistent', 'isConnected', ['system']);
```
### Funnel Configuration
Complete funnel configuration example:
```javascript
const onboardingFunnelConfig = {
funnelId: 'onboarding',
stores: ['wc/paypal/onboarding', 'wc/paypal/common'],
// Only track when merchant is not connected
trackingCondition: {
store: 'wc/paypal/common',
selector: 'merchant',
field: 'isConnected',
expectedValue: false
},
// Field configurations per store
fieldConfigs: {
'wc/paypal/onboarding': [
createFieldTrackingConfig('step', 'persistent', {
rules: { allowedSources: ['user', 'system'] }
}),
createBooleanFieldConfig('isCasualSeller', 'persistent', ['user']),
createArrayFieldConfig('products', 'persistent', ['user', 'system'])
],
'wc/paypal/common': [
createNestedFieldConfig('merchant', 'persistent', 'isConnected', ['system'])
]
},
// Translation functions
translations,
// Debug mode
debug: process.env.NODE_ENV === 'development'
};
```
## Integration with Redux Stores
### Action Enhancement
PayPal store actions for tracked fields have been enhanced to support source tracking. **Note:** Source tracking support must be manually added to specific actions when you want to track new fields or extend tracking to additional data stores.
```javascript
// Before
export const setIsCasualSeller = (isCasualSeller) =>
setPersistent('isCasualSeller', isCasualSeller);
// After
export const setIsCasualSeller = (isCasualSeller, source = 'user') =>
setPersistent('isCasualSeller', isCasualSeller, source);
```
#### Adding Source Support to New Fields
To add source tracking to additional fields, you need to:
1. **Update the action creator** to accept and pass the source parameter:
```javascript
// Add source parameter to action creator
export const setNewField = (value, source = 'user') =>
setPersistent('newField', value, source);
```
2. **Update the hook** to pass source values:
```javascript
// In hooks file
const setNewField = async (value, source = 'user') => {
setNewFieldAction(value, source);
await dispatchActions.persist();
};
```
3. **Add field configuration** to your funnel:
```javascript
fieldConfigs: {
'your-store-name': [
createFieldTrackingConfig('newField', 'persistent', {
rules: { allowedSources: ['user'] }
})
]
}
```
#### Adding Source Support to New Data Stores
To extend tracking to a new data store:
1. **Enhance the base actions** (`setPersistent`/`setTransient`) to handle source:
```javascript
// In your new store's actions.js
export const setPersistent = (prop, value, source) => ({
type: ACTION_TYPES.SET_PERSISTENT,
payload: { [prop]: value },
source,
fieldName: prop,
});
```
2. **Update the reducer** to track field sources:
```javascript
// In your reducer
case ACTION_TYPES.SET_PERSISTENT:
const fieldName = action.fieldName;
let newState = changePersistent(state, action);
// Add field source tracking
if (action?.source && fieldName) {
newState.fieldSources = updateFieldSources(
newState.fieldSources,
fieldName,
action.source
);
}
return newState;
```
3. **Register the store** in your funnel configuration:
```javascript
registerTrackingFunnel('your-funnel', {
stores: ['your-new-store-name'],
// ... rest of configuration
});
```
### Reducer Enhancement
Reducers automatically handle field source tracking (only for onboarding and common data stores for now):
```javascript
case ACTION_TYPES.SET_PERSISTENT:
const fieldName = action.fieldName;
let newState = changePersistent(state, action);
// Track field source if provided
if (action?.source && fieldName) {
newState.fieldSources = updateFieldSources(
newState.fieldSources,
fieldName,
action.source
);
}
return newState;
```
### Hook Integration
Custom hooks pass appropriate source values (only for onboarding and common data stores for now):
```javascript
export const useOnboardingHooks = () => {
const { setIsCasualSeller: setIsCasualSellerAction } = useActions();
return {
setIsCasualSeller: async (value, source = 'user') => {
setIsCasualSellerAction(value, source);
await dispatchActions.persist();
}
};
};
```
## Event Schema
### Event Naming Convention
Events follow the pattern: `ppcp_{funnel}_{action}_{object}`
Examples:
- `ppcp_onboarding_account_type_select`
- `ppcp_onboarding_step_forward`
- `ppcp_onboarding_product_add`
- `ppcp_settings_payment_method_toggle`
## Testing and Debugging
### Enable Debug Mode
Set debug mode in funnel configuration:
```javascript
registerTrackingFunnel('onboarding', {
// ... other config
debug: true
});
```
### WooCommerce Tracks Debug
Enable WooCommerce Tracks debugging in browser console:
```javascript
localStorage.setItem('debug', 'wc-admin:*');
```