From 79544160b7d3b3b29825c6033e36a2d00e68bb5a Mon Sep 17 00:00:00 2001 From: Till JS Date: Sun, 22 Mar 2026 18:48:18 +0100 Subject: [PATCH] feat(analytics): add key action tracking to todo, calendar, chat, contacts Add Umami event tracking for the most important user actions: - Todo: task_created (with deadline flag), task_completed, task_deleted - Calendar: event_created (with recurring flag), view_changed - Chat: message_sent (with model ID) - Contacts: contact_created Uses pre-built event helpers from @manacore/shared-utils/analytics. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/calendar/apps/web/src/lib/stores/events.svelte.ts | 2 ++ apps/calendar/apps/web/src/lib/stores/view.svelte.ts | 2 ++ apps/chat/apps/web/src/lib/stores/chat.svelte.ts | 2 ++ apps/contacts/apps/web/src/lib/stores/contacts.svelte.ts | 2 ++ apps/todo/apps/web/src/lib/stores/tasks.svelte.ts | 4 ++++ 5 files changed, 12 insertions(+) diff --git a/apps/calendar/apps/web/src/lib/stores/events.svelte.ts b/apps/calendar/apps/web/src/lib/stores/events.svelte.ts index d02d05650..0336e03b4 100644 --- a/apps/calendar/apps/web/src/lib/stores/events.svelte.ts +++ b/apps/calendar/apps/web/src/lib/stores/events.svelte.ts @@ -8,6 +8,7 @@ import * as api from '$lib/api/events'; import { format, isWithinInterval, isSameDay, differenceInMilliseconds } from 'date-fns'; import { toDate } from '$lib/utils/eventDateHelpers'; import { toastStore } from '@manacore/shared-ui'; +import { CalendarEvents } from '@manacore/shared-utils/analytics'; // State let events = $state([]); @@ -168,6 +169,7 @@ export const eventsStore = { if (result.data) { events = [...events, result.data]; + CalendarEvents.eventCreated(!!data.recurrenceRule); } return result; diff --git a/apps/calendar/apps/web/src/lib/stores/view.svelte.ts b/apps/calendar/apps/web/src/lib/stores/view.svelte.ts index 038275747..87b97e8f9 100644 --- a/apps/calendar/apps/web/src/lib/stores/view.svelte.ts +++ b/apps/calendar/apps/web/src/lib/stores/view.svelte.ts @@ -4,6 +4,7 @@ import { browser } from '$app/environment'; import type { CalendarViewType } from '@calendar/shared'; +import { CalendarEvents } from '@manacore/shared-utils/analytics'; import { startOfDay, startOfWeek, @@ -109,6 +110,7 @@ export const viewStore = { if (browser) { localStorage.setItem('calendar-view-type', type); } + CalendarEvents.viewChanged(type as 'day' | 'week' | 'month' | 'agenda'); }, /** diff --git a/apps/chat/apps/web/src/lib/stores/chat.svelte.ts b/apps/chat/apps/web/src/lib/stores/chat.svelte.ts index 15bb47d66..bdbbb054f 100644 --- a/apps/chat/apps/web/src/lib/stores/chat.svelte.ts +++ b/apps/chat/apps/web/src/lib/stores/chat.svelte.ts @@ -5,6 +5,7 @@ import { chatService } from '$lib/services/chat'; import type { ChatCompletionRequest } from '$lib/services/chat'; import type { Message, AIModel, ChatMessage } from '@chat/types'; +import { ChatEvents } from '@manacore/shared-utils/analytics'; // State let messages = $state([]); @@ -103,6 +104,7 @@ export const chatStore = { createdAt: new Date().toISOString(), }; messages = [...messages, assistantMessage]; + ChatEvents.messageSent(selectedModelId); } else { error = 'Failed to get response'; } diff --git a/apps/contacts/apps/web/src/lib/stores/contacts.svelte.ts b/apps/contacts/apps/web/src/lib/stores/contacts.svelte.ts index 2c33e1df9..5d4363fd6 100644 --- a/apps/contacts/apps/web/src/lib/stores/contacts.svelte.ts +++ b/apps/contacts/apps/web/src/lib/stores/contacts.svelte.ts @@ -8,6 +8,7 @@ import { contactsApi } from '$lib/api/contacts'; import type { Contact, ContactFilters } from '$lib/api/contacts'; import { authStore } from './auth.svelte'; import { generateDemoContacts, isDemoContact } from '$lib/data/demo-contacts'; +import { ContactsEvents } from '@manacore/shared-utils/analytics'; // Default page size for pagination const DEFAULT_PAGE_SIZE = 50; @@ -186,6 +187,7 @@ export const contactsStore = { // Add to local state contacts = [contact, ...contacts]; total += 1; + ContactsEvents.contactCreated(); return contact; } catch (e) { error = e instanceof Error ? e.message : 'Failed to create contact'; diff --git a/apps/todo/apps/web/src/lib/stores/tasks.svelte.ts b/apps/todo/apps/web/src/lib/stores/tasks.svelte.ts index 24c6a1144..b5993351b 100644 --- a/apps/todo/apps/web/src/lib/stores/tasks.svelte.ts +++ b/apps/todo/apps/web/src/lib/stores/tasks.svelte.ts @@ -9,6 +9,7 @@ import * as tasksApi from '$lib/api/tasks'; import { isToday, isPast, isFuture, startOfDay, addDays } from 'date-fns'; import { authStore } from './auth.svelte'; import { generateDemoTasks, isDemoTask } from '$lib/data/demo-tasks'; +import { TodoEvents } from '@manacore/shared-utils/analytics'; // State let tasks = $state([]); @@ -224,6 +225,7 @@ export const tasksStore = { try { const newTask = await tasksApi.createTask(data); tasks = [...tasks, newTask]; + TodoEvents.taskCreated(!!data.dueDate); return newTask; } catch (e) { error = e instanceof Error ? e.message : 'Failed to create task'; @@ -339,6 +341,7 @@ export const tasksStore = { try { await tasksApi.deleteTask(id); tasks = tasks.filter((t) => t.id !== id); + TodoEvents.taskDeleted(); } catch (e) { error = e instanceof Error ? e.message : 'Failed to delete task'; console.error('Failed to delete task:', e); @@ -362,6 +365,7 @@ export const tasksStore = { try { const completedTask = await tasksApi.completeTask(id); tasks = tasks.map((t) => (t.id === id ? completedTask : t)); + TodoEvents.taskCompleted(); return completedTask; } catch (e) { error = e instanceof Error ? e.message : 'Failed to complete task';