From aa5292b65353359b21e6b95040add54d79d1185e Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Thu, 15 Jan 2026 15:45:03 +0100 Subject: [PATCH] feat: add Umami analytics tracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Umami v2.13.2 script to index.html - Track key events: lesson_complete, module_start, section_view, language_change, playground_template, reset_progress 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- src/app.js | 22 ++++++++++++++++++++++ src/index.html | 1 + 2 files changed, 23 insertions(+) diff --git a/src/app.js b/src/app.js index 7d77d30..67c4360 100644 --- a/src/app.js +++ b/src/app.js @@ -14,6 +14,13 @@ import { oneDark } from "@codemirror/theme-one-dark"; import { html } from "@codemirror/lang-html"; import { css } from "@codemirror/lang-css"; +// Analytics tracking helper (Umami v2.13.2) +function track(eventName, eventData = {}) { + if (typeof umami !== "undefined" && umami.track) { + umami.track(eventName, eventData); + } +} + // Simplified state - LessonEngine now manages lesson state and progress const state = { userSettings: { @@ -223,6 +230,8 @@ function toggleExpectedResult() { // ================= LANGUAGE TOGGLE ================= function changeLanguage(newLang) { + track("language_change", { language: newLang }); + // Add transition class before any updates elements.editorSection?.classList.add("transitioning"); @@ -396,6 +405,8 @@ function selectModule(moduleId) { const success = lessonEngine.setModuleById(moduleId); if (!success) return; + track("module_start", { module: moduleId }); + // Show lesson UI showLessonUI(); @@ -734,6 +745,7 @@ function resetCode() { function loadRandomTemplate() { const template = getRandomTemplate(); if (codeEditor && template) { + track("playground_template", { template: template.name }); codeEditor.setValue(template.code); // Apply the code to the preview lessonEngine.applyUserCode(template.code, true); @@ -764,6 +776,12 @@ function runCode() { const validationResult = lessonEngine.validateCode(); if (validationResult.isValid) { + // Track lesson completion + track("lesson_complete", { + module: engineState.module?.id, + lesson: engineState.lessonIndex + }); + // Show success hint showSuccessHint(validationResult.message || t("successMessage")); @@ -846,6 +864,7 @@ function closeResetDialog() { } function handleResetConfirm() { + track("reset_progress"); lessonEngine.clearProgress(); closeResetDialog(); closeSidebar(); @@ -1361,6 +1380,9 @@ function showSectionPage(sectionId) { hideAllPages(); elements.sectionPage?.classList.remove("hidden"); + // Track section page view + track("section_view", { section: sectionId }); + const section = getSection(sectionId); if (!section) { showLandingPage(); diff --git a/src/index.html b/src/index.html index 7cae984..43c412c 100644 --- a/src/index.html +++ b/src/index.html @@ -10,6 +10,7 @@ /> Code Crispies - Learn HTML & CSS Interactively +