Merge pull request #37 from davidmonterocrespo24/copilot/add-google-analytics-key-events-tracking
Add GA4 key events tracking for core user actionspull/74/head
commit
2782282d26
|
|
@ -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.
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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] });
|
||||
|
|
|
|||
|
|
@ -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,13 +500,13 @@ 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>
|
||||
</div>
|
||||
<p className="hero-trust-line">No signup required · Runs 100% in your browser · Free & open-source</p>
|
||||
|
||||
|
||||
</div>
|
||||
<div className="hero-right">
|
||||
<img src="/image.png" alt="Velxio simulator preview" className="hero-preview-img" />
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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' });
|
||||
}
|
||||
Loading…
Reference in New Issue