Skip to content

posthog migration: part 4 #7366

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

Merged
merged 1 commit into from
Jun 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion .cursor/rules/dashboard.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ description: Rules for writing features in apps/dashboard
globs: dashboard
alwaysApply: false
---

# Reusable Core UI Components

- Always import from the central UI library under `@/components/ui/*` – e.g. `import { Button } from "@/components/ui/button"`.
Expand Down Expand Up @@ -101,3 +100,29 @@ Guidelines:
- Keep `queryKey` stable and descriptive for cache hits.
- Prefer API routes or server actions to keep tokens secret; the browser only sees relative paths.
- Configure `staleTime` / `cacheTime` according to freshness requirements.

# Analytics Event Reporting

- **Add events intentionally** – only when they answer a concrete product/business question.
- **Event name**: human-readable `<subject> <verb>` phrase (e.g. `"contract deployed"`).
- **Reporting helper**: `report<Subject><Verb>` (PascalCase); all live in `src/@/analytics/report.ts`.
- **Mandatory JSDoc**: explain *Why* the event exists and *Who* owns it (`@username`).
- **Typed properties**: accept a single `properties` object and pass it unchanged to `posthog.capture`.
- **Client-side only**: never import `posthog-js` in server components.
- **Housekeeping**: ping **#core-services** before renaming or removing an event.

```ts
/**
* ### Why do we need to report this event?
* - Tracks number of contracts deployed
*
* ### Who is responsible for this event?
* @jnsdls
*/
export function reportContractDeployed(properties: {
address: string;
chainId: number;
}) {
posthog.capture("contract deployed", properties);
}
```
35 changes: 35 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,41 @@ Welcome, AI copilots! This guide captures the coding standards, architectural de
-- Configure staleTime / cacheTime based on freshness requirements (default ≥ 60 s).
-- Keep tokens secret by calling internal API routes or server actions.

6.5 Analytics Event Reporting

- **When to create a new event**
-- Only add events that answer a clear product or business question.
-- Check `src/@/analytics/report.ts` first; avoid duplicates.

- **Naming conventions**
-- **Event name**: human-readable phrase in the form `<subject> <verb>` (e.g. "contract deployed").
-- **Reporting function**: `report<Subject><Verb>` (PascalCase).
-- All reporting helpers currently live in the shared `report.ts` file.

- **Boilerplate template**
-- Add a JSDoc header explaining **Why** the event exists and **Who** owns it (`@username`).
-- Accept a single typed `properties` object and forward it unchanged to `posthog.capture`.
-- Example:

```ts
/**
* ### Why do we need to report this event?
* - Tracks number of contracts deployed
*
* ### Who is responsible for this event?
* @jnsdls
*/
export function reportContractDeployed(properties: {
address: string;
chainId: number;
}) {
posthog.capture("contract deployed", properties);
}
```

- **Client-side only**: never import `posthog-js` in server components.
- **Housekeeping**: Inform **#eng-core-services** before renaming or removing an existing event.


7. Performance & Bundle Size
Expand Down
67 changes: 67 additions & 0 deletions apps/dashboard/src/@/analytics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Analytics Guidelines

This folder centralises the **PostHog** tracking logic for the dashboard app.
Most developers will only need to add or extend _event-reporting_ functions in `report.ts`.

---

## 1. When to add an event
1. Ask yourself if the data will be **actionable**. Every event should have a clear product or business question it helps answer.
2. Check if a similar event already exists in `report.ts`. Avoid duplicates.

---

## 2. Naming conventions
| Concept | Convention | Example |
|---------|------------|---------|
| **Event name** (string sent to PostHog) | Human-readable phrase formatted as `<subject> <verb>` | `"contract deployed"` |
| **Reporting function** | `report<Subject><Verb>` (PascalCase) | `reportContractDeployed` |
| **File** | All event functions live in the shared `report.ts` file (for now) | — |

> Keeping names predictable makes it easy to search both code and analytics.

---

## 3. Boilerplate / template
Add a new function to `report.ts` following this pattern:

```ts
/**
* ### Why do we need to report this event?
* - _Add bullet points explaining the product metrics/questions this event answers._
*
* ### Who is responsible for this event?
* @your-github-handle
*/
export function reportExampleEvent(properties: {
/* Add typed properties here */
}) {
posthog.capture("example event", {
/* Pass the same properties here */
});
}
```

Guidelines:
1. **Explain the "why".** The JSDoc block is mandatory so future contributors know the purpose.
2. **Type everything.** The `properties` object should be fully typed—this doubles as documentation.
3. **Client-side only.** `posthog-js` must never run on the server. Call these reporting helpers from client components, event handlers, etc.

---

## 4. Editing or removing events
1. Update both the function and the PostHog event definition (if required).
2. Inform the core services team before removing or renaming an event.

---

## 5. Identification & housekeeping (FYI)
Most devs can ignore this section, but for completeness:

- `hooks/identify-account.ts` and `hooks/identify-team.ts` wrap `posthog.identify`/`group` calls.
- `resetAnalytics` clears identity state (used on logout).

---

## 6. Need help?
Ping #eng-core-services in slack.
150 changes: 150 additions & 0 deletions apps/dashboard/src/@/analytics/report.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import posthog from "posthog-js";

import type { Team } from "../api/team";

// ----------------------------
// CONTRACTS
// ----------------------------

/**
* ### Why do we need to report this event?
* - To track the number of contracts deployed
* - To track the number of contracts deployed on each chain
*
* ### Who is responsible for this event?
* @jnsdls
*
*/
export function reportContractDeployed(properties: {
address: string;
chainId: number;
publisher: string | undefined;
contractName: string | undefined;
}) {
posthog.capture("contract deployed", properties);
}

/**
* ### Why do we need to report this event?
* - To track the number of contracts that failed to deploy
* - To track the error message of the failed contract deployment (so we can fix it / add workarounds)
*
* ### Who is responsible for this event?
* @jnsdls
*
*/
export function reportContractDeployFailed(properties: {
errorMessage: string;
}) {
posthog.capture("contract deploy failed", properties);
}

// ----------------------------
// ONBOARDING (TEAM)
// ----------------------------

/**
* ### Why do we need to report this event?
* - To track the number of teams that enter the onboarding flow
*
* ### Who is responsible for this event?
* @jnsdls
*
*/
export function reportOnboardingStarted() {
posthog.capture("onboarding started");
}

/**
* ### Why do we need to report this event?
* - To track the number of teams that select a paid plan during onboarding
* - To know **which** plan was selected
*
* ### Who is responsible for this event?
* @jnsdls
*
*/
export function reportOnboardingPlanSelected(properties: {
plan: Team["billingPlan"];
}) {
posthog.capture("onboarding plan selected", properties);
}

/**
* ### Why do we need to report this event?
* - To track the number of teams that skip the plan-selection step during onboarding
*
* ### Who is responsible for this event?
* @jnsdls
*
*/
export function reportOnboardingPlanSelectionSkipped() {
posthog.capture("onboarding plan selection skipped");
}

/**
* ### Why do we need to report this event?
* - To track the number of teams that invite members during onboarding
* - To track **how many** members were invited
*
* ### Who is responsible for this event?
* @jnsdls
*
*/
export function reportOnboardingMembersInvited(properties: {
count: number;
}) {
posthog.capture("onboarding members invited", {
count: properties.count,
});
}

/**
* ### Why do we need to report this event?
* - To track the number of teams that skip inviting members during onboarding
*
* ### Who is responsible for this event?
* @jnsdls
*
*/
export function reportOnboardingMembersSkipped() {
posthog.capture("onboarding members skipped");
}

/**
* ### Why do we need to report this event?
* - To track how many teams click the upsell (upgrade) button on the member-invite step during onboarding
*
* ### Who is responsible for this event?
* @jnsdls
*
*/
export function reportOnboardingMembersUpsellButtonClicked() {
posthog.capture("onboarding members upsell clicked");
}

/**
* ### Why do we need to report this event?
* - To track which plan is selected from the members-step upsell during onboarding
*
* ### Who is responsible for this event?
* @jnsdls
*
*/
export function reportOnboardingMembersUpsellPlanSelected(properties: {
plan: Team["billingPlan"];
}) {
posthog.capture("onboarding members upsell plan selected", properties);
}

/**
* ### Why do we need to report this event?
* - To track the number of teams that completed onboarding
*
* ### Who is responsible for this event?
* @jnsdls
*
*/
export function reportOnboardingCompleted() {
posthog.capture("onboarding completed");
}
Loading
Loading