Files

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);