Skip to content

Commit bcf6d4d

Browse files
committed
stripe subs
1 parent c3739af commit bcf6d4d

File tree

6 files changed

+137
-5
lines changed

6 files changed

+137
-5
lines changed
File renamed without changes.

apps/web/src/components/tools/auth0/dashboard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { useQueryState } from 'nuqs'
44
import { useEffect, useState } from 'react'
55
import { pipe } from '@/lib/tinybird'
6-
import MetricCard from './metric'
6+
import MetricCard from '@/components/metric'
77
import { DauChart, DauDataPoint } from './dau-chart'
88
import { AuthMechChart, AuthMechDataPoint } from './auth-mech-chart'
99
import { DailySignupsChart, DailySignupsDataPoint } from './daily-signups-chart'

apps/web/src/components/tools/clerk/dashboard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { useQueryState } from 'nuqs'
44
import { useEffect, useState } from 'react'
55
import { pipe } from '@/lib/tinybird'
6-
import MetricCard from '../auth0/metric'
6+
import MetricCard from '@/components/metric'
77
import { DauChart } from './dau-chart'
88
import { AuthMechChart } from './auth-mech-chart'
99

apps/web/src/components/tools/orb/dashboard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { useQueryState } from 'nuqs'
44
import { useEffect, useState } from 'react'
55
import { pipe } from '@/lib/tinybird'
6-
import MetricCard from '../auth0/metric'
6+
import MetricCard from '@/components/metric'
77
import { SubsChart, SubsDataPoint } from './subs-chart'
88
import { SubsByPlanChart, SubsByPlanDataPoint } from './sub-by-plan-chart'
99
import { SubsByPlanTsChart, SubsByPlanTsDataPoint } from './sub-by-plan-ts-chart'

apps/web/src/components/tools/stripe/dashboard.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
"use client"
22

33
import { useQueryState } from 'nuqs'
4-
import { useEffect } from 'react'
4+
import { useEffect, useState } from 'react'
5+
import { DailySubsChart, DailySubsDataPoint } from './subs-ts-chart'
6+
import { pipe } from '@/lib/tinybird'
7+
import MetricCard from '@/components/metric'
8+
9+
type SubsMetricsDataPoint = {
10+
created: number
11+
deleted: number
12+
}
513

614
export default function StripeDashboard() {
715
const [token] = useQueryState('token')
16+
const [subsTimeSeriesData, setSubsTimeSeriesData] = useState<DailySubsDataPoint[]>([])
17+
const [subsMetricsData, setSubsMetricsData] = useState<SubsMetricsDataPoint[]>([])
818

919
useEffect(() => {
1020
async function fetchMetrics() {
1121
if (!token) return
1222

1323
try {
14-
const [] = await Promise.all([
24+
const [dailySubsResult, subsMetricsResult] = await Promise.all([
25+
pipe<{ data: DailySubsDataPoint[] }>(token, 'stripe_subs_ts'),
26+
pipe<{ data: SubsMetricsDataPoint[] }>(token, 'stripe_subs_metric'),
1527
])
28+
29+
setSubsTimeSeriesData(dailySubsResult.data)
30+
setSubsMetricsData(subsMetricsResult.data)
1631
} catch (error) {
1732
console.error('Failed to fetch metrics:', error)
1833
}
@@ -25,10 +40,14 @@ export default function StripeDashboard() {
2540
<div className="space-y-8">
2641
{/* Metrics Row */}
2742
<div className="grid gap-4 md:grid-cols-3">
43+
<MetricCard title="New Subs" value={subsMetricsData[0]?.created || 0} />
44+
<MetricCard title="Churned Subs" value={subsMetricsData[0]?.deleted || 0} />
45+
<MetricCard title="Churn Rate" value={`${subsMetricsData[0]?.deleted > 0 ? Math.round((subsMetricsData[0]?.deleted / subsMetricsData[0]?.created) * 100) : 0}%`} />
2846
</div>
2947

3048
{/* Charts Grid */}
3149
<div className="grid gap-4 md:grid-cols-2">
50+
<DailySubsChart data={subsTimeSeriesData} timeRange="daily" />
3251
</div>
3352
</div>
3453
)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"use client"
2+
3+
import {
4+
Card,
5+
CardContent,
6+
CardHeader,
7+
CardTitle,
8+
} from "@/components/ui/card"
9+
import {
10+
ChartContainer,
11+
ChartConfig
12+
} from "@/components/ui/chart"
13+
import { Line, LineChart, XAxis, YAxis, CartesianGrid } from "recharts"
14+
import { format } from "date-fns"
15+
16+
export interface DailySubsDataPoint {
17+
day: string
18+
created: number
19+
deleted: number
20+
}
21+
22+
export interface DailySubsChartData {
23+
data: DailySubsDataPoint[]
24+
timeRange: 'monthly' | 'daily'
25+
className?: string
26+
}
27+
28+
const chartConfig = {
29+
created: {
30+
color: "hsl(var(--chart-1))",
31+
label: "Subscriptions Created",
32+
},
33+
deleted: {
34+
color: "hsl(var(--chart-2))",
35+
label: "Subscriptions Deleted",
36+
},
37+
} satisfies ChartConfig
38+
39+
export function DailySubsChart({ data, timeRange, className }: DailySubsChartData) {
40+
return (
41+
<Card>
42+
<CardHeader>
43+
<CardTitle>Subscriptions</CardTitle>
44+
</CardHeader>
45+
<CardContent className="">
46+
<ChartContainer config={chartConfig} className={`w-full ${className}`}>
47+
<LineChart
48+
data={data}
49+
margin={{
50+
left: 48,
51+
right: 12,
52+
top: 12,
53+
bottom: 32
54+
}}
55+
>
56+
<CartesianGrid
57+
horizontal={true}
58+
vertical={false}
59+
className="stroke-muted"
60+
/>
61+
<XAxis
62+
dataKey="day"
63+
tickLine={false}
64+
axisLine={false}
65+
tickMargin={8}
66+
interval="equidistantPreserveStart"
67+
tickFormatter={(value) => {
68+
const date = new Date(value)
69+
return timeRange === 'monthly'
70+
? format(date, 'MMM yyyy')
71+
: value.split('-')[2]
72+
}}
73+
label={{
74+
value: timeRange === 'monthly' ? "Month of Year" : "Day of Month",
75+
position: "bottom",
76+
offset: 20
77+
}}
78+
/>
79+
<YAxis
80+
tickLine={false}
81+
axisLine={false}
82+
tickMargin={8}
83+
label={{
84+
value: "Subscriptions",
85+
angle: -90,
86+
position: "left",
87+
offset: 32
88+
}}
89+
/>
90+
<Line
91+
type="monotone"
92+
dataKey="created"
93+
strokeWidth={2}
94+
dot={false}
95+
style={{
96+
stroke: "hsl(var(--chart-1))",
97+
}}
98+
/>
99+
<Line
100+
type="monotone"
101+
dataKey="deleted"
102+
strokeWidth={2}
103+
dot={false}
104+
style={{
105+
stroke: "hsl(var(--chart-2))",
106+
}}
107+
/>
108+
</LineChart>
109+
</ChartContainer>
110+
</CardContent>
111+
</Card>
112+
)
113+
}

0 commit comments

Comments
 (0)