feat(analytics): add Analytics Maturity metric to ManaScore and custom event tracking to 4 apps

Add new "Analytics Maturity" extended metric to ManaScore schema with 5 checks:
pageViewTracking, customEvents, authTracking, landingTracking, publicDashboard.
Populate analytics data across all 20 audit files. Document the metric in about.md.

Add app-specific Umami custom event helpers and integrate tracking into:
- ManaCore: 13 events (nav, onboarding, dashboard widgets, credits, settings)
- Presi: 12 events (deck/slide CRUD, presentation start/exit, sharing)
- Zitare: 9 events (quotes, favorites, categories, search, lists, language)
- Mukke: 12 events (upload, library, playlists, projects, editor export)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-25 09:20:59 +01:00
parent f0233b8d31
commit d2264f5360
46 changed files with 314 additions and 4 deletions

View file

@ -4,6 +4,7 @@
import { onMount, onDestroy } from 'svelte';
import { authStore } from '$lib/stores/auth.svelte';
import { projectStore } from '$lib/stores/project.svelte';
import { MukkeEvents } from '@manacore/shared-utils/analytics';
import { audioStore } from '$lib/stores/audio.svelte';
import { editorStore } from '$lib/stores/editor.svelte';
import { MARKER_COLORS } from '@mukke/shared';
@ -226,6 +227,7 @@
if (!response.ok) throw new Error('Export failed');
const blob = await response.blob();
MukkeEvents.projectExported(format);
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;

View file

@ -4,6 +4,7 @@
import { libraryStore } from '$lib/stores/library.svelte';
import { authStore } from '$lib/stores/auth.svelte';
import { playerStore } from '$lib/stores/player.svelte';
import { MukkeEvents } from '@manacore/shared-utils/analytics';
import SongEditor from '$lib/components/SongEditor.svelte';
import { ContextMenu, type ContextMenuItem } from '@manacore/shared-ui';
import type { Song } from '@mukke/shared';
@ -109,7 +110,9 @@
async function handleToggleFavorite(id: string, e: Event) {
e.preventDefault();
e.stopPropagation();
const song = libraryStore.songs.find((s) => s.id === id);
await libraryStore.toggleFavorite(id);
MukkeEvents.songFavorited(!song?.favorite);
}
function handleEditSong(song: Song, e: Event) {
@ -120,6 +123,7 @@
function handlePlaySong(song: Song, index: number) {
playerStore.playSong(song, libraryStore.songs, index);
MukkeEvents.songPlayed();
}
async function openInEditor(songId: string, e: Event) {

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { playlistStore } from '$lib/stores/playlist.svelte';
import { MukkeEvents } from '@manacore/shared-utils/analytics';
let showCreateModal = $state(false);
let newName = $state('');
@ -16,6 +17,7 @@
isCreating = true;
try {
await playlistStore.createPlaylist(newName.trim(), newDescription.trim() || undefined);
MukkeEvents.playlistCreated();
newName = '';
newDescription = '';
showCreateModal = false;
@ -30,6 +32,7 @@
e.stopPropagation();
if (!confirm('Delete this playlist?')) return;
await playlistStore.deletePlaylist(id);
MukkeEvents.playlistDeleted();
}
function truncate(text: string, max: number): string {

View file

@ -3,6 +3,7 @@
import { page } from '$app/stores';
import { playlistStore } from '$lib/stores/playlist.svelte';
import { playerStore } from '$lib/stores/player.svelte';
import { MukkeEvents } from '@manacore/shared-utils/analytics';
import type { Song } from '@mukke/shared';
let isEditingName = $state(false);
@ -32,6 +33,7 @@
const playlist = playlistStore.currentPlaylist;
if (!playlist || playlist.songs.length === 0) return;
playerStore.playQueue(playlist.songs, 0);
MukkeEvents.playlistPlayAll();
}
function handleShufflePlay() {
@ -39,6 +41,7 @@
if (!playlist || playlist.songs.length === 0) return;
const shuffled = shuffleArray(playlist.songs);
playerStore.playQueue(shuffled, 0);
MukkeEvents.playlistShufflePlay();
}
function handlePlaySong(song: Song, index: number) {

View file

@ -2,6 +2,7 @@
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import { projectStore } from '$lib/stores/project.svelte';
import { MukkeEvents } from '@manacore/shared-utils/analytics';
let showCreateModal = $state(false);
let newProjectTitle = $state('');
@ -18,6 +19,7 @@
isCreating = true;
try {
const project = await projectStore.createProject(newProjectTitle, newProjectDescription);
MukkeEvents.projectCreated();
showCreateModal = false;
newProjectTitle = '';
newProjectDescription = '';
@ -32,6 +34,7 @@
e.preventDefault();
if (confirm('Are you sure you want to delete this project?')) {
await projectStore.deleteProject(id);
MukkeEvents.projectDeleted();
}
}

View file

@ -1,5 +1,6 @@
<script lang="ts">
import { libraryStore } from '$lib/stores/library.svelte';
import { MukkeEvents } from '@manacore/shared-utils/analytics';
interface UploadFile {
file: File;
@ -90,6 +91,7 @@
files[index].status = 'uploaded';
files[index].progress = 100;
files[index].songId = song.id;
MukkeEvents.songUploaded();
// Auto-extract ID3 tags from the uploaded file
try {
@ -107,6 +109,7 @@
} catch (e) {
files[index].status = 'error';
files[index].error = e instanceof Error ? e.message : 'Upload failed';
MukkeEvents.songUploadFailed();
}
}