8408fd6466
ci/woodpecker/push/woodpecker Pipeline was successful
SEO : - composable useSeoPage() : og:*, Twitter Cards, canonical sur toutes les pages (15 pages) - app.vue : JSON-LD Organization + Book, og:image global og-default.png - og-default.png 1200×630 : logo § calligraphique + texte (Pillow) - nuxt.config.ts : @nuxtjs/sitemap avec 26 URLs statiques Analytics Umami : - useTracking() : helpers typés audio/pdf/player/scroll/cta - useScrollTracking() : scroll depth 25/50/75/100% + liens externes auto - useAudioPlayer : trackAudioPlay/Progress/Complete - BookPdfReader : trackPdfOpen/Close avec durée - BookPlayer : trackPlayerOpen/Chapter/Mode - docker-compose : variables NUXT_PUBLIC_UMAMI_* passées au container Images : - Couv-Economie-du-don.jpg ajoutée dans public/images/ - bookplayer.config.yml + home.yml : références mises à jour Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
97 lines
3.6 KiB
TypeScript
97 lines
3.6 KiB
TypeScript
/**
|
|
* Umami analytics wrapper — safe server-side, no-op when not configured.
|
|
*
|
|
* Events convention:
|
|
* audio:play | audio:pause | audio:complete | audio:progress
|
|
* pdf:open | pdf:close
|
|
* player:open | player:chapter | player:mode
|
|
* scroll:depth
|
|
* link:external
|
|
* cta:click
|
|
*/
|
|
export function useTracking() {
|
|
const runtimeConfig = useRuntimeConfig()
|
|
const enabled = !!runtimeConfig.public.umamiWebsiteId
|
|
|
|
// ── Core ────────────────────────────────────────────────────────────────
|
|
function track(event: string, data?: Record<string, unknown>) {
|
|
if (!import.meta.client || !enabled) return
|
|
const umami = (window as Record<string, unknown>).umami as
|
|
| { track: (event: string, data?: unknown) => void }
|
|
| undefined
|
|
umami?.track(event, data)
|
|
}
|
|
|
|
// ── Audio ────────────────────────────────────────────────────────────────
|
|
function trackAudioPlay(songId: string, songTitle: string, context?: string) {
|
|
track('audio:play', { song_id: songId, song_title: songTitle, context })
|
|
}
|
|
|
|
function trackAudioPause(songId: string, progressPct: number) {
|
|
track('audio:pause', { song_id: songId, progress_pct: progressPct })
|
|
}
|
|
|
|
function trackAudioComplete(songId: string, songTitle: string) {
|
|
track('audio:complete', { song_id: songId, song_title: songTitle })
|
|
}
|
|
|
|
/** Fired at 25 / 50 / 75 % milestones */
|
|
function trackAudioProgress(songId: string, milestone: 25 | 50 | 75) {
|
|
track('audio:progress', { song_id: songId, milestone })
|
|
}
|
|
|
|
// ── PDF ──────────────────────────────────────────────────────────────────
|
|
function trackPdfOpen(trigger?: string) {
|
|
track('pdf:open', { trigger })
|
|
}
|
|
|
|
function trackPdfClose(pagesVisited: number, durationMs: number) {
|
|
track('pdf:close', { pages_visited: pagesVisited, duration_ms: durationMs })
|
|
}
|
|
|
|
// ── BookPlayer ───────────────────────────────────────────────────────────
|
|
function trackPlayerOpen(trigger?: string) {
|
|
track('player:open', { trigger })
|
|
}
|
|
|
|
function trackPlayerChapter(chapterSlug: string) {
|
|
track('player:chapter', { chapter_slug: chapterSlug })
|
|
}
|
|
|
|
function trackPlayerMode(mode: 'guided' | 'scroll') {
|
|
track('player:mode', { mode })
|
|
}
|
|
|
|
// ── Navigation & UX ──────────────────────────────────────────────────────
|
|
function trackScrollDepth(page: string, depth: 25 | 50 | 75 | 100) {
|
|
track('scroll:depth', { page, depth })
|
|
}
|
|
|
|
function trackExternalLink(url: string, label?: string) {
|
|
const route = useRoute()
|
|
track('link:external', { url, label, from_page: route.path })
|
|
}
|
|
|
|
function trackCta(label: string, target?: string) {
|
|
const route = useRoute()
|
|
track('cta:click', { label, target, from_page: route.path })
|
|
}
|
|
|
|
return {
|
|
track,
|
|
enabled,
|
|
trackAudioPlay,
|
|
trackAudioPause,
|
|
trackAudioComplete,
|
|
trackAudioProgress,
|
|
trackPdfOpen,
|
|
trackPdfClose,
|
|
trackPlayerOpen,
|
|
trackPlayerChapter,
|
|
trackPlayerMode,
|
|
trackScrollDepth,
|
|
trackExternalLink,
|
|
trackCta,
|
|
}
|
|
}
|