feat: wire PostHog analytics
Some checks failed
Stuffle/agos-client/pipeline/head There was a failure building this commit
Some checks failed
Stuffle/agos-client/pipeline/head There was a failure building this commit
- Add posthog-js to package.json dependencies - Add VITE_POSTHOG_KEY + VITE_POSTHOG_HOST to .env.production - Create src/lib/posthog.ts: init, identifyUser, resetIdentity helpers - Call initPostHog() at app startup in main.tsx - Identify user on auth state change, reset on logout in useAuth.ts
This commit is contained in:
21
.devin/skills/noterm/SKILL.md
Normal file
21
.devin/skills/noterm/SKILL.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# /noterm — Prevent Session Termination
|
||||
|
||||
**Trigger**: User types `/noterm` in any message.
|
||||
|
||||
## Behavior
|
||||
|
||||
When this skill is invoked, reinforce the following rule for the remainder of the session:
|
||||
|
||||
1. **NEVER** terminate or end the session on your own.
|
||||
2. After completing any task, use the `ask_user_question` tool to confirm whether the user considers the work done.
|
||||
3. Do **not** produce a final summary and stop — always ask for next steps.
|
||||
4. If you are unsure whether the user wants to continue, ask.
|
||||
5. Treat every completed task as potentially having follow-up work.
|
||||
|
||||
## Response
|
||||
|
||||
After invoking this skill, acknowledge briefly:
|
||||
|
||||
> Session termination guard is active. I will not end this session until you explicitly confirm we're done.
|
||||
|
||||
Then continue with whatever the user asked in the same message (if anything).
|
||||
@@ -46,3 +46,9 @@ VITE_IAM_DESKTOP_REDIRECT_URI=app://bundle/callback
|
||||
|
||||
# Google Identity Services (optional — enables native one-tap sign-in)
|
||||
VITE_GOOGLE_CLIENT_ID=your-google-oauth2-client-id.apps.googleusercontent.com
|
||||
|
||||
# ── PostHog Analytics ──────────────────────────────────────────────────────────
|
||||
# Public project token (safe to commit) — from analytics.armco.dev
|
||||
VITE_POSTHOG_KEY=phc_your_project_token
|
||||
# Self-hosted PostHog host
|
||||
VITE_POSTHOG_HOST=https://analytics.armco.dev
|
||||
|
||||
@@ -5,3 +5,7 @@ VITE_IAM_ISSUER=https://iam.armco.dev
|
||||
VITE_IAM_AUDIENCE=stuffle-api
|
||||
VITE_IAM_DESKTOP_CLIENT_ID=client_6a243d0c127843ff8f2f2a28a486638f
|
||||
VITE_IAM_DESKTOP_REDIRECT_URI=app://bundle/callback
|
||||
|
||||
# ── PostHog Analytics ──────────────────────────────────────────────────────────
|
||||
VITE_POSTHOG_KEY=phc_xNzMtXgsxmu74yTncZZuEJMACvdAjSfV3jMKaWAGvj5v
|
||||
VITE_POSTHOG_HOST=https://analytics.armco.dev
|
||||
|
||||
19
.windsurf/workflows/noterm.md
Normal file
19
.windsurf/workflows/noterm.md
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
description: Prevent session termination - keep working until user confirms done
|
||||
---
|
||||
|
||||
## /noterm — Session Termination Guard
|
||||
|
||||
When invoked, enforce the following for the remainder of the session:
|
||||
|
||||
1. NEVER terminate or end the session on your own.
|
||||
2. After completing any task, ask the user whether they consider the work done.
|
||||
3. Do not produce a final summary and stop — always ask for next steps.
|
||||
4. If unsure whether the user wants to continue, ask.
|
||||
5. Treat every completed task as potentially having follow-up work.
|
||||
|
||||
Acknowledge briefly:
|
||||
|
||||
> Session termination guard is active. I will not end this session until you explicitly confirm we're done.
|
||||
|
||||
Then continue with whatever the user asked in the same message (if anything).
|
||||
@@ -55,6 +55,7 @@
|
||||
"lowlight": "^3.3.0",
|
||||
"lucide-react": "^0.577.0",
|
||||
"marked": "^17.0.6",
|
||||
"posthog-js": "^1.250.0",
|
||||
"prosemirror-search": "^1.1.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
|
||||
@@ -16,6 +16,7 @@ import { createContext, createElement, useContext, useEffect, useMemo, useState,
|
||||
import type { User } from '@armco/iam-client'
|
||||
import { iam } from './iam'
|
||||
import { shutdown as wsShutdown } from '@/services/ws.service'
|
||||
import { identifyUser, resetIdentity } from '@/lib/posthog'
|
||||
import { DEV_BYPASS_ACTIVE } from './dev-bypass' // re-exported below for consumers
|
||||
import { installAuthDebug } from './authDebug'
|
||||
|
||||
@@ -138,6 +139,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const nextState = await resolveAuthState(authState.isAuthenticated)
|
||||
if (cancelled) return
|
||||
setState(nextState)
|
||||
if (nextState.user) {
|
||||
identifyUser(nextState.user.sub, {
|
||||
email: nextState.user.email,
|
||||
name: nextState.user.name,
|
||||
})
|
||||
}
|
||||
})()
|
||||
})
|
||||
|
||||
@@ -173,6 +180,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
...state,
|
||||
logout: () => {
|
||||
if (DEV_BYPASS_ACTIVE) return
|
||||
resetIdentity()
|
||||
wsShutdown()
|
||||
iam.logout({ returnTo: window.location.origin })
|
||||
},
|
||||
|
||||
37
src/lib/posthog.ts
Normal file
37
src/lib/posthog.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import posthog from 'posthog-js'
|
||||
|
||||
const POSTHOG_KEY = import.meta.env.VITE_POSTHOG_KEY as string | undefined
|
||||
const POSTHOG_HOST = import.meta.env.VITE_POSTHOG_HOST as string | undefined
|
||||
|
||||
let _initialised = false
|
||||
|
||||
export function initPostHog(): void {
|
||||
if (_initialised || !POSTHOG_KEY || !POSTHOG_HOST) return
|
||||
_initialised = true
|
||||
posthog.init(POSTHOG_KEY, {
|
||||
api_host: POSTHOG_HOST,
|
||||
ui_host: POSTHOG_HOST,
|
||||
person_profiles: 'identified_only',
|
||||
capture_pageview: true,
|
||||
capture_pageleave: true,
|
||||
session_recording: {
|
||||
maskAllInputs: false,
|
||||
maskInputOptions: { password: true },
|
||||
},
|
||||
loaded: (ph) => {
|
||||
if (import.meta.env.DEV) ph.debug()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function identifyUser(userId: string, traits?: Record<string, unknown>): void {
|
||||
if (!_initialised) return
|
||||
posthog.identify(userId, traits)
|
||||
}
|
||||
|
||||
export function resetIdentity(): void {
|
||||
if (!_initialised) return
|
||||
posthog.reset()
|
||||
}
|
||||
|
||||
export { posthog }
|
||||
@@ -4,9 +4,12 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { ThemeProvider } from '@/theme'
|
||||
import { ShortlistProvider } from '@/context/ShortlistContext'
|
||||
import { AuthProvider } from '@/auth/useAuth'
|
||||
import { initPostHog } from '@/lib/posthog'
|
||||
import './index.css'
|
||||
import App from './App'
|
||||
|
||||
initPostHog()
|
||||
|
||||
/* ── Global scroll-reveal scrollbar ────────────────────────────────────────
|
||||
Adds `.ag-scrolling` to any element while it scrolls, removes after 2 s
|
||||
of idle. The CSS in index.css transitions the scrollbar thumb via this
|
||||
|
||||
Reference in New Issue
Block a user