Skip to content

Commit 135c88a

Browse files
feat(analytics): enhance Plausible tracking with journey context and new events
- Add page property to copy_code and download_image for user journey tracking (home, spec_overview, spec_detail) - Add toggle_grid_size event for view preference tracking - Add filter_remove event for filter removal tracking - Add spec property to SpecTabs copy_code (was missing) - Reduce debounce times: pageview 300ms→150ms, search_no_results 500ms→200ms - Move and update plausible.md documentation with Plausible dashboard setup guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 07b7b4b commit 135c88a

8 files changed

Lines changed: 387 additions & 428 deletions

File tree

PLAUSIBLE.md

Lines changed: 0 additions & 412 deletions
This file was deleted.

app/src/components/FilterBar.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ export function FilterBar({
221221
const timer = setTimeout(() => {
222222
onTrackEvent('search_no_results', { query });
223223
lastTrackedQueryRef.current = query;
224-
}, 500);
224+
}, 200);
225225
return () => clearTimeout(timer);
226226
}
227227
}, [searchQuery, searchResults.length, onTrackEvent]);
@@ -378,7 +378,11 @@ export function FilterBar({
378378
{/* Grid size toggle */}
379379
<Tooltip title={imageSize === 'normal' ? 'compact view' : 'normal view'}>
380380
<Box
381-
onClick={() => onImageSizeChange(imageSize === 'normal' ? 'compact' : 'normal')}
381+
onClick={() => {
382+
const newSize = imageSize === 'normal' ? 'compact' : 'normal';
383+
onImageSizeChange(newSize);
384+
onTrackEvent('toggle_grid_size', { size: newSize });
385+
}}
382386
sx={{
383387
display: 'flex',
384388
alignItems: 'center',
@@ -588,7 +592,11 @@ export function FilterBar({
588592
{/* Grid size toggle */}
589593
<Tooltip title={imageSize === 'normal' ? 'compact view' : 'normal view'}>
590594
<Box
591-
onClick={() => onImageSizeChange(imageSize === 'normal' ? 'compact' : 'normal')}
595+
onClick={() => {
596+
const newSize = imageSize === 'normal' ? 'compact' : 'normal';
597+
onImageSizeChange(newSize);
598+
onTrackEvent('toggle_grid_size', { size: newSize });
599+
}}
592600
sx={{
593601
display: 'flex',
594602
alignItems: 'center',

app/src/components/ImageCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export const ImageCard = memo(function ImageCard({
8989
if (code) {
9090
await navigator.clipboard.writeText(code);
9191
setCopyState('copied');
92-
onTrackEvent?.('copy_code', { spec: image.spec_id, library: image.library, method: 'card' });
92+
onTrackEvent?.('copy_code', { spec: image.spec_id, library: image.library, method: 'card', page: 'home' });
9393
setTimeout(() => setCopyState('idle'), 2000);
9494
} else {
9595
setCopyState('idle');

app/src/components/SpecTabs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export function SpecTabs({
164164
try {
165165
await navigator.clipboard.writeText(code);
166166
setCopied(true);
167-
onTrackEvent?.('copy_code', { library: libraryId, method: 'tab' });
167+
onTrackEvent?.('copy_code', { spec: specId, library: libraryId, method: 'tab', page: 'spec_detail' });
168168
setTimeout(() => setCopied(false), 2000);
169169
} catch (err) {
170170
console.error('Copy failed:', err);

app/src/hooks/useAnalytics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export function useAnalytics() {
6464
[isProduction]
6565
);
6666

67-
const trackPageview = useMemo(() => debounce(sendPageview, 300), [sendPageview]);
67+
const trackPageview = useMemo(() => debounce(sendPageview, 150), [sendPageview]);
6868

6969
const trackEvent = useCallback(
7070
(name: string, props?: EventProps) => {

app/src/hooks/useFilterState.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,24 +210,32 @@ export function useFilterState({
210210

211211
// Remove a filter value from a specific group
212212
const handleRemoveFilter = useCallback((groupIndex: number, value: string) => {
213+
const group = activeFiltersRef.current[groupIndex];
214+
if (group) {
215+
onTrackEvent('filter_remove', { category: group.category, value });
216+
}
213217
setActiveFilters((prev) => {
214218
const newFilters = [...prev];
215-
const group = newFilters[groupIndex];
216-
if (!group) return prev;
219+
const grp = newFilters[groupIndex];
220+
if (!grp) return prev;
217221

218-
const updatedValues = group.values.filter((v) => v !== value);
222+
const updatedValues = grp.values.filter((v) => v !== value);
219223
if (updatedValues.length === 0) {
220224
return newFilters.filter((_, i) => i !== groupIndex);
221225
}
222-
newFilters[groupIndex] = { ...group, values: updatedValues };
226+
newFilters[groupIndex] = { ...grp, values: updatedValues };
223227
return newFilters;
224228
});
225-
}, []);
229+
}, [onTrackEvent]);
226230

227231
// Remove entire group by index
228232
const handleRemoveGroup = useCallback((groupIndex: number) => {
233+
const group = activeFiltersRef.current[groupIndex];
234+
if (group) {
235+
onTrackEvent('filter_remove', { category: group.category, value: group.values.join(',') });
236+
}
229237
setActiveFilters((prev) => prev.filter((_, i) => i !== groupIndex));
230-
}, []);
238+
}, [onTrackEvent]);
231239

232240
// Random filter - replaces last filter slot (or adds first one)
233241
const handleRandom = useCallback(

app/src/pages/SpecPage.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,9 @@ export function SpecPage() {
156156
link.href = impl.preview_url;
157157
link.download = `${specId}-${impl.library_id}.png`;
158158
link.click();
159-
trackEvent('download_image', { spec: specId, library: impl.library_id });
159+
trackEvent('download_image', { spec: specId, library: impl.library_id, page: isOverviewMode ? 'spec_overview' : 'spec_detail' });
160160
},
161-
[specId, trackEvent]
161+
[specId, trackEvent, isOverviewMode]
162162
);
163163

164164
// Handle copy code (works for both overview and detail mode)
@@ -168,13 +168,13 @@ export function SpecPage() {
168168
try {
169169
await navigator.clipboard.writeText(impl.code);
170170
setCodeCopied(impl.library_id);
171-
trackEvent('copy_code', { spec: specId, library: impl.library_id, method: 'image' });
171+
trackEvent('copy_code', { spec: specId, library: impl.library_id, method: 'image', page: isOverviewMode ? 'spec_overview' : 'spec_detail' });
172172
setTimeout(() => setCodeCopied(null), 2000);
173173
} catch (err) {
174174
console.error('Copy failed:', err);
175175
}
176176
},
177-
[specId, trackEvent]
177+
[specId, trackEvent, isOverviewMode]
178178
);
179179

180180
// Track page view

0 commit comments

Comments
 (0)