diff --git a/docs/analytics.md b/docs/analytics.md new file mode 100644 index 00000000..8f593e6a --- /dev/null +++ b/docs/analytics.md @@ -0,0 +1,108 @@ +# Analytics Events + +Velxio uses **Google Analytics 4 (GA4)** to measure key user interactions. Events are fired via `gtag` and should be marked as **Key Events** in the GA4 dashboard to track conversions and engagement. + +The tracking utility lives in [`frontend/src/utils/analytics.ts`](../frontend/src/utils/analytics.ts). + +--- + +## Key Events + +### `run_simulation` + +Fired when the user clicks the **Run** button to start a simulation. + +| Property | Value | +|------------------|--------------| +| `event_category` | `engagement` | + +**Location:** `EditorToolbar.tsx` → `handleRun()` + +--- + +### `open_example` + +Fired when a user loads a sample project from the Examples gallery. + +| Property | Value | +|------------------|------------------------| +| `event_category` | `engagement` | +| `event_label` | Title of the example | + +**Location:** `ExamplesPage.tsx` → `handleLoadExample()` + +--- + +### `create_project` + +Fired when a user successfully saves a **new** project (not when updating an existing one). + +| Property | Value | +|------------------|--------------| +| `event_category` | `engagement` | + +**Location:** `SaveProjectModal.tsx` → `handleSave()` + +--- + +### `compile_code` + +Fired when code compilation starts (user clicks the **Compile** button). + +| Property | Value | +|------------------|---------------| +| `event_category` | `development` | + +**Location:** `EditorToolbar.tsx` → `handleCompile()` + +--- + +### `visit_github` + +Fired when a user clicks any link pointing to the Velxio GitHub repository. + +| Property | Value | +|------------------|-----------------| +| `event_category` | `external_link` | + +**Locations:** +- `LandingPage.tsx` — nav bar, hero CTA, and footer GitHub links +- `AppHeader.tsx` — editor header GitHub link + +--- + +## Implementation + +All helpers are exported from `frontend/src/utils/analytics.ts`: + +```typescript +import { + trackRunSimulation, + trackOpenExample, + trackCreateProject, + trackCompileCode, + trackVisitGitHub, +} from '../utils/analytics'; +``` + +Example usage: + +```typescript +// Fire an event +trackRunSimulation(); + +// Fire an event with a label +trackOpenExample('Blink LED'); +``` + +The module safely checks whether `gtag` is available before calling it, so it does not throw errors in environments where the GA script is not loaded (e.g., local development without the GA tag). + +--- + +## Marking Events as Key Events in GA4 + +1. Open **Google Analytics → Admin → Events**. +2. Locate the event by name (e.g. `run_simulation`). +3. Toggle **Mark as key event** to enable conversion tracking. + +Repeat for each event listed above. diff --git a/frontend/src/components/editor/EditorToolbar.tsx b/frontend/src/components/editor/EditorToolbar.tsx index df83d1ba..01f95ba9 100644 --- a/frontend/src/components/editor/EditorToolbar.tsx +++ b/frontend/src/components/editor/EditorToolbar.tsx @@ -7,6 +7,7 @@ import { InstallLibrariesModal } from '../simulator/InstallLibrariesModal'; import { parseCompileResult } from '../../utils/compilationLogger'; import type { CompilationLog } from '../../utils/compilationLogger'; import { exportToWokwiZip, importFromWokwiZip } from '../../utils/wokwiZip'; +import { trackCompileCode, trackRunSimulation } from '../../utils/analytics'; import './EditorToolbar.css'; interface EditorToolbarProps { @@ -43,6 +44,7 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi setCompiling(true); setMessage(null); setConsoleOpen(true); + trackCompileCode(); const fqbn = BOARD_FQBN[boardType]; const boardLabel = BOARD_LABELS[boardType]; @@ -81,6 +83,7 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi const handleRun = () => { if (compiledHex) { + trackRunSimulation(); startSimulation(); setMessage(null); } else { diff --git a/frontend/src/components/layout/AppHeader.tsx b/frontend/src/components/layout/AppHeader.tsx index 42130362..cca6ccf7 100644 --- a/frontend/src/components/layout/AppHeader.tsx +++ b/frontend/src/components/layout/AppHeader.tsx @@ -1,6 +1,7 @@ import { useState, useRef, useEffect } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { useAuthStore } from '../../store/useAuthStore'; +import { trackVisitGitHub } from '../../utils/analytics'; interface AppHeaderProps {} @@ -55,6 +56,7 @@ export const AppHeader: React.FC = () => { target="_blank" rel="noopener noreferrer" title="GitHub — Velxio" + onClick={trackVisitGitHub} style={{ display: 'flex', alignItems: 'center', gap: 5, color: '#ccc', textDecoration: 'none', fontSize: 13 }} > diff --git a/frontend/src/components/layout/SaveProjectModal.tsx b/frontend/src/components/layout/SaveProjectModal.tsx index 443b6ee2..015b4baf 100644 --- a/frontend/src/components/layout/SaveProjectModal.tsx +++ b/frontend/src/components/layout/SaveProjectModal.tsx @@ -4,6 +4,7 @@ import { useEditorStore } from '../../store/useEditorStore'; import { useSimulatorStore } from '../../store/useSimulatorStore'; import { useProjectStore } from '../../store/useProjectStore'; import { createProject, updateProject } from '../../services/projectService'; +import { trackCreateProject } from '../../utils/analytics'; interface SaveProjectModalProps { onClose: () => void; @@ -56,6 +57,7 @@ export const SaveProjectModal: React.FC = ({ onClose }) = saved = await updateProject(currentProject.id, payload); } else { saved = await createProject(payload); + trackCreateProject(); } setCurrentProject({ id: saved.id, diff --git a/frontend/src/pages/ExamplesPage.tsx b/frontend/src/pages/ExamplesPage.tsx index 69409e71..b71ecc46 100644 --- a/frontend/src/pages/ExamplesPage.tsx +++ b/frontend/src/pages/ExamplesPage.tsx @@ -10,6 +10,7 @@ import { ExamplesGallery } from '../components/examples/ExamplesGallery'; import { useEditorStore } from '../store/useEditorStore'; import { useSimulatorStore } from '../store/useSimulatorStore'; import type { ExampleProject } from '../data/examples'; +import { trackOpenExample } from '../utils/analytics'; export const ExamplesPage: React.FC = () => { const navigate = useNavigate(); @@ -18,6 +19,7 @@ export const ExamplesPage: React.FC = () => { const handleLoadExample = (example: ExampleProject) => { console.log('Loading example:', example.title); + trackOpenExample(example.title); // Switch board type if the example specifies one const targetBoard = example.boardType || 'arduino-uno'; diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx index 5c0d1615..09244e7c 100644 --- a/frontend/src/pages/LandingPage.tsx +++ b/frontend/src/pages/LandingPage.tsx @@ -1,6 +1,7 @@ import { useState, useRef, useEffect } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { useAuthStore } from '../store/useAuthStore'; +import { trackVisitGitHub } from '../utils/analytics'; import './LandingPage.css'; const GITHUB_URL = 'https://github.com/davidmonterocrespo24/velxio'; @@ -417,7 +418,7 @@ export const LandingPage: React.FC = () => { Velxio
- + GitHub Docs @@ -458,7 +459,7 @@ export const LandingPage: React.FC = () => { Launch Editor - + View on GitHub @@ -564,7 +565,7 @@ export const LandingPage: React.FC = () => { Velxio
- GitHub + GitHub Docs Examples Editor diff --git a/frontend/src/utils/analytics.ts b/frontend/src/utils/analytics.ts new file mode 100644 index 00000000..87dc6d90 --- /dev/null +++ b/frontend/src/utils/analytics.ts @@ -0,0 +1,42 @@ +/** + * Google Analytics 4 Key Events Tracking + * + * Provides helper functions to fire GA4 custom events for key user actions. + * Each function maps to an event that should be marked as a Key Event in GA4. + */ + +declare function gtag(command: 'event', eventName: string, eventParams?: Record): void; + +function fireEvent(eventName: string, params: Record): void { + if (typeof gtag === 'function') { + gtag('event', eventName, params); + } +} + +/** Fired when the user starts a simulation (clicks Run). */ +export function trackRunSimulation(): void { + fireEvent('run_simulation', { event_category: 'engagement' }); +} + +/** Fired when a user loads a sample project from the examples gallery. */ +export function trackOpenExample(exampleTitle?: string): void { + fireEvent('open_example', { + event_category: 'engagement', + ...(exampleTitle ? { event_label: exampleTitle } : {}), + }); +} + +/** Fired when a user successfully creates a new project. */ +export function trackCreateProject(): void { + fireEvent('create_project', { event_category: 'engagement' }); +} + +/** Fired when code compilation starts. */ +export function trackCompileCode(): void { + fireEvent('compile_code', { event_category: 'development' }); +} + +/** Fired when a user clicks any GitHub repository link. */ +export function trackVisitGitHub(): void { + fireEvent('visit_github', { event_category: 'external_link' }); +}