Skip to content

Commit 80d5f23

Browse files
committed
Better analytics
1 parent 8907703 commit 80d5f23

File tree

10 files changed

+242
-8
lines changed

10 files changed

+242
-8
lines changed

package-lock.json

+120
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/groups/[groupId]/balances/page.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BalancesList } from '@/app/groups/[groupId]/balances-list'
22
import { ReimbursementList } from '@/app/groups/[groupId]/reimbursement-list'
3+
import { TrackPage } from '@/components/track-page'
34
import {
45
Card,
56
CardContent,
@@ -30,6 +31,7 @@ export default async function GroupPage({
3031

3132
return (
3233
<>
34+
<TrackPage path={`/groups/${group.id}/balances`} />
3335
<Card className="mb-4">
3436
<CardHeader>
3537
<CardTitle>Balances</CardTitle>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use client'
2+
3+
import { useAnalytics } from '@/components/track-page'
4+
import Link from 'next/link'
5+
import { PropsWithChildren } from 'react'
6+
7+
export const ExportLink = function ExportLink({
8+
groupId,
9+
children,
10+
className,
11+
}: PropsWithChildren<{ groupId: string; className?: string }>) {
12+
const sendEvent = useAnalytics()
13+
14+
return (
15+
<Link
16+
className={className}
17+
href={`/groups/${groupId}/expenses/export/json`}
18+
target="_blank"
19+
onClick={() => {
20+
sendEvent(
21+
{ event: 'group: export expenses', props: { groupId } },
22+
`/groups/${groupId}/expenses`,
23+
)
24+
}}
25+
>
26+
{children}
27+
</Link>
28+
)
29+
}

src/app/groups/[groupId]/expenses/page.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { ActiveUserModal } from '@/app/groups/[groupId]/expenses/active-user-modal'
22
import { ExpenseList } from '@/app/groups/[groupId]/expenses/expense-list'
3+
import { ExportLink } from '@/app/groups/[groupId]/expenses/export-link'
4+
import { TrackPage } from '@/components/track-page'
35
import { Button } from '@/components/ui/button'
46
import {
57
Card,
@@ -30,6 +32,7 @@ export default async function GroupExpensesPage({
3032

3133
return (
3234
<>
35+
<TrackPage path={`/groups/${group.id}/expenses`} />
3336
<Card className="mb-4 rounded-none -mx-4 border-x-0 sm:border-x sm:rounded-lg sm:mx-0">
3437
<div className="flex flex-1">
3538
<CardHeader className="flex-1 p-4 sm:p-6">
@@ -40,12 +43,9 @@ export default async function GroupExpensesPage({
4043
</CardHeader>
4144
<CardHeader className="p-4 sm:p-6 flex flex-row space-y-0 gap-2">
4245
<Button variant="secondary" size="icon" asChild>
43-
<Link
44-
href={`/groups/${groupId}/expenses/export/json`}
45-
target="_blank"
46-
>
46+
<ExportLink groupId={groupId}>
4747
<Download className="w-4 h-4" />
48-
</Link>
48+
</ExportLink>
4949
</Button>
5050
<Button asChild size="icon">
5151
<Link href={`/groups/${groupId}/expenses/create`}>

src/app/groups/page.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { RecentGroupList } from '@/app/groups/recent-group-list'
2+
import { TrackPage } from '@/components/track-page'
23
import { Button } from '@/components/ui/button'
34
import { Plus } from 'lucide-react'
45
import { Metadata } from 'next'
@@ -11,6 +12,7 @@ export const metadata: Metadata = {
1112
export default async function GroupsPage() {
1213
return (
1314
<>
15+
<TrackPage path="/groups" />
1416
<div className="flex justify-between items-center gap-4">
1517
<h1 className="font-bold text-2xl">
1618
<Link href="/groups">My groups</Link>

src/app/layout.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,11 @@ export default function RootLayout({
6868
return (
6969
<html lang="en" suppressHydrationWarning>
7070
{env.PLAUSIBLE_DOMAIN && (
71-
<PlausibleProvider domain={env.PLAUSIBLE_DOMAIN} trackOutboundLinks />
71+
<PlausibleProvider
72+
domain={env.PLAUSIBLE_DOMAIN}
73+
trackOutboundLinks
74+
manualPageviews
75+
/>
7276
)}
7377
<body className="pt-16 min-h-[100dvh] flex flex-col items-stretch bg-slate-50 bg-opacity-30 dark:bg-background">
7478
<ThemeProvider

src/app/page.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { StatsDisplay } from '@/app/stats-display'
2+
import { TrackPage } from '@/components/track-page'
23
import { Button } from '@/components/ui/button'
34
import {
45
BarChartHorizontalBig,
@@ -22,6 +23,7 @@ export const dynamic = 'force-dynamic'
2223
export default function HomePage() {
2324
return (
2425
<main>
26+
<TrackPage path="/" />
2527
<section className="py-16 md:py-24 lg:py-32">
2628
<div className="container flex max-w-screen-md flex-col items-center gap-4 text-center">
2729
<h1 className="!leading-none font-bold text-3xl sm:text-5xl md:text-6xl lg:text-7xl landing-header py-2">

src/components/expense-form.tsx

+31-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { AsyncButton } from '@/components/async-button'
33
import { CategorySelector } from '@/components/category-selector'
44
import { SubmitButton } from '@/components/submit-button'
5+
import { useAnalytics } from '@/components/track-page'
56
import { Button } from '@/components/ui/button'
67
import {
78
Card,
@@ -112,10 +113,29 @@ export function ExpenseForm({
112113
splitMode: 'EVENLY',
113114
},
114115
})
116+
const sendEvent = useAnalytics()
115117

116118
return (
117119
<Form {...form}>
118-
<form onSubmit={form.handleSubmit((values) => onSubmit(values))}>
120+
<form
121+
onSubmit={form.handleSubmit(async (values) => {
122+
if (expense) {
123+
sendEvent(
124+
{
125+
event: 'expense: update',
126+
props: { groupId: group.id, expenseId: expense.id },
127+
},
128+
`/groups/${group.id}/expenses`,
129+
)
130+
} else {
131+
sendEvent(
132+
{ event: 'expense: create', props: { groupId: group.id } },
133+
`/groups/${group.id}/expenses`,
134+
)
135+
}
136+
await onSubmit(values)
137+
})}
138+
>
119139
<Card>
120140
<CardHeader>
121141
<CardTitle>
@@ -514,7 +534,16 @@ export function ExpenseForm({
514534
type="button"
515535
variant="destructive"
516536
loadingContent="Deleting…"
517-
action={onDelete}
537+
action={async () => {
538+
sendEvent(
539+
{
540+
event: 'expense: delete',
541+
props: { groupId: group.id, expenseId: expense.id },
542+
},
543+
`/groups/${group.id}/expenses`,
544+
)
545+
await onDelete()
546+
}}
518547
>
519548
<Trash2 className="w-4 h-4 mr-2" />
520549
Delete

src/components/group-form.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use client'
22
import { SubmitButton } from '@/components/submit-button'
3+
import { useAnalytics } from '@/components/track-page'
34
import { Button } from '@/components/ui/button'
45
import {
56
Card,
@@ -67,6 +68,7 @@ export function GroupForm({
6768
name: 'participants',
6869
keyName: 'key',
6970
})
71+
const sendEvent = useAnalytics()
7072

7173
let activeUser = 'None'
7274

@@ -87,6 +89,14 @@ export function GroupForm({
8789
<Form {...form}>
8890
<form
8991
onSubmit={form.handleSubmit(async (values) => {
92+
if (group) {
93+
sendEvent(
94+
{ event: 'group: update', props: { groupId: group.id } },
95+
`/groups/${group.id}/edit`,
96+
)
97+
} else {
98+
sendEvent({ event: 'group: create', props: {} }, `/groups`)
99+
}
90100
await onSubmit(values)
91101
})}
92102
>

src/components/track-page.tsx

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use client'
2+
3+
import { usePlausible } from 'next-plausible'
4+
import { useEffect } from 'react'
5+
6+
type Event =
7+
| { event: 'pageview'; props: {} }
8+
| { event: 'group: create'; props: {} }
9+
| { event: 'group: update'; props: { groupId: string } }
10+
| { event: 'expense: create'; props: { groupId: string } }
11+
| { event: 'expense: update'; props: { groupId: string; expenseId: string } }
12+
| { event: 'expense: delete'; props: { groupId: string; expenseId: string } }
13+
| { event: 'group: export expenses'; props: { groupId: string } }
14+
15+
export function TrackPage({ path }: { path: string }) {
16+
const sendEvent = useAnalytics()
17+
18+
useEffect(() => {
19+
sendEvent({ event: 'pageview', props: {} }, path)
20+
}, [path, sendEvent])
21+
22+
return null
23+
}
24+
25+
export function useAnalytics() {
26+
const plausible = usePlausible()
27+
28+
const sendEvent = ({ event, props }: Event, path: string) => {
29+
const url = `${window.location.origin}${path}`
30+
if (process.env.NODE_ENV !== 'production')
31+
console.log('Analytics event:', event, props, url)
32+
plausible(event, { props, u: url })
33+
}
34+
35+
return sendEvent
36+
}

0 commit comments

Comments
 (0)