Skip to content

Commit e876dae

Browse files
committed
fixing ga events
1 parent d5e4d97 commit e876dae

File tree

7 files changed

+130
-39
lines changed

7 files changed

+130
-39
lines changed

.gitignore

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
11
# macOS system files
22
.DS_Store
3+
.idea
34

4-
# Editor/AI config
5-
.cursorrules
6-
7-
# Backup files
8-
bkp.index.html
9-
10-
# Node/npm (if ever used)
115
node_modules/
126

13-
# OS generated files
14-
Thumbs.db
15-
16-
# Git
17-
.git/
7+
.git/
8+
notes/

main.js

Lines changed: 127 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* SouJava 30-Year Celebration Week - Event Application
33
* Optimized JavaScript with Sessionize API integration
44
* FIXES: Proper session scheduling, timezone handling, and data validation
5+
* FIXED: Analytics tracking to prevent "(not set)" values
56
*/
67

78
// Application State
@@ -10,6 +11,7 @@ const AppState = {
1011
currentTimezone: 'America/Sao_Paulo',
1112
isLoading: true,
1213
error: null,
14+
isDataReady: false, // New flag to prevent race conditions
1315
filters: {
1416
day: '',
1517
language: '',
@@ -29,7 +31,8 @@ const CONFIG = {
2931
speakers: [],
3032
categories: [],
3133
rooms: []
32-
}
34+
},
35+
DEBUG_ANALYTICS: false // Disable debug logging for production
3336
};
3437

3538
// Utility Functions
@@ -104,6 +107,14 @@ const Utils = {
104107
return div.innerHTML;
105108
},
106109

110+
// New utility to ensure non-empty strings for analytics
111+
ensureNonEmptyString(value, fallback = 'Unknown') {
112+
if (!value || typeof value !== 'string' || value.trim() === '') {
113+
return fallback;
114+
}
115+
return value.trim();
116+
},
117+
107118
debounce(func, wait) {
108119
let timeout;
109120
return function executedFunction(...args) {
@@ -198,14 +209,18 @@ class DataLoader {
198209
static transformSessionizeData(sessionizeData) {
199210
const transformed = {
200211
speakers: {},
201-
schedule: []
212+
schedule: [],
213+
sessionsById: {} // New: Store sessions by ID for quick lookup
202214
};
203215

204-
// Transform speakers
216+
// Transform speakers with validation
205217
if (sessionizeData.speakers) {
206218
sessionizeData.speakers.forEach(speaker => {
219+
// Validate speaker data
220+
const speakerName = Utils.ensureNonEmptyString(speaker.fullName, 'Unknown Speaker');
221+
207222
transformed.speakers[speaker.id] = {
208-
name: speaker.fullName,
223+
name: speakerName,
209224
title: speaker.tagLine || '',
210225
bio: speaker.bio || '',
211226
image: speaker.profilePicture || '',
@@ -239,10 +254,13 @@ class DataLoader {
239254
const timeInfo = Utils.parseSessionizeTime(session.startsAt);
240255
if (!timeInfo) return;
241256

257+
// Validate session title
258+
const sessionTitle = Utils.ensureNonEmptyString(session.title, 'Untitled Session');
259+
242260
const transformedSession = {
243261
id: session.id.toString(),
244262
time: timeInfo.time,
245-
title: session.title,
263+
title: sessionTitle,
246264
description: session.description || '',
247265
language: this.getLanguageFromCategories(session.categoryItems, categoryLookup),
248266
type: this.getTypeFromCategories(session.categoryItems, categoryLookup),
@@ -255,6 +273,9 @@ class DataLoader {
255273
room: sessionizeData.rooms?.find(r => r.id === session.roomId)?.name || ''
256274
};
257275

276+
// Store in sessionsById for quick lookup
277+
transformed.sessionsById[transformedSession.id] = transformedSession;
278+
258279
// Find or create day in schedule
259280
let daySchedule = transformed.schedule.find(day => day.date === timeInfo.date);
260281
if (!daySchedule) {
@@ -455,16 +476,37 @@ class ModalHandler {
455476
}
456477

457478
showSpeaker(speakerId) {
479+
// Check if data is ready
480+
if (!AppState.isDataReady) {
481+
console.warn('[Analytics] Attempted to show speaker before data loaded');
482+
return;
483+
}
484+
485+
if (CONFIG.DEBUG_ANALYTICS) {
486+
console.log('[Analytics Debug] showSpeaker called with speakerId:', speakerId);
487+
}
488+
458489
const speaker = AppState.eventData?.speakers[speakerId];
459490

460491
if (!speaker) {
492+
console.warn('[Analytics] No speaker found for ID:', speakerId);
493+
// Track the failed lookup
494+
Analytics.trackSpeakerProfileView('Speaker Not Found', false);
461495
return;
462496
}
463497

464-
// Track speaker view
498+
// Ensure speaker name is valid
499+
const speakerName = Utils.ensureNonEmptyString(speaker.name, 'Unknown Speaker');
500+
501+
// Track speaker view with validated data
465502
const isJavaChampion = speaker.title?.toLowerCase().includes('java champion') ||
466503
speaker.bio?.toLowerCase().includes('java champion');
467-
Analytics.trackSpeakerProfileView(speaker.name, isJavaChampion);
504+
505+
if (CONFIG.DEBUG_ANALYTICS) {
506+
console.log('[Analytics Debug] Tracking speaker:', speakerName, 'isJavaChampion:', isJavaChampion);
507+
}
508+
509+
Analytics.trackSpeakerProfileView(speakerName, isJavaChampion);
468510

469511
this.populateSpeakerContent(speaker);
470512
this.showModal(this.speakerModal);
@@ -912,6 +954,12 @@ class ScheduleRenderer {
912954
}
913955

914956
bindEvents() {
957+
// Only bind events if data is ready
958+
if (!AppState.isDataReady) {
959+
console.warn('[Events] Attempted to bind events before data loaded');
960+
return;
961+
}
962+
915963
// Session toggle events
916964
this.container.addEventListener('click', (e) => {
917965
const toggleElement = e.target.closest('[data-session-toggle]');
@@ -938,7 +986,7 @@ class ScheduleRenderer {
938986
this.container.addEventListener('click', (e) => {
939987
if (e.target.classList.contains('speaker-avatar') || e.target.classList.contains('speaker-fallback')) {
940988
const speakerId = e.target.dataset.speakerId;
941-
if (speakerId) {
989+
if (speakerId && AppState.isDataReady) {
942990
this.modalHandler.showSpeaker(speakerId);
943991
}
944992
}
@@ -949,7 +997,7 @@ class ScheduleRenderer {
949997
const speakerDetail = e.target.closest('.session-speaker-detail');
950998
if (speakerDetail) {
951999
const speakerId = speakerDetail.dataset.speakerId;
952-
if (speakerId) {
1000+
if (speakerId && AppState.isDataReady) {
9531001
this.modalHandler.showSpeaker(speakerId);
9541002
}
9551003
}
@@ -966,12 +1014,26 @@ class ScheduleRenderer {
9661014

9671015
const isExpanded = sessionElement.classList.contains('expanded');
9681016

969-
// Track session view when expanding
1017+
// Track session view when expanding - USE ACTUAL SESSION DATA
9701018
if (!isExpanded) {
971-
const sessionTitle = sessionElement.querySelector('.session-title')?.textContent || 'Unknown Session';
972-
const topicsElements = sessionElement.querySelectorAll('.badge-topic');
973-
const topics = Array.from(topicsElements).map(el => el.textContent.trim());
974-
Analytics.trackSessionDetailsView(sessionTitle, topics);
1019+
// Get session from AppState instead of DOM
1020+
const session = AppState.eventData?.sessionsById[sessionId];
1021+
1022+
if (session) {
1023+
// Validate session title
1024+
const sessionTitle = Utils.ensureNonEmptyString(session.title, 'Unknown Session');
1025+
const topics = session.topics || [];
1026+
1027+
if (CONFIG.DEBUG_ANALYTICS) {
1028+
console.log('[Analytics Debug] Tracking session:', sessionTitle, 'topics:', topics);
1029+
}
1030+
1031+
Analytics.trackSessionDetailsView(sessionTitle, topics);
1032+
} else {
1033+
console.warn('[Analytics] Session not found in AppState:', sessionId);
1034+
// Fallback tracking
1035+
Analytics.trackSessionDetailsView('Session Not Found', []);
1036+
}
9751037
}
9761038

9771039
if (isExpanded) {
@@ -1050,10 +1112,14 @@ class ScheduleRenderer {
10501112
}
10511113

10521114
findSessionById(sessionId) {
1053-
for (const day of AppState.eventData.schedule) {
1054-
const session = day.sessions.find(s => s.id === sessionId);
1055-
if (session) {
1056-
return { ...session, date: day.date, dayName: day.dayName };
1115+
// Use the new sessionsById lookup for better performance
1116+
const session = AppState.eventData?.sessionsById[sessionId];
1117+
if (session) {
1118+
// Find the date from schedule
1119+
for (const day of AppState.eventData.schedule) {
1120+
if (day.sessions.some(s => s.id === sessionId)) {
1121+
return { ...session, date: day.date, dayName: day.dayName };
1122+
}
10571123
}
10581124
}
10591125
return null;
@@ -1256,6 +1322,12 @@ class SpeakersRenderer {
12561322
}
12571323

12581324
bindEvents() {
1325+
// Only bind if data is ready
1326+
if (!AppState.isDataReady) {
1327+
console.warn('[Events] Attempted to bind speaker events before data loaded');
1328+
return;
1329+
}
1330+
12591331
this.container.addEventListener('click', (e) => {
12601332
const speakerCard = e.target.closest('.speaker-card');
12611333
if (speakerCard && !e.target.closest('.speaker-social')) {
@@ -1351,8 +1423,21 @@ class AnimationController {
13511423
// Analytics Helper with Specific Event Names
13521424
const Analytics = {
13531425
track(eventName, parameters = {}) {
1426+
// Validate all parameters before sending
1427+
const validatedParams = {};
1428+
1429+
Object.entries(parameters).forEach(([key, value]) => {
1430+
// Ensure all values are non-empty strings
1431+
validatedParams[key] = Utils.ensureNonEmptyString(value, 'Not Specified');
1432+
});
1433+
13541434
if (typeof gtag !== 'undefined') {
1355-
gtag('event', eventName, parameters);
1435+
gtag('event', eventName, validatedParams);
1436+
}
1437+
1438+
// Debug logging
1439+
if (CONFIG.DEBUG_ANALYTICS) {
1440+
console.log('[Analytics] Event:', eventName, 'Params:', validatedParams);
13561441
}
13571442
},
13581443

@@ -1372,35 +1457,47 @@ const Analytics = {
13721457
});
13731458
},
13741459

1375-
// Track session detail views
1460+
// Track session detail views - FIXED
13761461
trackSessionDetailsView(sessionTitle, sessionTopics = []) {
1462+
// Ensure session title is valid
1463+
const validTitle = Utils.ensureNonEmptyString(sessionTitle, 'Unknown Session');
1464+
const validTopics = sessionTopics.filter(t => t && t.trim()).join(', ') || 'No Topics';
1465+
13771466
this.track('view_session_details', {
1378-
session_title: sessionTitle,
1379-
session_topics: sessionTopics.join(', '),
1467+
session_title: validTitle,
1468+
session_topics: validTopics,
13801469
topics_count: sessionTopics.length
13811470
});
13821471
},
13831472

1384-
// Track speaker profile views
1473+
// Track speaker profile views - ENHANCED
13851474
trackSpeakerProfileView(speakerName, isJavaChampion = false) {
1475+
// Ensure speaker name is valid
1476+
const validName = Utils.ensureNonEmptyString(speakerName, 'Unknown Speaker');
1477+
13861478
this.track('view_speaker_profile', {
1387-
speaker_name: speakerName,
1479+
speaker_name: validName,
13881480
is_java_champion: isJavaChampion
13891481
});
13901482
},
13911483

13921484
// Track filter usage
13931485
trackFilterUse(filterType, filterValue) {
1486+
const validType = Utils.ensureNonEmptyString(filterType, 'Unknown Filter');
1487+
const validValue = Utils.ensureNonEmptyString(filterValue, 'No Value');
1488+
13941489
this.track('filter_used', {
1395-
filter_type: filterType,
1396-
filter_value: filterValue
1490+
filter_type: validType,
1491+
filter_value: validValue
13971492
});
13981493
},
13991494

14001495
// Track timezone changes
14011496
trackTimezoneChange(timezone) {
1497+
const validTimezone = Utils.ensureNonEmptyString(timezone, 'Unknown Timezone');
1498+
14021499
this.track('change_timezone', {
1403-
timezone: timezone
1500+
timezone: validTimezone
14041501
});
14051502
}
14061503
};
@@ -1506,12 +1603,14 @@ class App {
15061603
async init() {
15071604
try {
15081605
AppState.isLoading = true;
1606+
AppState.isDataReady = false; // Ensure data ready flag is false
15091607
this.scheduleRenderer.renderLoading();
15101608
this.speakersRenderer.renderLoading();
15111609

15121610
AppState.eventData = await DataLoader.loadEventData();
15131611
AppState.isLoading = false;
15141612
AppState.error = null;
1613+
AppState.isDataReady = true; // Set data ready flag
15151614

15161615
// Preload speaker images
15171616
if (AppState.eventData.speakers) {
@@ -1527,6 +1626,7 @@ class App {
15271626

15281627
} catch (error) {
15291628
AppState.isLoading = false;
1629+
AppState.isDataReady = false;
15301630
AppState.error = error.message;
15311631

15321632
this.scheduleRenderer.renderError(error.message);
1.5 MB
Loading
1.51 MB
Loading
1.54 MB
Loading
1.45 MB
Loading
1.49 MB
Loading

0 commit comments

Comments
 (0)