388 lines
9.2 KiB
JavaScript
388 lines
9.2 KiB
JavaScript
/* ============================================
|
|
ANALYTICS & EVENT TRACKING
|
|
============================================ */
|
|
|
|
/**
|
|
* Manager d'analytics pour tracker les événements
|
|
*/
|
|
class AnalyticsManager {
|
|
constructor() {
|
|
this.sessionId = generateUUID();
|
|
this.events = [];
|
|
this.enabled = true;
|
|
this.endpoint = window.ANALYTICS_ENDPOINT || null;
|
|
}
|
|
|
|
/**
|
|
* Enregistrer un événement
|
|
*/
|
|
track(eventName, data = {}) {
|
|
if (!this.enabled) return;
|
|
|
|
const event = {
|
|
type: eventName,
|
|
timestamp: Date.now(),
|
|
sessionId: this.sessionId,
|
|
userId: getUserId(),
|
|
playerName: getPlayerName(),
|
|
data: data
|
|
};
|
|
|
|
this.events.push(event);
|
|
|
|
// Logger en debug
|
|
DEBUG.log(`Event tracked: ${eventName}`, data);
|
|
|
|
// Envoyer au serveur si configuré
|
|
if (this.endpoint) {
|
|
this.sendToServer(event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Démarrage du jeu
|
|
*/
|
|
trackGameStart(difficulty) {
|
|
this.track('game_started', {
|
|
difficulty: difficulty,
|
|
mode: DIFFICULTY_MODES[difficulty].name
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Retournement de carte
|
|
*/
|
|
trackFlip(cardIndex, totalFlips) {
|
|
this.track('card_flipped', {
|
|
cardIndex: cardIndex,
|
|
totalFlips: totalFlips
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Paire trouvée
|
|
*/
|
|
trackMatch(pictogrammeId, totalMatches, totalMoves) {
|
|
this.track('pair_matched', {
|
|
pictogrammeId: pictogrammeId,
|
|
totalMatches: totalMatches,
|
|
totalMoves: totalMoves
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Paire incorrecte
|
|
*/
|
|
trackMismatch(pictogramme1Id, pictogramme2Id, move) {
|
|
this.track('pair_mismatched', {
|
|
pictogramme1: pictogramme1Id,
|
|
pictogramme2: pictogramme2Id,
|
|
move: move
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Fin du jeu
|
|
*/
|
|
trackGameEnd(gameData) {
|
|
this.track('game_completed', {
|
|
difficulty: gameData.difficulty,
|
|
finalScore: gameData.finalScore,
|
|
moves: gameData.moves,
|
|
duration: gameData.duration,
|
|
won: gameData.won,
|
|
achievements: gameData.achievements || []
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Changement de difficulté
|
|
*/
|
|
trackDifficultyChange(newDifficulty) {
|
|
this.track('difficulty_changed', {
|
|
newDifficulty: newDifficulty
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Affichage du classement
|
|
*/
|
|
trackLeaderboardView(filter) {
|
|
this.track('leaderboard_viewed', {
|
|
filter: filter
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Envoyer les événements au serveur
|
|
*/
|
|
sendToServer(event) {
|
|
if (!this.endpoint) return;
|
|
|
|
fetch(this.endpoint, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(event)
|
|
}).catch(err => {
|
|
// Silencieusement échouer en offline
|
|
DEBUG.log('Analytics offline:', err);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Envoyer batch d'événements
|
|
*/
|
|
sendBatch() {
|
|
if (!this.endpoint || this.events.length === 0) return;
|
|
|
|
fetch(this.endpoint, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
batch: true,
|
|
events: this.events,
|
|
sessionId: this.sessionId
|
|
})
|
|
}).then(() => {
|
|
this.events = []; // Vider après envoi
|
|
DEBUG.log('Analytics batch sent');
|
|
}).catch(err => {
|
|
DEBUG.log('Analytics batch failed:', err);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Obtenir le résumé de la session
|
|
*/
|
|
getSessionSummary() {
|
|
return {
|
|
sessionId: this.sessionId,
|
|
userId: getUserId(),
|
|
playerName: getPlayerName(),
|
|
eventCount: this.events.length,
|
|
events: this.events
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Exporter les données en JSON
|
|
*/
|
|
exportJSON() {
|
|
const data = {
|
|
exportDate: new Date().toISOString(),
|
|
sessionSummary: this.getSessionSummary(),
|
|
allEvents: this.events
|
|
};
|
|
return JSON.stringify(data, null, 2);
|
|
}
|
|
|
|
/**
|
|
* Télécharger les données
|
|
*/
|
|
downloadData() {
|
|
const data = this.exportJSON();
|
|
const blob = new Blob([data], { type: 'application/json' });
|
|
const url = URL.createObjectURL(blob);
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = `analytics-${generateUUID().slice(0, 8)}.json`;
|
|
link.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
/**
|
|
* Nettoyer les événements
|
|
*/
|
|
clear() {
|
|
this.events = [];
|
|
DEBUG.log('Analytics cleared');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Heatmap Analytics - Tracker l'interaction avec les cartes
|
|
*/
|
|
class HeatmapAnalytics {
|
|
constructor() {
|
|
this.heatmap = new Map();
|
|
}
|
|
|
|
/**
|
|
* Enregistrer un clic de carte
|
|
*/
|
|
recordCardClick(cardIndex) {
|
|
if (!this.heatmap.has(cardIndex)) {
|
|
this.heatmap.set(cardIndex, 0);
|
|
}
|
|
this.heatmap.set(cardIndex, this.heatmap.get(cardIndex) + 1);
|
|
}
|
|
|
|
/**
|
|
* Obtenir les données de heatmap
|
|
*/
|
|
getHeatmap() {
|
|
const sorted = Array.from(this.heatmap.entries())
|
|
.sort((a, b) => b[1] - a[1]);
|
|
|
|
return Object.fromEntries(sorted);
|
|
}
|
|
|
|
/**
|
|
* Calculer la carte la plus cliquée
|
|
*/
|
|
getMostClickedCard() {
|
|
let maxClicks = 0;
|
|
let maxCard = -1;
|
|
|
|
for (const [card, clicks] of this.heatmap) {
|
|
if (clicks > maxClicks) {
|
|
maxClicks = clicks;
|
|
maxCard = card;
|
|
}
|
|
}
|
|
|
|
return { card: maxCard, clicks: maxClicks };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Performance Monitor - Analyser les performances du jeu
|
|
*/
|
|
class PerformanceMonitor {
|
|
constructor() {
|
|
this.metrics = {};
|
|
this.startTime = performance.now();
|
|
}
|
|
|
|
/**
|
|
* Marquer un point de début
|
|
*/
|
|
mark(name) {
|
|
this.metrics[name] = {
|
|
start: performance.now(),
|
|
end: null,
|
|
duration: null
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Marquer la fin
|
|
*/
|
|
measure(name) {
|
|
if (this.metrics[name]) {
|
|
this.metrics[name].end = performance.now();
|
|
this.metrics[name].duration = this.metrics[name].end - this.metrics[name].start;
|
|
return this.metrics[name].duration;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Obtenir les métriques
|
|
*/
|
|
getMetrics() {
|
|
return this.metrics;
|
|
}
|
|
|
|
/**
|
|
* Afficher les métriques en console
|
|
*/
|
|
logMetrics() {
|
|
console.table(this.metrics);
|
|
}
|
|
|
|
/**
|
|
* FPS Monitor
|
|
*/
|
|
monitorFPS(callback) {
|
|
let lastTime = performance.now();
|
|
let frames = 0;
|
|
|
|
const checkFPS = () => {
|
|
frames++;
|
|
const currentTime = performance.now();
|
|
|
|
if (currentTime - lastTime >= 1000) {
|
|
const fps = frames;
|
|
callback(fps);
|
|
frames = 0;
|
|
lastTime = currentTime;
|
|
}
|
|
|
|
requestAnimationFrame(checkFPS);
|
|
};
|
|
|
|
checkFPS();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A/B Testing Manager
|
|
*/
|
|
class ABTestingManager {
|
|
constructor() {
|
|
this.tests = {};
|
|
}
|
|
|
|
/**
|
|
* Créer un test A/B
|
|
*/
|
|
createTest(testName, variants) {
|
|
const userId = getUserId();
|
|
const variantIndex = parseInt(userId.slice(0, 8), 16) % variants.length;
|
|
|
|
this.tests[testName] = {
|
|
variants: variants,
|
|
selectedVariant: variants[variantIndex],
|
|
variantIndex: variantIndex
|
|
};
|
|
|
|
return this.tests[testName].selectedVariant;
|
|
}
|
|
|
|
/**
|
|
* Obtenir le variant
|
|
*/
|
|
getVariant(testName) {
|
|
return this.tests[testName]?.selectedVariant || null;
|
|
}
|
|
|
|
/**
|
|
* Tracker une conversion
|
|
*/
|
|
trackConversion(testName) {
|
|
const test = this.tests[testName];
|
|
if (test) {
|
|
DEBUG.log(`Conversion tracked for test: ${testName}, variant: ${test.selectedVariant}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// INITIALISATION GLOBALES
|
|
// ============================================
|
|
|
|
// Créer les managers globalement
|
|
const analyticsManager = new AnalyticsManager();
|
|
const heatmapAnalytics = new HeatmapAnalytics();
|
|
const performanceMonitor = new PerformanceMonitor();
|
|
const abTestingManager = new ABTestingManager();
|
|
|
|
// Exporter pour utilisation
|
|
window.AnalyticsManager = AnalyticsManager;
|
|
window.HeatmapAnalytics = HeatmapAnalytics;
|
|
window.PerformanceMonitor = PerformanceMonitor;
|
|
window.ABTestingManager = ABTestingManager;
|
|
|
|
// Event listener pour envoyer les analytics avant de quitter
|
|
window.addEventListener('beforeunload', () => {
|
|
analyticsManager.sendBatch();
|
|
});
|
|
|
|
// Log de session au démarrage
|
|
DEBUG.log('Analytics session started:', analyticsManager.sessionId);
|