@codaijs/keel 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/cli.test.d.ts +2 -0
- package/dist/__tests__/cli.test.d.ts.map +1 -0
- package/dist/__tests__/cli.test.js +173 -0
- package/dist/__tests__/cli.test.js.map +1 -0
- package/dist/__tests__/registry.test.d.ts +2 -0
- package/dist/__tests__/registry.test.d.ts.map +1 -0
- package/dist/__tests__/registry.test.js +86 -0
- package/dist/__tests__/registry.test.js.map +1 -0
- package/dist/__tests__/sail-installer.test.d.ts +2 -0
- package/dist/__tests__/sail-installer.test.d.ts.map +1 -0
- package/dist/__tests__/sail-installer.test.js +158 -0
- package/dist/__tests__/sail-installer.test.js.map +1 -0
- package/dist/create-runner.d.ts +11 -0
- package/dist/create-runner.d.ts.map +1 -0
- package/dist/create-runner.js +63 -0
- package/dist/create-runner.js.map +1 -0
- package/dist/create.d.ts +10 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +15 -0
- package/dist/create.js.map +1 -0
- package/dist/manage.d.ts +24 -0
- package/dist/manage.d.ts.map +1 -0
- package/dist/manage.js +1461 -0
- package/dist/manage.js.map +1 -0
- package/dist/prompts.d.ts +36 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +208 -0
- package/dist/prompts.js.map +1 -0
- package/dist/sail-installer.d.ts +37 -0
- package/dist/sail-installer.d.ts.map +1 -0
- package/dist/sail-installer.js +935 -0
- package/dist/sail-installer.js.map +1 -0
- package/dist/scaffold.d.ts +10 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +297 -0
- package/dist/scaffold.js.map +1 -0
- package/package.json +57 -0
- package/sails/_template/addon.json +20 -0
- package/sails/_template/install.ts +402 -0
- package/sails/admin-dashboard/README.md +117 -0
- package/sails/admin-dashboard/addon.json +28 -0
- package/sails/admin-dashboard/files/backend/middleware/admin.ts +34 -0
- package/sails/admin-dashboard/files/backend/routes/admin.ts +243 -0
- package/sails/admin-dashboard/files/frontend/components/admin/StatsCard.tsx +40 -0
- package/sails/admin-dashboard/files/frontend/components/admin/UsersTable.tsx +240 -0
- package/sails/admin-dashboard/files/frontend/hooks/useAdmin.ts +149 -0
- package/sails/admin-dashboard/files/frontend/pages/admin/Dashboard.tsx +173 -0
- package/sails/admin-dashboard/files/frontend/pages/admin/UserDetail.tsx +203 -0
- package/sails/admin-dashboard/install.ts +305 -0
- package/sails/analytics/README.md +178 -0
- package/sails/analytics/addon.json +27 -0
- package/sails/analytics/files/frontend/components/AnalyticsProvider.tsx +58 -0
- package/sails/analytics/files/frontend/hooks/useAnalytics.ts +64 -0
- package/sails/analytics/files/frontend/lib/analytics.ts +103 -0
- package/sails/analytics/install.ts +297 -0
- package/sails/file-uploads/README.md +191 -0
- package/sails/file-uploads/addon.json +30 -0
- package/sails/file-uploads/files/backend/routes/files.ts +198 -0
- package/sails/file-uploads/files/backend/schema/files.ts +36 -0
- package/sails/file-uploads/files/backend/services/file-storage.ts +128 -0
- package/sails/file-uploads/files/frontend/components/FileList.tsx +248 -0
- package/sails/file-uploads/files/frontend/components/FileUploadButton.tsx +147 -0
- package/sails/file-uploads/files/frontend/hooks/useFileUpload.ts +106 -0
- package/sails/file-uploads/files/frontend/hooks/useFiles.ts +118 -0
- package/sails/file-uploads/files/frontend/pages/Files.tsx +37 -0
- package/sails/file-uploads/install.ts +466 -0
- package/sails/gdpr/README.md +174 -0
- package/sails/gdpr/addon.json +27 -0
- package/sails/gdpr/files/backend/routes/gdpr.ts +140 -0
- package/sails/gdpr/files/backend/services/gdpr.ts +293 -0
- package/sails/gdpr/files/frontend/components/auth/ConsentCheckboxes.tsx +97 -0
- package/sails/gdpr/files/frontend/components/gdpr/AccountDeletionRequest.tsx +192 -0
- package/sails/gdpr/files/frontend/components/gdpr/DataExportButton.tsx +75 -0
- package/sails/gdpr/files/frontend/pages/PrivacyPolicy.tsx +186 -0
- package/sails/gdpr/install.ts +756 -0
- package/sails/google-oauth/README.md +121 -0
- package/sails/google-oauth/addon.json +22 -0
- package/sails/google-oauth/files/GoogleButton.tsx +50 -0
- package/sails/google-oauth/install.ts +252 -0
- package/sails/i18n/README.md +193 -0
- package/sails/i18n/addon.json +30 -0
- package/sails/i18n/files/frontend/components/LanguageSwitcher.tsx +108 -0
- package/sails/i18n/files/frontend/hooks/useLanguage.ts +31 -0
- package/sails/i18n/files/frontend/lib/i18n.ts +32 -0
- package/sails/i18n/files/frontend/locales/de/common.json +44 -0
- package/sails/i18n/files/frontend/locales/en/common.json +44 -0
- package/sails/i18n/install.ts +407 -0
- package/sails/push-notifications/README.md +163 -0
- package/sails/push-notifications/addon.json +31 -0
- package/sails/push-notifications/files/backend/routes/notifications.ts +153 -0
- package/sails/push-notifications/files/backend/schema/notifications.ts +31 -0
- package/sails/push-notifications/files/backend/services/notifications.ts +117 -0
- package/sails/push-notifications/files/frontend/components/PushNotificationInit.tsx +12 -0
- package/sails/push-notifications/files/frontend/hooks/usePushNotifications.ts +154 -0
- package/sails/push-notifications/install.ts +384 -0
- package/sails/r2-storage/README.md +101 -0
- package/sails/r2-storage/addon.json +29 -0
- package/sails/r2-storage/files/backend/services/storage.ts +71 -0
- package/sails/r2-storage/files/frontend/components/ProfilePictureUpload.tsx +167 -0
- package/sails/r2-storage/install.ts +412 -0
- package/sails/rate-limiting/README.md +145 -0
- package/sails/rate-limiting/addon.json +20 -0
- package/sails/rate-limiting/files/backend/middleware/rate-limit-store.ts +104 -0
- package/sails/rate-limiting/files/backend/middleware/rate-limit.ts +137 -0
- package/sails/rate-limiting/install.ts +300 -0
- package/sails/registry.json +107 -0
- package/sails/stripe/README.md +214 -0
- package/sails/stripe/addon.json +24 -0
- package/sails/stripe/files/backend/routes/stripe.ts +154 -0
- package/sails/stripe/files/backend/schema/stripe.ts +74 -0
- package/sails/stripe/files/backend/services/stripe.ts +224 -0
- package/sails/stripe/files/frontend/components/SubscriptionStatus.tsx +135 -0
- package/sails/stripe/files/frontend/hooks/useSubscription.ts +86 -0
- package/sails/stripe/files/frontend/pages/Checkout.tsx +116 -0
- package/sails/stripe/files/frontend/pages/Pricing.tsx +226 -0
- package/sails/stripe/install.ts +378 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# PostHog Analytics Sail
|
|
2
|
+
|
|
3
|
+
Adds privacy-friendly analytics to your keel application using PostHog. Supports automatic page view tracking, user identification, custom events, and more.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Automatic page view tracking on SPA route changes
|
|
8
|
+
- User identification tied to BetterAuth sessions
|
|
9
|
+
- Custom event tracking API
|
|
10
|
+
- Session recording and heatmaps (configurable in PostHog)
|
|
11
|
+
- Feature flags support
|
|
12
|
+
- Works with PostHog Cloud or self-hosted instances
|
|
13
|
+
- GDPR-compatible (supports cookie-less tracking and consent)
|
|
14
|
+
- No backend dependencies — everything runs client-side
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
- A PostHog account (cloud or self-hosted)
|
|
19
|
+
- Cloud: https://app.posthog.com (US) or https://eu.posthog.com (EU)
|
|
20
|
+
- Self-hosted: https://posthog.com/docs/self-host
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx tsx sails/analytics/install.ts
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The installer will ask whether you are using PostHog Cloud or self-hosted and collect your API key.
|
|
29
|
+
|
|
30
|
+
## Manual Setup
|
|
31
|
+
|
|
32
|
+
### 1. Get Your API Key
|
|
33
|
+
|
|
34
|
+
#### PostHog Cloud
|
|
35
|
+
1. Sign up at https://app.posthog.com
|
|
36
|
+
2. Create a project
|
|
37
|
+
3. Go to **Project Settings**
|
|
38
|
+
4. Copy the **Project API Key** (starts with `phc_`)
|
|
39
|
+
|
|
40
|
+
#### Self-Hosted
|
|
41
|
+
1. Log into your PostHog instance
|
|
42
|
+
2. Go to **Project Settings**
|
|
43
|
+
3. Copy the **Project API Key**
|
|
44
|
+
|
|
45
|
+
### 2. Environment Variables
|
|
46
|
+
|
|
47
|
+
```env
|
|
48
|
+
VITE_POSTHOG_KEY=phc_your_project_api_key
|
|
49
|
+
VITE_POSTHOG_HOST=https://us.i.posthog.com
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For EU cloud, use `https://eu.i.posthog.com`. For self-hosted, use your instance URL.
|
|
53
|
+
|
|
54
|
+
## Architecture
|
|
55
|
+
|
|
56
|
+
### Analytics Service (`src/lib/analytics.ts`)
|
|
57
|
+
|
|
58
|
+
Singleton service that wraps the PostHog SDK:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import analytics from "@/lib/analytics";
|
|
62
|
+
|
|
63
|
+
// Initialize (done automatically by AnalyticsProvider)
|
|
64
|
+
analytics.init();
|
|
65
|
+
|
|
66
|
+
// Identify user after login
|
|
67
|
+
analytics.identify("user-123", { email: "user@example.com", plan: "pro" });
|
|
68
|
+
|
|
69
|
+
// Track custom event
|
|
70
|
+
analytics.trackEvent("feature_used", { feature: "dark-mode" });
|
|
71
|
+
|
|
72
|
+
// Track page view (done automatically on route changes)
|
|
73
|
+
analytics.trackPageView();
|
|
74
|
+
|
|
75
|
+
// Reset on logout
|
|
76
|
+
analytics.reset();
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
When `VITE_POSTHOG_KEY` is not set (local development), all calls become no-ops.
|
|
80
|
+
|
|
81
|
+
### AnalyticsProvider Component
|
|
82
|
+
|
|
83
|
+
Wraps your app and handles:
|
|
84
|
+
- PostHog initialization on mount
|
|
85
|
+
- User identification when logged in
|
|
86
|
+
- Identity reset on logout
|
|
87
|
+
- Page view tracking on route changes
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
// In App.tsx (added automatically by installer)
|
|
91
|
+
import { AnalyticsProvider } from "./components/AnalyticsProvider";
|
|
92
|
+
|
|
93
|
+
export default function App() {
|
|
94
|
+
return (
|
|
95
|
+
<AnalyticsProvider>
|
|
96
|
+
<AppRouter />
|
|
97
|
+
</AnalyticsProvider>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### useAnalytics Hook
|
|
103
|
+
|
|
104
|
+
For tracking events in components:
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { useAnalytics } from "@/hooks/useAnalytics";
|
|
108
|
+
|
|
109
|
+
function PricingPage() {
|
|
110
|
+
const { trackEvent } = useAnalytics();
|
|
111
|
+
|
|
112
|
+
const handlePlanSelect = (plan: string) => {
|
|
113
|
+
trackEvent("plan_selected", { plan });
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<button onClick={() => handlePlanSelect("pro")}>
|
|
118
|
+
Select Pro Plan
|
|
119
|
+
</button>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The hook also auto-handles user identification and page views, so you can use it in any component that needs event tracking.
|
|
125
|
+
|
|
126
|
+
## Common Events to Track
|
|
127
|
+
|
|
128
|
+
| Event | When | Properties |
|
|
129
|
+
|-------|------|------------|
|
|
130
|
+
| `signup_completed` | After successful signup | `{ method: "email" }` |
|
|
131
|
+
| `plan_selected` | User clicks a pricing plan | `{ plan: "pro" }` |
|
|
132
|
+
| `feature_used` | User interacts with a feature | `{ feature: "export" }` |
|
|
133
|
+
| `error_occurred` | An error happens | `{ page: "/settings", type: "validation" }` |
|
|
134
|
+
| `onboarding_step` | User progresses in onboarding | `{ step: 3, total: 5 }` |
|
|
135
|
+
|
|
136
|
+
## PostHog Features
|
|
137
|
+
|
|
138
|
+
### Session Recording
|
|
139
|
+
|
|
140
|
+
Enable in PostHog > Project Settings > Session Recording. Records user sessions as videos for debugging and UX research.
|
|
141
|
+
|
|
142
|
+
### Feature Flags
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { getPostHog } from "@/lib/analytics";
|
|
146
|
+
|
|
147
|
+
const posthog = getPostHog();
|
|
148
|
+
if (posthog?.isFeatureEnabled("new-dashboard")) {
|
|
149
|
+
// Show new dashboard
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Surveys
|
|
154
|
+
|
|
155
|
+
Create in-app surveys directly from the PostHog dashboard. They appear automatically when conditions are met.
|
|
156
|
+
|
|
157
|
+
## GDPR Compliance
|
|
158
|
+
|
|
159
|
+
PostHog offers several privacy-friendly options:
|
|
160
|
+
|
|
161
|
+
1. **Cookie-less tracking**: PostHog can work without cookies using localStorage
|
|
162
|
+
2. **Consent management**: Disable autocapture until consent is given:
|
|
163
|
+
```typescript
|
|
164
|
+
// In analytics.ts, change persistence to "memory" and disable autocapture
|
|
165
|
+
// Then enable after consent:
|
|
166
|
+
posthog.opt_in_capturing();
|
|
167
|
+
```
|
|
168
|
+
3. **EU hosting**: Use `https://eu.i.posthog.com` for EU data residency
|
|
169
|
+
4. **Self-hosting**: Full data control with a self-hosted instance
|
|
170
|
+
|
|
171
|
+
If you have the GDPR sail installed, consider integrating analytics initialization with the consent tracking system.
|
|
172
|
+
|
|
173
|
+
## Disabling in Development
|
|
174
|
+
|
|
175
|
+
Analytics is automatically disabled when `VITE_POSTHOG_KEY` is not set. In local development, you can either:
|
|
176
|
+
|
|
177
|
+
- Leave `VITE_POSTHOG_KEY` empty in your `.env` (analytics disabled)
|
|
178
|
+
- Set it to your dev/staging PostHog project key (analytics enabled)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "analytics",
|
|
3
|
+
"displayName": "PostHog Analytics",
|
|
4
|
+
"description": "Privacy-friendly analytics with PostHog — auto page views, user identification, and custom event tracking",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"compatibility": ">=1.0.0",
|
|
7
|
+
"requiredEnvVars": [
|
|
8
|
+
{ "key": "VITE_POSTHOG_KEY", "description": "PostHog project API key" },
|
|
9
|
+
{ "key": "VITE_POSTHOG_HOST", "description": "PostHog instance URL (e.g., https://us.i.posthog.com)" }
|
|
10
|
+
],
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"backend": {},
|
|
13
|
+
"frontend": { "posthog-js": "^1.200.0" }
|
|
14
|
+
},
|
|
15
|
+
"modifies": {
|
|
16
|
+
"backend": [],
|
|
17
|
+
"frontend": ["src/App.tsx"]
|
|
18
|
+
},
|
|
19
|
+
"adds": {
|
|
20
|
+
"backend": [],
|
|
21
|
+
"frontend": [
|
|
22
|
+
"src/lib/analytics.ts",
|
|
23
|
+
"src/hooks/useAnalytics.ts",
|
|
24
|
+
"src/components/AnalyticsProvider.tsx"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useEffect, type ReactNode } from "react";
|
|
2
|
+
import { useLocation } from "react-router";
|
|
3
|
+
import { useAuth } from "@/hooks/useAuth.js";
|
|
4
|
+
import {
|
|
5
|
+
initAnalytics,
|
|
6
|
+
identify,
|
|
7
|
+
reset,
|
|
8
|
+
trackPageView,
|
|
9
|
+
} from "@/lib/analytics.js";
|
|
10
|
+
|
|
11
|
+
interface AnalyticsProviderProps {
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Provider component that initializes PostHog analytics and handles
|
|
17
|
+
* automatic user identification and page view tracking.
|
|
18
|
+
*
|
|
19
|
+
* Wrap your app (or router) with this component:
|
|
20
|
+
*
|
|
21
|
+
* <AnalyticsProvider>
|
|
22
|
+
* <AppRouter />
|
|
23
|
+
* </AnalyticsProvider>
|
|
24
|
+
*
|
|
25
|
+
* Behavior:
|
|
26
|
+
* - Initializes PostHog on mount
|
|
27
|
+
* - Identifies the user when they are logged in
|
|
28
|
+
* - Resets identity on logout
|
|
29
|
+
* - Tracks page views on every route change
|
|
30
|
+
*/
|
|
31
|
+
export function AnalyticsProvider({ children }: AnalyticsProviderProps) {
|
|
32
|
+
const { user, isAuthenticated } = useAuth();
|
|
33
|
+
const location = useLocation();
|
|
34
|
+
|
|
35
|
+
// Initialize PostHog on mount
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
initAnalytics();
|
|
38
|
+
}, []);
|
|
39
|
+
|
|
40
|
+
// Identify or reset user when auth state changes
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (isAuthenticated && user) {
|
|
43
|
+
identify(user.id, {
|
|
44
|
+
email: user.email,
|
|
45
|
+
name: user.name,
|
|
46
|
+
});
|
|
47
|
+
} else {
|
|
48
|
+
reset();
|
|
49
|
+
}
|
|
50
|
+
}, [isAuthenticated, user]);
|
|
51
|
+
|
|
52
|
+
// Track page views on route changes
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
trackPageView();
|
|
55
|
+
}, [location.pathname]);
|
|
56
|
+
|
|
57
|
+
return <>{children}</>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useEffect, useCallback } from "react";
|
|
2
|
+
import { useLocation } from "react-router";
|
|
3
|
+
import { useAuth } from "@/hooks/useAuth.js";
|
|
4
|
+
import { identify, reset, trackEvent, trackPageView } from "@/lib/analytics.js";
|
|
5
|
+
|
|
6
|
+
interface UseAnalyticsResult {
|
|
7
|
+
trackEvent: (name: string, properties?: Record<string, string | number | boolean>) => void;
|
|
8
|
+
identify: (userId: string, traits?: Record<string, string | number | boolean>) => void;
|
|
9
|
+
reset: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* React hook that integrates PostHog analytics with auth state and routing.
|
|
14
|
+
*
|
|
15
|
+
* - Auto-identifies the user when they log in (using useAuth)
|
|
16
|
+
* - Auto-resets identity when the user logs out
|
|
17
|
+
* - Auto-tracks page views on every route change (using useLocation)
|
|
18
|
+
* - Returns functions for manual event tracking
|
|
19
|
+
*/
|
|
20
|
+
export function useAnalytics(): UseAnalyticsResult {
|
|
21
|
+
const { user, isAuthenticated } = useAuth();
|
|
22
|
+
const location = useLocation();
|
|
23
|
+
|
|
24
|
+
// Auto-identify / reset when auth state changes
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (isAuthenticated && user) {
|
|
27
|
+
identify(user.id, {
|
|
28
|
+
email: user.email,
|
|
29
|
+
name: user.name,
|
|
30
|
+
});
|
|
31
|
+
} else {
|
|
32
|
+
reset();
|
|
33
|
+
}
|
|
34
|
+
}, [isAuthenticated, user]);
|
|
35
|
+
|
|
36
|
+
// Auto-track page views on route changes
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
trackPageView();
|
|
39
|
+
}, [location.pathname]);
|
|
40
|
+
|
|
41
|
+
const track = useCallback(
|
|
42
|
+
(name: string, properties?: Record<string, string | number | boolean>) => {
|
|
43
|
+
trackEvent(name, properties);
|
|
44
|
+
},
|
|
45
|
+
[],
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const identifyUser = useCallback(
|
|
49
|
+
(userId: string, traits?: Record<string, string | number | boolean>) => {
|
|
50
|
+
identify(userId, traits);
|
|
51
|
+
},
|
|
52
|
+
[],
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const resetIdentity = useCallback(() => {
|
|
56
|
+
reset();
|
|
57
|
+
}, []);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
trackEvent: track,
|
|
61
|
+
identify: identifyUser,
|
|
62
|
+
reset: resetIdentity,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import posthog from "posthog-js";
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// PostHog Analytics Service
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
const POSTHOG_KEY = import.meta.env.VITE_POSTHOG_KEY as string | undefined;
|
|
8
|
+
const POSTHOG_HOST = import.meta.env.VITE_POSTHOG_HOST as string | undefined;
|
|
9
|
+
|
|
10
|
+
/** Whether PostHog is configured and initialized. */
|
|
11
|
+
let isInitialized = false;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Initialize PostHog analytics.
|
|
15
|
+
*
|
|
16
|
+
* If VITE_POSTHOG_KEY is not set (common in local dev), all calls become
|
|
17
|
+
* no-ops so analytics never interfere with development.
|
|
18
|
+
*/
|
|
19
|
+
export function initAnalytics(): void {
|
|
20
|
+
if (isInitialized) return;
|
|
21
|
+
|
|
22
|
+
if (!POSTHOG_KEY) {
|
|
23
|
+
if (import.meta.env.DEV) {
|
|
24
|
+
console.log("[analytics] VITE_POSTHOG_KEY not set — analytics disabled in dev");
|
|
25
|
+
}
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
posthog.init(POSTHOG_KEY, {
|
|
30
|
+
api_host: POSTHOG_HOST || "https://us.i.posthog.com",
|
|
31
|
+
autocapture: true,
|
|
32
|
+
capture_pageview: false, // We handle page views manually for SPA routing
|
|
33
|
+
capture_pageleave: true,
|
|
34
|
+
persistence: "localStorage",
|
|
35
|
+
person_profiles: "identified_only",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
isInitialized = true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Identify a user after login. Associates all future events with this user.
|
|
43
|
+
*
|
|
44
|
+
* @param userId - The user's unique ID
|
|
45
|
+
* @param traits - Optional user properties (email, name, plan, etc.)
|
|
46
|
+
*/
|
|
47
|
+
export function identify(
|
|
48
|
+
userId: string,
|
|
49
|
+
traits?: Record<string, string | number | boolean>,
|
|
50
|
+
): void {
|
|
51
|
+
if (!isInitialized) return;
|
|
52
|
+
posthog.identify(userId, traits);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Reset analytics identity after logout.
|
|
57
|
+
* Creates a new anonymous ID for subsequent events.
|
|
58
|
+
*/
|
|
59
|
+
export function reset(): void {
|
|
60
|
+
if (!isInitialized) return;
|
|
61
|
+
posthog.reset();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Track a custom event.
|
|
66
|
+
*
|
|
67
|
+
* @param name - Event name (e.g., "subscription_started", "feature_used")
|
|
68
|
+
* @param properties - Optional event properties
|
|
69
|
+
*/
|
|
70
|
+
export function trackEvent(
|
|
71
|
+
name: string,
|
|
72
|
+
properties?: Record<string, string | number | boolean>,
|
|
73
|
+
): void {
|
|
74
|
+
if (!isInitialized) return;
|
|
75
|
+
posthog.capture(name, properties);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Track a page view. Call this on every route change in an SPA.
|
|
80
|
+
*/
|
|
81
|
+
export function trackPageView(): void {
|
|
82
|
+
if (!isInitialized) return;
|
|
83
|
+
posthog.capture("$pageview");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get the PostHog instance for advanced usage.
|
|
88
|
+
* Returns null if not initialized.
|
|
89
|
+
*/
|
|
90
|
+
export function getPostHog(): typeof posthog | null {
|
|
91
|
+
return isInitialized ? posthog : null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const analytics = {
|
|
95
|
+
init: initAnalytics,
|
|
96
|
+
identify,
|
|
97
|
+
reset,
|
|
98
|
+
trackEvent,
|
|
99
|
+
trackPageView,
|
|
100
|
+
getPostHog,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export default analytics;
|