-
Notifications
You must be signed in to change notification settings - Fork 48
chore: insights events (IN-1025) #1818
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
joanagmaia
wants to merge
11
commits into
main
Choose a base branch
from
chore/insights-events
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
b7e766c
chore: setup insights events tracking
joanagmaia b762dba
chore: add event-tracking skill
joanagmaia 1185534
chore: collections events tracking
joanagmaia 2f6ce55
chore: update claude skill
joanagmaia d3752f9
fix: migration
joanagmaia d1d108b
fix: api
joanagmaia 34c2a77
fix: pr comments
joanagmaia 87f6200
Merge remote-tracking branch 'origin/main' into chore/insights-events
joanagmaia d67ba6e
fix: insert into events table
joanagmaia d1ecb4b
fix: userId
joanagmaia b8b11f0
chore: track custom and segment events
joanagmaia File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| --- | ||
| name: event-tracking | ||
| description: > | ||
| Add event tracking calls to Vue/Nuxt components in the Insights app using the useTrackEvent composable. | ||
| Use this skill whenever a developer asks to "track" a user action or page view, "add analytics", "instrument" a | ||
| component, "fire an event", implement an event from the events catalog, or wire up tracking for anything in | ||
| the Insights frontend — even if they just say "track when the user clicks X" or "add tracking to this page". | ||
| --- | ||
|
|
||
| # Event Tracking — Insights App | ||
|
|
||
| ## What this skill does | ||
|
|
||
| Helps you add the right `trackEvent()` call to the right place in the codebase, using the catalog-approved event definitions. | ||
|
|
||
| ## File structure | ||
|
|
||
| ``` | ||
| frontend/app/components/shared/types/events/ | ||
| ├── index.ts # EventType, EventFeature enums; EventDefinition interface; | ||
| │ # EventKey union type; aggregated EVENT_DEFINITIONS record | ||
| └── collections.ts # CollectionsEventKey enum + COLLECTIONS_EVENT_DEFINITIONS | ||
| <feature>.ts # (future) FeatureEventKey enum + FEATURE_EVENT_DEFINITIONS | ||
|
|
||
| frontend/composables/useTrackEvent.ts # just the tracking function | ||
| ``` | ||
|
|
||
| **Adding events for a new feature:** create `events/<feature>.ts`, define its `<Feature>EventKey` enum and `<FEATURE>_EVENT_DEFINITIONS` record, then re-export both from `index.ts`. | ||
|
|
||
| **`index.ts` shape:** | ||
| ```ts | ||
| export type EventKey = CollectionsEventKey // | FutureFeatureEventKey | ... | ||
| export const EVENT_DEFINITIONS: Record<EventKey, EventDefinition> = { | ||
| ...COLLECTIONS_EVENT_DEFINITIONS, | ||
| // ...FUTURE_FEATURE_EVENT_DEFINITIONS, | ||
| } | ||
| ``` | ||
|
|
||
| ## How to use | ||
|
|
||
| Always use the **feature-specific key enum** (e.g. `CollectionsEventKey`), not the union `EventKey` type. The composable itself only needs `useTrackEvent` — the key enum comes from the feature's events file. | ||
|
|
||
| ```ts | ||
| import { useTrackEvent } from '~~/composables/useTrackEvent'; | ||
| import { CollectionsEventKey } from '~/components/shared/types/events/collections'; | ||
|
|
||
| const { trackEvent } = useTrackEvent() | ||
|
|
||
| trackEvent({ | ||
| key: CollectionsEventKey.CREATE_COLLECTION, | ||
| properties: { collectionId, isPrivate }, // catalog-defined fields only — optional | ||
| }) | ||
| ``` | ||
|
|
||
| `name`, `type`, `description`, and `feature` are looked up automatically from `EVENT_DEFINITIONS` — **never pass them in the call**. | ||
|
|
||
| `source` (current URL) and `entrySource` (referrer) are captured **automatically** — never pass them manually. | ||
|
|
||
| The call is always fire-and-forget: errors are caught inside the composable and never bubble up. | ||
|
|
||
| ## Step-by-step workflow | ||
|
|
||
| ### 1. Read the events catalog | ||
|
|
||
| Always start by reading `references/events-catalog.md` to find the event that matches what the developer described. The catalog is organized by feature section (e.g. "Community Collections"). Note the `enum value` and `properties` columns — use those exactly. | ||
|
|
||
| If no catalog entry matches, note this to the developer and suggest the closest match or a new entry following the conventions at the bottom of the catalog. | ||
|
|
||
| ### 2. Identify the target file and feature | ||
|
|
||
| Infer the feature from context (e.g. "track when the user creates a collection" → Community Collections). This tells you which key enum to import: | ||
|
|
||
| | Feature | Key enum | Import path | | ||
| |---------|----------|-------------| | ||
| | Community Collections | `CollectionsEventKey` | `~/components/shared/types/events/collections` | | ||
| | (future features) | `<Feature>EventKey` | `~/components/shared/types/events/<feature>` | | ||
|
|
||
| Read the target component or page file before making any changes. You need to understand: | ||
| - Whether `useTrackEvent` is already imported | ||
| - For **feature** events: which function handles the user action | ||
| - For **page** events: whether `onMounted` already exists | ||
|
|
||
| ### 3. Insert the tracking call | ||
|
|
||
| #### Feature events | ||
|
|
||
| Place `trackEvent(...)` inside the action handler, **after** the main logic succeeds. | ||
|
|
||
| ```ts | ||
| const handleCreateCollection = async () => { | ||
| const result = await createCollection(...) | ||
|
|
||
| trackEvent({ | ||
| key: CollectionsEventKey.CREATE_COLLECTION, | ||
| properties: { collectionId: result.id, isPrivate: form.isPrivate }, | ||
| }) | ||
| } | ||
| ``` | ||
|
|
||
| For abandonment events, fire when the user dismisses or navigates away without completing the flow — typically on modal close before the success state is reached. | ||
|
|
||
| #### Page events | ||
|
|
||
| Place inside `onMounted()`. Add to an existing `onMounted` if one already exists. | ||
|
|
||
| ```ts | ||
| onMounted(() => { | ||
| trackEvent({ key: CollectionsEventKey.VIEW_DISCOVER_COLLECTIONS }) | ||
| }) | ||
| ``` | ||
|
|
||
| #### Events that need async data (e.g. `viewerType`) | ||
|
|
||
| If the event requires data that loads asynchronously (e.g. collection details needed to determine `viewerType`), use `watch` with `{ once: true }` instead of `onMounted`: | ||
|
|
||
| ```ts | ||
| watch(data, (collection) => { | ||
| if (!collection) return | ||
| trackEvent({ | ||
| key: CollectionsEventKey.VIEW_COLLECTION, | ||
| properties: { | ||
| collectionId: collection.id, | ||
| viewerType: collection.ssoUserId && user.value?.sub === collection.ssoUserId ? 'owner' : 'guest', | ||
| }, | ||
| }) | ||
| }, { once: true }) | ||
| ``` | ||
|
|
||
| ### 4. Add the imports (if missing) | ||
|
|
||
| ```ts | ||
| import { useTrackEvent } from '~~/composables/useTrackEvent'; | ||
| import { CollectionsEventKey } from '~/components/shared/types/events/collections'; | ||
| ``` | ||
|
|
||
| Then destructure at the top level of `<script setup>`: | ||
|
|
||
| ```ts | ||
| const { trackEvent } = useTrackEvent() | ||
| ``` | ||
|
|
||
| The `~~` alias points to `frontend/`, `~` points to `frontend/app/`. Neither is auto-imported. | ||
|
|
||
| ### 5. Verify | ||
|
|
||
| - No TypeScript errors introduced | ||
| - The tracking call does not change the control flow of existing logic | ||
| - Properties only include fields listed in the catalog for that event | ||
|
|
||
| ## Common patterns | ||
|
|
||
| ### Properties with runtime values | ||
|
|
||
| ```ts | ||
| // Good | ||
| properties: { collectionId: collection.id, isPrivate: collection.isPrivate } | ||
|
|
||
| // Bad | ||
| properties: { collectionId: 'collectionId', isPrivate: 'isPrivate' } | ||
| ``` | ||
|
|
||
| ### Tracking after async operations | ||
|
|
||
| Place after the `await` so it only fires on success: | ||
|
|
||
| ```ts | ||
| await updateCollection(payload) | ||
| trackEvent({ key: CollectionsEventKey.UPDATE_COLLECTION, properties: { collectionId, changedFields } }) | ||
| ``` | ||
|
|
||
| ### Multiple events in one handler | ||
|
|
||
| Track each separately in the right order. | ||
|
|
||
| ## Adding events for a new feature | ||
|
|
||
| 1. Create `frontend/app/components/shared/types/events/<feature>.ts`: | ||
| ```ts | ||
| import { EventFeature, EventType, type EventDefinition } from '.'; | ||
|
|
||
| export enum MyFeatureEventKey { | ||
| DO_THING = 'do-thing', | ||
| } | ||
|
|
||
| export const MY_FEATURE_EVENT_DEFINITIONS: Record<MyFeatureEventKey, EventDefinition> = { | ||
| [MyFeatureEventKey.DO_THING]: { | ||
| key: MyFeatureEventKey.DO_THING, | ||
| type: EventType.FEATURE, | ||
| name: 'Do thing', | ||
| feature: EventFeature.MY_FEATURE, | ||
| }, | ||
| }; | ||
| ``` | ||
|
|
||
| 2. In `index.ts`, extend the union and spread into `EVENT_DEFINITIONS`: | ||
| ```ts | ||
| export type EventKey = CollectionsEventKey | MyFeatureEventKey | ||
| export const EVENT_DEFINITIONS = { | ||
| ...COLLECTIONS_EVENT_DEFINITIONS, | ||
| ...MY_FEATURE_EVENT_DEFINITIONS, | ||
| } | ||
| ``` | ||
|
|
||
| 3. Add `EventFeature.MY_FEATURE` to the `EventFeature` enum in `index.ts`. | ||
|
|
||
| 4. Add the new events to `references/events-catalog.md` under a new section. |
46 changes: 46 additions & 0 deletions
46
.claude/skills/event-tracking/references/events-catalog.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # Insights Events Catalog | ||
|
|
||
| All approved events for the Insights application. Use the exact `enum value` when calling `trackEvent`. Only pass `properties` fields listed below — no extras. | ||
|
|
||
| --- | ||
|
|
||
| ## Community Collections | ||
|
|
||
| **Key enum:** `CollectionsEventKey` from `~/components/shared/types/events/collections` | ||
|
|
||
| | enum value | key | type | name | properties | | ||
| |------------|-----|------|------|------------| | ||
| | `CREATE_COLLECTION` | `create-collection` | `feature` | `Create collection` | `newCollectionId`, `isPrivate` | | ||
| | `UPDATE_COLLECTION` | `update-collection` | `feature` | `Update collection` | `collectionId`, `changedFields` (e.g. `['name', 'privacy']`) | | ||
| | `DELETE_COLLECTION` | `delete-collection` | `feature` | `Delete collection` | `collectionId` | | ||
| | `SHARE_COLLECTION` | `share-collection` | `feature` | `Share collection` | `collectionId`, `shareMethod` | | ||
| | `VIEW_COLLECTION` | `view-collection` | `feature` | `View collection` | `collectionId` | | ||
| | `LIKE_COLLECTION` | `like-collection` | `feature` | `Like collection` | `collectionId` | | ||
| | `DISLIKE_COLLECTION` | `dislike-collection` | `feature` | `Dislike collection` | `collectionId` | | ||
| | `VIEW_DISCOVER_COLLECTIONS` | `view-discover-collections` | `page` | `View Discover Collections` | — | | ||
| | `VIEW_CURATED_COLLECTIONS` | `view-curated-collections` | `page` | `View Curated Collections` | — | | ||
| | `VIEW_COMMUNITY_COLLECTIONS` | `view-community-collections` | `page` | `View Community Collections` | — | | ||
| | `VIEW_MY_COLLECTIONS` | `view-my-collections` | `page` | `View My Collections` | — | | ||
| | `DUPLICATE_COLLECTION` | `duplicate-collection` | `feature` | `Duplicate collections` | `sourceCollectionId`, `newCollectionId` | | ||
| | `ADD_PROJECT_TO_COLLECTION` | `add-project-to-collection` | `feature` | `Add project to collection` | `collectionId`, `projectId` | | ||
| | `ABANDONED_COLLECTION_CREATION` | `abandoned-collection-creation` | `feature` | `Abandoned collection creation` | — | | ||
| | `ABANDONED_COLLECTION_DUPLICATION` | `abandoned-collection-duplication` | `feature` | `Abandoned collection duplication` | — | | ||
| | `ABANDONED_COLLECTION_EDITION` | `abandoned-collection-edition` | `feature` | `Abandoned collection edition` | `sourceCollectionId` | | ||
|
|
||
| --- | ||
|
|
||
| ## Adding new events | ||
|
|
||
| When implementing an event not yet in this catalog, inform the developer so the catalog can be updated. | ||
|
|
||
| **Naming conventions:** | ||
| - `key`: kebab-case, verb-noun format (e.g. `create-collection`) | ||
| - `type`: `'page'` for page views, `'feature'` for user interactions | ||
| - `feature`: the product area name (e.g. `'Community Collections'`) | ||
| - enum value: SCREAMING_SNAKE_CASE matching the key (e.g. `CREATE_COLLECTION`) | ||
|
|
||
| **New feature checklist:** | ||
| 1. Create `events/<feature>.ts` with the key enum and definitions | ||
| 2. Re-export from `events/index.ts` (extend `EventKey` union + spread definitions) | ||
| 3. Add `EventFeature.<FEATURE>` to the enum in `index.ts` | ||
| 4. Add a new section to this catalog |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| -- Events table for tracking user interactions throughout the application | ||
| CREATE TABLE IF NOT EXISTS public.events ( | ||
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | ||
| key TEXT NOT NULL, | ||
| type TEXT NOT NULL, | ||
| name TEXT NOT NULL, | ||
| user_id TEXT, | ||
| created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, | ||
| properties JSONB, | ||
| feature TEXT, | ||
| source TEXT, | ||
| entry_source TEXT | ||
| ); | ||
|
|
||
| -- Indexes for common query patterns | ||
| CREATE INDEX idx_events_key ON public.events(key); | ||
|
|
||
| CREATE INDEX idx_events_type ON public.events(type); | ||
|
|
||
| CREATE INDEX idx_events_user_id ON public.events(user_id); | ||
|
|
||
| CREATE INDEX idx_events_feature ON public.events(feature); | ||
|
|
||
| CREATE INDEX idx_events_created_at ON public.events(created_at DESC); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.