Skip to content

Commit b355bb8

Browse files
authored
Merge pull request #397 from Zuehlke/feat/add-posthog
feat: add posthog
2 parents db0892a + 7429afd commit b355bb8

File tree

23 files changed

+1041
-160
lines changed

23 files changed

+1041
-160
lines changed

.cursor/rules/poinz.mdc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ alwaysApply: false
77

88
- we are still using javascript
99
- if you are working on a js file, do not create a ts file, do everything in the javascript file
10+
- if you are creating new files you must use typescript
1011

1112
- NEVER FINISH BEFORE YOU ARE NOT FINISHED, I DO NOT WANT TO HEAR "should I continue?"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ client/*.local
3030
webpack-build-stats.json
3131
deploy/
3232
npm_dependencies_report.*.md
33+
.env
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
description: apply when interacting with PostHog/analytics tasks
3+
globs:
4+
alwaysApply: true
5+
---
6+
7+
Never hallucinate an API key. Instead, always use the API key populated in the .env file.
8+
9+
# Feature flags
10+
11+
A given feature flag should be used in as few places as possible. Do not increase the risk of undefined behavior by scattering the same feature flag across multiple areas of code. If the same feature flag needs to be introduced at multiple callsites, flag this for the developer to inspect carefully.
12+
13+
If a job requires creating new feature flag names, make them as clear and descriptive as possible.
14+
15+
If using TypeScript, use an enum to store flag names. If using JavaScript, store flag names as strings to an object declared as a constant, to simulate an enum. Use a consistent naming convention for this storage. enum/const object members should be written UPPERCASE_WITH_UNDERSCORE.
16+
17+
Gate flag-dependent code on a check that verifies the flag's values are valid and expected.
18+
19+
# Custom properties
20+
21+
If a custom property for a person or event is at any point referenced in two or more files or two or more callsites in the same file, use an enum or const object, as above in feature flags.
22+
23+
# Naming
24+
25+
Before creating any new event or property names, consult with the developer for any existing naming convention. Consistency in naming is essential, and additional context may exist outside this project. Similarly, be careful about any changes to existing event and property names, as this may break reporting and distort data for the project.
26+
27+

client/package-lock.json

Lines changed: 238 additions & 102 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"history": "5.3.0",
2626
"loglevel": "1.8.1",
2727
"nanoid": "5.0.1",
28+
"posthog-js": "^1.245.1",
2829
"purecss": "3.0.0",
2930
"react": "^18.3.1",
3031
"react-dnd": "^16.0.1",
@@ -46,17 +47,19 @@
4647
"@babel/core": "7.23.2",
4748
"@babel/preset-env": "7.23.2",
4849
"@babel/preset-react": "7.22.15",
49-
"babel-jest": "29.7.0",
50-
"jest": "29.7.0",
50+
"@babel/preset-typescript": "^7.27.1",
5151
"@eslint/js": "^9.17.0",
5252
"@quickbaseoss/babel-plugin-styled-components-css-namespace": "1.0.1",
5353
"@types/react": "^18.3.18",
5454
"@types/react-dom": "^18.3.5",
5555
"@vitejs/plugin-react": "^4.3.4",
56+
"babel-jest": "29.7.0",
5657
"eslint": "^9.17.0",
5758
"eslint-plugin-react-hooks": "^5.0.0",
5859
"eslint-plugin-react-refresh": "^0.4.16",
5960
"globals": "^15.14.0",
61+
"jest": "29.7.0",
62+
"react-test-renderer": "^18.3.1",
6063
"typescript": "~5.6.2",
6164
"typescript-eslint": "^8.18.2",
6265
"vite": "^6.0.5"
@@ -73,17 +76,25 @@
7376
},
7477
"jest": {
7578
"transform": {
76-
"^.+\\.[jt]sx?$": ["babel-jest", { "configFile": "./test/.babelrc" }]
79+
"^.+\\.[jt]sx?$": [
80+
"babel-jest",
81+
{
82+
"configFile": "./test/.babelrc"
83+
}
84+
]
7785
},
7886
"testMatch": [
79-
"**/test/unit/**/*Test.js",
80-
"**/test/integration/**/*Test.js"
87+
"**/test/unit/**/*Test.[jt]s?(x)",
88+
"**/test/integration/**/*Test.[jt]s?(x)"
8189
],
8290
"setupFilesAfterEnv": [
8391
"<rootDir>/test/unit/setup.js"
8492
],
8593
"transformIgnorePatterns": [
8694
"/node_modules/(?!nanoid).+\\.js$"
87-
]
95+
],
96+
"moduleNameMapper": {
97+
"^.+\\.(css|less|scss)$": "<rootDir>/test/styleMock.js"
98+
}
8899
}
89100
}

client/src/App.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,29 @@ import configureStore from './state/configureStore';
1212
import {WithL10n} from './services/l10n';
1313
import ErrorBoundary from './components/common/ErrorBoundary';
1414
import Main from './components/Main';
15+
import {usePostHogIdentify} from './hooks/usePostHogIdentify';
1516

1617
import GlobalStyle from './_styled';
1718

1819
log.setLevel(appConfig.env === 'dev' ? 'debug' : 'error');
1920
const store = configureStore(initialState());
2021

22+
function AppContent() {
23+
usePostHogIdentify();
24+
return (
25+
<>
26+
<GlobalStyle />
27+
<Main />
28+
</>
29+
);
30+
}
31+
2132
function App() {
2233
return (
2334
<ErrorBoundary>
2435
<Provider store={store}>
2536
<WithL10n>
26-
<GlobalStyle />
27-
<Main />
37+
<AppContent />
2838
</WithL10n>
2939
</Provider>
3040
</ErrorBoundary>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {useEffect} from 'react';
2+
import {useSelector} from 'react-redux';
3+
import posthog from 'posthog-js';
4+
5+
interface RootState {
6+
users: {
7+
ownUserId: string | undefined;
8+
};
9+
}
10+
11+
export function usePostHogIdentify() {
12+
const userId = useSelector((state: RootState) => state.users.ownUserId);
13+
14+
useEffect(() => {
15+
if (userId) {
16+
posthog.identify(userId);
17+
}
18+
}, [userId]);
19+
}

client/src/main.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import {StrictMode} from 'react';
22
import {createRoot} from 'react-dom/client';
3+
import {PostHogProvider} from 'posthog-js/react';
34
import App from './App.tsx';
45

56
createRoot(document.getElementById('root')!).render(
67
<StrictMode>
7-
<App />
8+
<PostHogProvider
9+
apiKey={import.meta.env.VITE_PUBLIC_POSTHOG_KEY}
10+
options={{
11+
api_host: import.meta.env.VITE_PUBLIC_POSTHOG_HOST,
12+
debug: import.meta.env.MODE === "development",
13+
}}
14+
>
15+
<App />
16+
</PostHogProvider>
817
</StrictMode>
918
);

client/src/services/tracking.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import posthog from 'posthog-js';
2+
import {
3+
RoomSettingsChangedEvent,
4+
StoryCreatedEvent,
5+
StoryEditedEvent,
6+
StoryTrashedEvent,
7+
StoryRestoredEvent,
8+
MatrixViewToggledEvent,
9+
RoomEvent
10+
} from './tracking.types';
11+
12+
export const trackRoomSettingsChanged = (data: RoomSettingsChangedEvent) => {
13+
posthog.capture('room_settings_changed', data);
14+
};
15+
16+
export const trackStoryCreated = (data: StoryCreatedEvent) => {
17+
posthog.capture('story_created', data);
18+
};
19+
20+
export const trackStoryEdited = (data: StoryEditedEvent) => {
21+
posthog.capture('story_edited', data);
22+
};
23+
24+
export const trackStoryTrashed = (data: StoryTrashedEvent) => {
25+
posthog.capture('story_trashed', data);
26+
};
27+
28+
export const trackStoryRestored = (data: StoryRestoredEvent) => {
29+
posthog.capture('story_restored', data);
30+
};
31+
32+
export const trackMatrixViewToggled = (data: MatrixViewToggledEvent) => {
33+
posthog.capture('matrix_view_toggled', data);
34+
};
35+
36+
export const trackNewEstimationRound = (data: RoomEvent) => {
37+
posthog.capture('new_estimation_round', data);
38+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
export interface RoomEvent {
2+
roomId: string;
3+
}
4+
5+
export interface RoomSettingsChangedEvent extends RoomEvent {
6+
setting: 'confidence' | 'autoReveal' | 'password' | 'issueTracking';
7+
newValue: boolean | string;
8+
}
9+
10+
export interface StoryEvent extends RoomEvent {
11+
storyId: string;
12+
}
13+
14+
export interface StoryCreatedEvent extends StoryEvent {
15+
hasDescription: boolean;
16+
descriptionLength: number;
17+
}
18+
19+
export interface StoryEditedEvent extends StoryEvent {
20+
fieldChanged: 'title' | 'description' | 'both';
21+
}
22+
23+
export interface StoryTrashedEvent extends StoryEvent {
24+
hadConsensus: boolean;
25+
storyLifetimeMinutes: number;
26+
}
27+
28+
export interface StoryRestoredEvent extends StoryEvent {
29+
timeInTrashMinutes: number;
30+
}
31+
32+
export interface MatrixViewToggledEvent extends RoomEvent {
33+
totalStories: number;
34+
}

0 commit comments

Comments
 (0)