mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:41:09 +02:00
feat(analytics): expand umami tracking in todo, calendar, and contacts apps
Todo: - Track projectCreated, projectDeleted, labelCreated - Track taskUncompleted - Track quickAddUsed via QuickInputBar Calendar: - Track eventUpdated, eventDeleted - Track calendarCreated, calendarDeleted Contacts: - Track contactUpdated, contactDeleted, contactFavorited, contactArchived - Track searchPerformed in SearchModal - Track contactExported in ExportModal - Track contactImported for both Google and file (vCard/CSV) imports Also extends analytics event helpers with new event types. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c1ef55fd54
commit
dd477d5fda
12 changed files with 39 additions and 5 deletions
|
|
@ -8,6 +8,7 @@ import * as api from '$lib/api/calendars';
|
|||
import { BIRTHDAY_CALENDAR } from '$lib/api/birthdays';
|
||||
import { settingsStore } from './settings.svelte';
|
||||
import { authStore } from './auth.svelte';
|
||||
import { CalendarEvents } from '@manacore/shared-utils/analytics';
|
||||
|
||||
// Guest calendar for unauthenticated users
|
||||
const GUEST_CALENDAR: Calendar = {
|
||||
|
|
@ -128,6 +129,7 @@ export const calendarsStore = {
|
|||
|
||||
if (result.data) {
|
||||
calendars = [...calendars, result.data];
|
||||
CalendarEvents.calendarCreated();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -154,6 +156,7 @@ export const calendarsStore = {
|
|||
|
||||
if (!result.error) {
|
||||
calendars = getCalendarsArray().filter((c) => c.id !== id);
|
||||
CalendarEvents.calendarDeleted();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ export const eventsStore = {
|
|||
toastStore.error(`Termin konnte nicht aktualisiert werden: ${result.error.message}`);
|
||||
} else if (result.data) {
|
||||
events = events.map((e) => (e.id === id ? result.data! : e));
|
||||
CalendarEvents.eventUpdated();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -207,6 +208,7 @@ export const eventsStore = {
|
|||
}
|
||||
toastStore.error(`Termin konnte nicht gelöscht werden: ${result.error.message}`);
|
||||
} else {
|
||||
CalendarEvents.eventDeleted();
|
||||
toastStore.success('Termin gelöscht');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import { contactsApi, type Contact } from '$lib/api/contacts';
|
||||
import { newContactModalStore } from '$lib/stores/new-contact-modal.svelte';
|
||||
import { ContactsEvents } from '@manacore/shared-utils/analytics';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
|
|
@ -47,6 +48,7 @@
|
|||
});
|
||||
results = response.contacts || [];
|
||||
selectedIndex = 0;
|
||||
ContactsEvents.searchPerformed();
|
||||
} catch (e) {
|
||||
console.error('Search error:', e);
|
||||
results = [];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { exportApi, type ExportFormat } from '$lib/api/export';
|
||||
import { ContactsEvents } from '@manacore/shared-utils/analytics';
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
contactIds: selectedContactIds.length > 0 ? selectedContactIds : undefined,
|
||||
includeArchived,
|
||||
});
|
||||
ContactsEvents.contactExported(format as 'csv' | 'vcard');
|
||||
onClose();
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Export fehlgeschlagen';
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
} from '$lib/api/google';
|
||||
import { contactsStore } from '$lib/stores/contacts.svelte';
|
||||
import { GoogleImportSkeleton } from '$lib/components/skeletons';
|
||||
import { ContactsEvents } from '@manacore/shared-utils/analytics';
|
||||
|
||||
type Step = 'connect' | 'select' | 'result';
|
||||
|
||||
|
|
@ -128,6 +129,7 @@
|
|||
try {
|
||||
result = await googleApi.importContacts(Array.from(selectedContacts));
|
||||
step = 'result';
|
||||
ContactsEvents.contactImported('google', result.imported);
|
||||
// Refresh contacts list
|
||||
await contactsStore.loadContacts();
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ export const contactsStore = {
|
|||
if (selectedContact?.id === id) {
|
||||
selectedContact = contact;
|
||||
}
|
||||
ContactsEvents.contactUpdated();
|
||||
return contact;
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Failed to update contact';
|
||||
|
|
@ -258,6 +259,7 @@ export const contactsStore = {
|
|||
if (selectedContact?.id === id) {
|
||||
selectedContact = null;
|
||||
}
|
||||
ContactsEvents.contactDeleted();
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Failed to delete contact';
|
||||
console.error('Failed to delete contact:', e);
|
||||
|
|
@ -284,6 +286,7 @@ export const contactsStore = {
|
|||
if (selectedContact?.id === id) {
|
||||
selectedContact = contact;
|
||||
}
|
||||
ContactsEvents.contactFavorited();
|
||||
return contact;
|
||||
} catch (e) {
|
||||
console.error('Failed to toggle favorite:', e);
|
||||
|
|
@ -309,6 +312,7 @@ export const contactsStore = {
|
|||
if (selectedContact?.id === id) {
|
||||
selectedContact = null;
|
||||
}
|
||||
ContactsEvents.contactArchived();
|
||||
return contact;
|
||||
} catch (e) {
|
||||
console.error('Failed to toggle archive:', e);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import { exportApi, type ExportFormat } from '$lib/api/export';
|
||||
import { contactsStore } from '$lib/stores/contacts.svelte';
|
||||
import { ImportPreviewSkeleton } from '$lib/components/skeletons';
|
||||
import { ContactsEvents } from '@manacore/shared-utils/analytics';
|
||||
import '$lib/i18n';
|
||||
|
||||
type Tab = 'import' | 'export';
|
||||
|
|
@ -111,6 +112,8 @@
|
|||
try {
|
||||
importResult = await importApi.execute(preview.contacts, duplicateAction, skipIndices);
|
||||
importStep = 'result';
|
||||
const fileExt = selectedFile?.name?.endsWith('.csv') ? 'csv' : 'vcard';
|
||||
ContactsEvents.contactImported(fileExt as 'csv' | 'vcard', importResult?.imported);
|
||||
await contactsStore.loadContacts();
|
||||
} catch (e) {
|
||||
importError = e instanceof Error ? e.message : 'Fehler beim Importieren';
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import type { Label } from '$lib/api/labels';
|
||||
import * as labelsApi from '$lib/api/labels';
|
||||
import { TodoEvents } from '@manacore/shared-utils/analytics';
|
||||
|
||||
// State
|
||||
let labels = $state<Label[]>([]);
|
||||
|
|
@ -65,6 +66,7 @@ export const labelsStore = {
|
|||
try {
|
||||
const newLabel = await labelsApi.createLabel(data);
|
||||
labels = [...labels, newLabel];
|
||||
TodoEvents.labelCreated();
|
||||
return newLabel;
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Failed to create label';
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
import type { Project } from '@todo/shared';
|
||||
import * as projectsApi from '$lib/api/projects';
|
||||
import { authStore } from './auth.svelte';
|
||||
import { TodoEvents } from '@manacore/shared-utils/analytics';
|
||||
|
||||
// Guest inbox project for unauthenticated users
|
||||
const GUEST_INBOX: Project = {
|
||||
|
|
@ -108,6 +109,7 @@ export const projectsStore = {
|
|||
try {
|
||||
const newProject = await projectsApi.createProject(data);
|
||||
projects = [...projects, newProject];
|
||||
TodoEvents.projectCreated();
|
||||
return newProject;
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Failed to create project';
|
||||
|
|
@ -145,6 +147,7 @@ export const projectsStore = {
|
|||
try {
|
||||
await projectsApi.deleteProject(id);
|
||||
projects = projects.filter((p) => p.id !== id);
|
||||
TodoEvents.projectDeleted();
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Failed to delete project';
|
||||
console.error('Failed to delete project:', e);
|
||||
|
|
|
|||
|
|
@ -390,6 +390,7 @@ export const tasksStore = {
|
|||
try {
|
||||
const uncompletedTask = await tasksApi.uncompleteTask(id);
|
||||
tasks = tasks.map((t) => (t.id === id ? uncompletedTask : t));
|
||||
TodoEvents.taskUncompleted();
|
||||
return uncompletedTask;
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Failed to uncomplete task';
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
import { parseTaskInput, resolveTaskIds, formatParsedTaskPreview } from '$lib/utils/task-parser';
|
||||
import { todoOnboarding } from '$lib/stores/app-onboarding.svelte';
|
||||
import { MiniOnboardingModal } from '@manacore/shared-app-onboarding';
|
||||
import { TodoEvents } from '@manacore/shared-utils/analytics';
|
||||
|
||||
// App switcher items
|
||||
const appItems = getPillAppItems('todo');
|
||||
|
|
@ -102,6 +103,7 @@
|
|||
projectId: resolved.projectId,
|
||||
labelIds: resolved.labelIds,
|
||||
});
|
||||
TodoEvents.quickAddUsed();
|
||||
} catch (error) {
|
||||
console.error('Failed to create task:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,10 +189,14 @@ export const TodoEvents = {
|
|||
taskCreated: (hasDeadline = false) => trackEvent('task_created', { has_deadline: hasDeadline }),
|
||||
taskCompleted: () => trackEvent('task_completed'),
|
||||
taskDeleted: () => trackEvent('task_deleted'),
|
||||
taskUncompleted: () => trackEvent('task_uncompleted'),
|
||||
subtaskCompleted: () => trackEvent('subtask_completed'),
|
||||
projectCreated: () => trackEvent('project_created'),
|
||||
projectDeleted: () => trackEvent('project_deleted'),
|
||||
labelCreated: () => trackEvent('label_created'),
|
||||
viewChanged: (view: 'inbox' | 'today' | 'upcoming' | 'project') =>
|
||||
trackEvent('view_changed', { view }),
|
||||
viewChanged: (view: string) => trackEvent('view_changed', { view }),
|
||||
quickAddUsed: () => trackEvent('quick_add_used'),
|
||||
filterUsed: (filterType: string) => trackEvent('filter_used', { filter: filterType }),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -203,9 +207,11 @@ export const CalendarEvents = {
|
|||
eventUpdated: () => trackEvent('event_updated'),
|
||||
eventDeleted: () => trackEvent('event_deleted'),
|
||||
calendarCreated: () => trackEvent('calendar_created'),
|
||||
calendarDeleted: () => trackEvent('calendar_deleted'),
|
||||
calendarShared: () => trackEvent('calendar_shared'),
|
||||
viewChanged: (view: 'day' | 'week' | 'month' | 'agenda') => trackEvent('view_changed', { view }),
|
||||
viewChanged: (view: string) => trackEvent('view_changed', { view }),
|
||||
reminderSet: (minutesBefore: number) => trackEvent('reminder_set', { minutes: minutesBefore }),
|
||||
eventDragged: () => trackEvent('event_dragged'),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -229,8 +235,10 @@ export const ContactsEvents = {
|
|||
contactCreated: () => trackEvent('contact_created'),
|
||||
contactUpdated: () => trackEvent('contact_updated'),
|
||||
contactDeleted: () => trackEvent('contact_deleted'),
|
||||
contactImported: (source: 'google' | 'csv' | 'vcard') =>
|
||||
trackEvent('contact_imported', { source }),
|
||||
contactFavorited: () => trackEvent('contact_favorited'),
|
||||
contactArchived: () => trackEvent('contact_archived'),
|
||||
contactImported: (source: 'google' | 'csv' | 'vcard', count?: number) =>
|
||||
trackEvent('contact_imported', { source, ...(count !== undefined && { count }) }),
|
||||
contactExported: (format: 'csv' | 'vcard') => trackEvent('contact_exported', { format }),
|
||||
tagCreated: () => trackEvent('tag_created'),
|
||||
searchPerformed: () => trackEvent('search_performed'),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue