Skip to content

Commit 0106db2

Browse files
committed
feat: add analytics tracking for repo scan and wizard interactions
1 parent 633e1e3 commit 0106db2

File tree

6 files changed

+94
-12
lines changed

6 files changed

+94
-12
lines changed

app/existing/[repoUrl]/repo-scan-client.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { generateFromRepoScan } from "@/lib/scan-generate"
1414
import FinalOutputView from "@/components/final-output-view"
1515
import RepoScanLoader from "@/components/repo-scan-loader"
1616
import type { GeneratedFileResult } from "@/types/output"
17+
import { track } from "@/lib/mixpanel"
18+
import { ANALYTICS_EVENTS } from "@/lib/analytics-events"
1719

1820
const buildQuery = (url: string) => `/api/scan-repo?url=${encodeURIComponent(url)}`
1921
const CONVENTIONS_DOC_URL =
@@ -110,23 +112,27 @@ export default function RepoScanClient({ initialRepoUrl }: RepoScanClientProps)
110112
}))
111113
}, [scanResult])
112114

113-
const handleStartScan = () => {
114-
if (!repoUrlForScan) {
115-
return
116-
}
117-
118-
setHasConfirmed(true)
119-
setScanToken((token) => token + 1)
115+
const handleStartScan = () => {
116+
if (!repoUrlForScan) {
117+
return
120118
}
121119

122-
const handleRetryScan = () => {
123-
if (!repoUrlForScan) {
124-
return
125-
}
120+
setHasConfirmed(true)
121+
setScanToken((token) => token + 1)
122+
123+
track(ANALYTICS_EVENTS.REPO_SCAN_START, { repo: repoUrlForScan })
124+
}
126125

127-
setScanToken((token) => token + 1)
126+
const handleRetryScan = () => {
127+
if (!repoUrlForScan) {
128+
return
128129
}
129130

131+
setScanToken((token) => token + 1)
132+
133+
track(ANALYTICS_EVENTS.REPO_SCAN_RETRY, { repo: repoUrlForScan })
134+
}
135+
130136
const warnings = scanResult?.warnings ?? []
131137
const stackMeta = scanResult?.conventions ?? null
132138
const detectedStackId = stackMeta?.stack ?? null

app/existing/existing-repo-entry-client.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { Button } from "@/components/ui/button"
88
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
99
import { Input } from "@/components/ui/input"
1010
import { normalizeGitHubRepoInput } from "@/lib/github"
11+
import { track } from "@/lib/mixpanel"
12+
import { ANALYTICS_EVENTS } from "@/lib/analytics-events"
1113

1214
export function ExistingRepoEntryClient() {
1315
const router = useRouter()
@@ -30,6 +32,11 @@ export function ExistingRepoEntryClient() {
3032

3133
const encoded = encodeURIComponent(normalized)
3234

35+
track(ANALYTICS_EVENTS.REPO_ANALYZE_SUBMIT, {
36+
inputProvided: value.length > 0,
37+
url: normalized,
38+
})
39+
3340
router.push(`/existing/${encoded}`)
3441
}
3542

app/new/stack/stack-summary-page.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import type {
2626
} from "@/types/wizard"
2727
import type { GeneratedFileResult } from "@/types/output"
2828
import { WizardEditAnswerDialog } from "@/components/wizard-edit-answer-dialog"
29+
import { track } from "@/lib/mixpanel"
30+
import { ANALYTICS_EVENTS } from "@/lib/analytics-events"
2931

3032
const fileOptions = getFileOptions()
3133
const fileSummaryQuestion = getFileSummaryQuestion()
@@ -210,6 +212,7 @@ export function StackSummaryPage({ stackId, mode }: StackSummaryPageProps) {
210212
}, [wizardSteps])
211213

212214
const handleEditClick = (questionId: string) => {
215+
track(ANALYTICS_EVENTS.SUMMARY_EDIT_OPEN, { questionId })
213216
setEditingQuestionId(questionId)
214217
}
215218

@@ -226,6 +229,7 @@ export function StackSummaryPage({ stackId, mode }: StackSummaryPageProps) {
226229
const trimmed = submittedValue.trim()
227230
const nextFreeText: FreeTextResponses = (() => {
228231
if (trimmed.length === 0) {
232+
track(ANALYTICS_EVENTS.SUMMARY_EDIT_FREE_TEXT_CLEARED, { questionId: question.id })
229233
if (!(question.id in freeTextResponses)) {
230234
return { ...freeTextResponses }
231235
}
@@ -235,6 +239,10 @@ export function StackSummaryPage({ stackId, mode }: StackSummaryPageProps) {
235239
return next
236240
}
237241

242+
track(ANALYTICS_EVENTS.SUMMARY_EDIT_FREE_TEXT_SAVED, {
243+
questionId: question.id,
244+
length: trimmed.length,
245+
})
238246
return {
239247
...freeTextResponses,
240248
[question.id]: trimmed,
@@ -314,6 +322,13 @@ export function StackSummaryPage({ stackId, mode }: StackSummaryPageProps) {
314322
})
315323
}
316324

325+
track(ANALYTICS_EVENTS.SUMMARY_EDIT_ANSWER_SELECTED, {
326+
questionId: question.id,
327+
answerValue: answer.value,
328+
answerLabel: answer.label,
329+
allowMultiple: question.allowMultiple ?? false,
330+
})
331+
317332
if (!question.allowMultiple) {
318333
handleCloseEdit()
319334
}

components/instructions-wizard.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
import { persistWizardState, clearWizardState } from "@/lib/wizard-storage"
2929
import { WizardAnswerGrid } from "./wizard-answer-grid"
3030
import { WizardConfirmationDialog } from "./wizard-confirmation-dialog"
31+
import { track } from "@/lib/mixpanel"
32+
import { ANALYTICS_EVENTS } from "@/lib/analytics-events"
3133

3234
const suffixSteps = getSuffixSteps()
3335
const buildStepSignature = (step: WizardStep) => `${step.id}::${step.questions.map((question) => question.id).join("|")}`
@@ -559,6 +561,16 @@ export function InstructionsWizard({
559561
return
560562
}
561563

564+
// Track answer selection
565+
track(ANALYTICS_EVENTS.WIZARD_ANSWER_SELECTED, {
566+
questionId: currentQuestion.id,
567+
answerValue: answer.value,
568+
answerLabel: answer.label,
569+
allowMultiple: currentQuestion.allowMultiple ?? false,
570+
isDefault: Boolean(answer.isDefault),
571+
stepId: currentStep?.id ?? null,
572+
})
573+
562574
void handleQuestionAnswerSelection(currentQuestion, answer)
563575
}
564576

@@ -704,6 +716,11 @@ export function InstructionsWizard({
704716
return
705717
}
706718

719+
track(ANALYTICS_EVENTS.WIZARD_FREE_TEXT_SAVED, {
720+
questionId: currentQuestion.id,
721+
length: currentFreeTextValue.trim().length,
722+
})
723+
707724
commitFreeTextValue(currentQuestion, currentFreeTextValue)
708725
}
709726

@@ -712,6 +729,10 @@ export function InstructionsWizard({
712729
return
713730
}
714731

732+
track(ANALYTICS_EVENTS.WIZARD_FREE_TEXT_CLEARED, {
733+
questionId: currentQuestion.id,
734+
})
735+
715736
commitFreeTextValue(currentQuestion, "", { allowAutoAdvance: false })
716737
}
717738

@@ -733,6 +754,14 @@ export function InstructionsWizard({
733754

734755
const isStackQuestion = currentQuestion.id === STACK_QUESTION_ID
735756

757+
// Track default use
758+
track(ANALYTICS_EVENTS.WIZARD_USE_DEFAULT, {
759+
questionId: currentQuestion.id,
760+
answerValue: defaultAnswer.value,
761+
answerLabel: defaultAnswer.label,
762+
isStackQuestion,
763+
})
764+
736765
if (isStackQuestion) {
737766
await loadStackQuestions(defaultAnswer.value, defaultAnswer.label, {
738767
skipFastTrackPrompt: autoStartAfterStackSelection,
@@ -769,6 +798,7 @@ export function InstructionsWizard({
769798
}
770799

771800
const requestResetWizard = () => {
801+
track(ANALYTICS_EVENTS.WIZARD_RESET)
772802
setPendingConfirmation("reset")
773803
}
774804

lib/analytics-events.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
export const ANALYTICS_EVENTS = {
22
PAGE_VIEW: "Page View",
33
CREATE_INSTRUCTIONS_FILE: "Create My Instructions File",
4+
// Wizard interactions
5+
WIZARD_ANSWER_SELECTED: "Wizard Answer Selected",
6+
WIZARD_USE_DEFAULT: "Wizard Use Default",
7+
WIZARD_FREE_TEXT_SAVED: "Wizard Free Text Saved",
8+
WIZARD_FREE_TEXT_CLEARED: "Wizard Free Text Cleared",
9+
WIZARD_RESET: "Wizard Reset",
10+
// Summary dialog interactions
11+
SUMMARY_EDIT_OPEN: "Summary Edit Open",
12+
SUMMARY_EDIT_ANSWER_SELECTED: "Summary Edit Answer Selected",
13+
SUMMARY_EDIT_FREE_TEXT_SAVED: "Summary Edit Free Text Saved",
14+
SUMMARY_EDIT_FREE_TEXT_CLEARED: "Summary Edit Free Text Cleared",
15+
// Existing repo / scan interactions
16+
REPO_ANALYZE_SUBMIT: "Existing Repo Analyze Submit",
17+
REPO_SCAN_START: "Repo Scan Start",
18+
REPO_SCAN_RETRY: "Repo Scan Retry",
19+
REPO_SCAN_GENERATE_FILE: "Repo Scan Generate File",
420
} as const
521

622
export type AnalyticsEvent =

lib/scan-generate.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { getFileOptions } from "@/lib/wizard-config"
44
import { getMimeTypeForFormat } from "@/lib/wizard-utils"
55
import type { RepoScanSummary } from "@/types/repo-scan"
66
import type { GeneratedFileResult } from "@/types/output"
7+
import { track } from "@/lib/mixpanel"
8+
import { ANALYTICS_EVENTS } from "@/lib/analytics-events"
79

810
const fileOptions = getFileOptions()
911

@@ -15,6 +17,12 @@ export async function generateFromRepoScan(
1517
): Promise<GeneratedFileResult | null> {
1618
const selected = fileOptions.find((f) => f.id === outputFileId) || null
1719

20+
track(ANALYTICS_EVENTS.REPO_SCAN_GENERATE_FILE, {
21+
outputFile: outputFileId,
22+
stack: scan.conventions?.stack ?? null,
23+
language: scan.language ?? null,
24+
})
25+
1826
const res = await fetch(`/api/scan-generate/${encodeURIComponent(outputFileId)}`, {
1927
method: "POST",
2028
headers: {

0 commit comments

Comments
 (0)