Merge pull request #37 from davidmonterocrespo24/copilot/add-google-analytics-key-events-tracking

Add GA4 key events tracking for core user actions
pull/74/head
David Montero Crespo 2026-03-25 02:19:41 -03:00 committed by GitHub
commit 2782282d26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 164 additions and 3 deletions

108
docs/analytics.md Normal file
View File

@ -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.

View File

@ -11,6 +11,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 {
@ -99,6 +100,7 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
setCompiling(true);
setMessage(null);
setConsoleOpen(true);
trackCompileCode();
const kind = activeBoard?.boardKind;
@ -165,6 +167,7 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
}
// legacy fallback
if (compiledHex) {
trackRunSimulation();
startSimulation();
setMessage(null);
} else {

View File

@ -1,6 +1,7 @@
import { useState, useRef, useEffect } from 'react';
import { Link, useNavigate, useLocation } from 'react-router-dom';
import { useAuthStore } from '../../store/useAuthStore';
import { trackVisitGitHub } from '../../utils/analytics';
const GITHUB_URL = 'https://github.com/davidmonterocrespo24/velxio';
const DISCORD_URL = 'https://discord.gg/rCScB9cG';

View File

@ -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<SaveProjectModalProps> = ({ onClose }) =
saved = await updateProject(currentProject.id, payload);
} else {
saved = await createProject(payload);
trackCreateProject();
}
setCurrentProject({
id: saved.id,

View File

@ -16,6 +16,7 @@ import { useVfsStore } from '../store/useVfsStore';
import { isBoardComponent } from '../utils/boardPinMapping';
import { getInstalledLibraries, installLibrary } from '../services/libraryService';
import type { ExampleProject } from '../data/examples';
import { trackOpenExample } from '../utils/analytics';
import type { BoardKind } from '../types/board';
export const ExamplesPage: React.FC = () => {
@ -37,6 +38,9 @@ export const ExamplesPage: React.FC = () => {
const missing = libs.filter((l) => !installedNames.has(l.toLowerCase()));
if (missing.length === 0) return;
const handleLoadExample = (example: ExampleProject) => {
console.log('Loading example:', example.title);
trackOpenExample(example.title);
setInstalling({ total: missing.length, done: 0, current: missing[0] });
for (let i = 0; i < missing.length; i++) {
setInstalling({ total: missing.length, done: i, current: missing[i] });

View File

@ -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 { AppHeader } from '../components/layout/AppHeader';
import { useSEO } from '../utils/useSEO';
import { getSeoMeta } from '../seoRoutes';
@ -499,7 +500,7 @@ export const LandingPage: React.FC = () => {
<IcoZap />
Try Simulator Free
</Link>
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" className="cta-secondary">
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" onClick={trackVisitGitHub} className="cta-secondary">
<IcoGitHub />
View on GitHub
</a>
@ -734,7 +735,7 @@ export const LandingPage: React.FC = () => {
<span>Velxio</span>
</div>
<div className="footer-links">
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer">GitHub</a>
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" onClick={trackVisitGitHub}>GitHub</a>
<Link to="/docs">Docs</Link>
<Link to="/examples">Examples</Link>
<Link to="/editor">Editor</Link>

View File

@ -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<string, unknown>): void;
function fireEvent(eventName: string, params: Record<string, string | number | boolean>): 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' });
}