Skip to content

Commit aff556d

Browse files
committed
add date ranges
1 parent 3e0c1d3 commit aff556d

File tree

9 files changed

+11091
-71
lines changed

9 files changed

+11091
-71
lines changed

apps/web/package-lock.json

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

apps/web/package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,27 @@
99
"lint": "next lint"
1010
},
1111
"dependencies": {
12+
"@radix-ui/react-checkbox": "^1.1.3",
13+
"@radix-ui/react-label": "^2.1.1",
14+
"@radix-ui/react-popover": "^1.1.4",
1215
"@radix-ui/react-scroll-area": "^1.2.2",
1316
"@radix-ui/react-slot": "^1.1.1",
1417
"@radix-ui/react-tooltip": "^1.1.6",
18+
"@shadcn/ui": "^0.0.4",
1519
"class-variance-authority": "^0.7.1",
1620
"clsx": "^2.1.1",
21+
"date-fns": "^4.1.0",
22+
"init": "^0.1.2",
1723
"lucide-react": "^0.468.0",
1824
"next": "15.1.1",
1925
"nuqs": "^2.2.3",
2026
"react": "^19.0.0",
27+
"react-day-picker": "^8.10.1",
2128
"react-dom": "^19.0.0",
2229
"react-markdown": "^9.0.1",
2330
"recharts": "^2.15.0",
24-
"tailwind-merge": "^2.5.5",
31+
"shadcn-ui": "^0.9.4",
32+
"tailwind-merge": "^2.6.0",
2533
"tailwindcss-animate": "^1.0.7"
2634
},
2735
"devDependencies": {

apps/web/pnpm-lock.yaml

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

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

Lines changed: 114 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { DauChart, DauDataPoint } from './dau-chart'
88
import { AuthMechChart, AuthMechDataPoint } from './auth-mech-chart'
99
import { DailySignupsChart, DailySignupsDataPoint } from './daily-signups-chart'
1010
import { DailyLoginFailsChart, DailyLoginFailsDataPoint } from './daily-login-fails-chart'
11+
import { DateRangePicker, DateRange } from '@/components/ui/date-range-picker'
12+
import { startOfDay, endOfDay } from 'date-fns'
1113

1214
interface ConversionData {
1315
new_signups: number
@@ -20,129 +22,183 @@ interface SummaryMetrics {
2022
total_applications: number
2123
total_apis: number
2224
total_connections: number
25+
monthly_signups: number
26+
monthly_active_users: number
27+
conversion_rate: number
2328
}
2429

2530
interface UsersResult {
26-
data: {
27-
total_users: number
28-
total_signups: number
29-
total_active_users: number
30-
total_failed_users: number
31-
first_seen: string
32-
last_seen: string
33-
}[]
31+
data: { total_users: number }[]
32+
}
33+
34+
interface ApplicationsResult {
35+
data: { total_applications: number }[]
36+
}
37+
38+
interface ApisResult {
39+
data: { total_apis: number }[]
40+
}
41+
42+
interface ConnectionsResult {
43+
data: { total_connections: number }[]
44+
}
45+
46+
interface MonthlySignupsResult {
47+
data: { monthly_signups: number }[]
48+
}
49+
50+
interface MonthlyActiveUsersResult {
51+
data: { active: number }[]
52+
}
53+
54+
interface ConversionRateResult {
55+
data: { conversion_rate: number }[]
3456
}
3557

3658
export default function Auth0Dashboard() {
3759
const [token] = useQueryState('token')
60+
const [dateRange, setDateRange] = useState<DateRange>({
61+
from: startOfDay(new Date(new Date().setDate(new Date().getDate() - 7))),
62+
to: endOfDay(new Date())
63+
})
3864
const [summaryMetrics, setSummaryMetrics] = useState<SummaryMetrics>({
3965
total_users: 0,
4066
total_applications: 0,
4167
total_apis: 0,
42-
total_connections: 0
68+
total_connections: 0,
69+
monthly_signups: 0,
70+
monthly_active_users: 0,
71+
conversion_rate: 0
4372
})
44-
const [monthlySignUps, setMonthlySignUps] = useState<number>(0)
45-
const [monthlyMau, setMonthlyMau] = useState<number>(0)
46-
const [conversionRate, setConversionRate] = useState<number>(0)
4773
const [dauData, setDauData] = useState<DauDataPoint[]>([])
4874
const [authMechData, setAuthMechData] = useState<AuthMechDataPoint[]>([])
4975
const [dailySignupsData, setDailySignupsData] = useState<DailySignupsDataPoint[]>([])
5076
const [dailyLoginFailsData, setDailyLoginFailsData] = useState<DailyLoginFailsDataPoint[]>([])
5177

52-
5378
useEffect(() => {
5479
async function fetchMetrics() {
5580
if (!token) return
5681

82+
const fromDate = dateRange.from.toISOString()
83+
const toDate = dateRange.to.toISOString()
84+
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()
85+
5786
try {
5887
const [
5988
usersResult,
6089
applicationsResult,
6190
apisResult,
6291
connectionsResult,
63-
monthlySignUpsResult,
64-
monthlyMauResult,
92+
monthlySignupsResult,
93+
monthlyActiveUsersResult,
94+
conversionRateResult,
6595
dauResult,
6696
authMechResult,
67-
conversionResult,
6897
dailySignupsResult,
6998
dailyLoginFailsResult
7099
] = await Promise.all([
71100
pipe<UsersResult>(token, 'auth0_users_total'),
72101
pipe(token, 'auth0_applications'),
73102
pipe(token, 'auth0_apis'),
74103
pipe(token, 'auth0_connections'),
75-
pipe(token, 'auth0_signups'),
76-
pipe(token, 'auth0_mau'),
77-
pipe<{ data: DauDataPoint[] }>(token, 'auth0_dau_ts'),
78-
pipe<{ data: AuthMechDataPoint[] }>(token, 'auth0_mech_usage'),
79-
pipe<{ data: ConversionData[] }>(token, 'auth0_conversion_rate'),
80-
pipe<{ data: DailySignupsDataPoint[] }>(token, 'auth0_daily_signups'),
81-
pipe<{ data: DailyLoginFailsDataPoint[] }>(token, 'auth0_daily_login_fails')
104+
pipe(token, 'auth0_signups', {
105+
date_from: thirtyDaysAgo
106+
}),
107+
pipe(token, 'auth0_mau', {
108+
date_from: thirtyDaysAgo
109+
}),
110+
pipe<ConversionRateResult>(token, 'auth0_conversion_rate', {
111+
date_from: thirtyDaysAgo
112+
}),
113+
pipe<{ data: DauDataPoint[] }>(token, 'auth0_dau_ts', {
114+
date_from: fromDate,
115+
date_to: toDate
116+
}),
117+
pipe<{ data: AuthMechDataPoint[] }>(token, 'auth0_mech_usage', {
118+
date_from: fromDate,
119+
date_to: toDate
120+
}),
121+
pipe<{ data: DailySignupsDataPoint[] }>(token, 'auth0_daily_signups', {
122+
date_from: fromDate,
123+
date_to: toDate
124+
}),
125+
pipe<{ data: DailyLoginFailsDataPoint[] }>(token, 'auth0_daily_login_fails', {
126+
date_from: fromDate,
127+
date_to: toDate
128+
})
82129
])
83130

84131
setSummaryMetrics({
85-
total_users: usersResult?.data?.[0]?.total_users || 0,
86-
total_applications: applicationsResult?.data?.length || 0,
87-
total_apis: apisResult?.data?.length || 0,
88-
total_connections: connectionsResult?.data?.length || 0
132+
total_users: usersResult?.data?.[0]?.total_users ?? 0,
133+
total_applications: applicationsResult?.data?.length ?? 0,
134+
total_apis: apisResult?.data?.length ?? 0,
135+
total_connections: connectionsResult?.data?.length ?? 0,
136+
monthly_signups: monthlySignupsResult?.data?.[0]?.total ?? 0,
137+
monthly_active_users: monthlyActiveUsersResult?.data?.[0]?.active ?? 0,
138+
conversion_rate: conversionRateResult?.data?.[0]?.conversion_rate ?? 0
89139
})
90-
setMonthlySignUps(monthlySignUpsResult.data[0]?.total || 0)
91-
setMonthlyMau(monthlyMauResult.data[0]?.active || 0)
92-
setConversionRate(conversionResult.data[0]?.conversion_rate || 0)
93-
setDauData(dauResult.data)
94-
setAuthMechData(authMechResult.data)
95-
setDailySignupsData(dailySignupsResult.data)
96-
setDailyLoginFailsData(dailyLoginFailsResult.data)
140+
141+
setDauData(dauResult?.data ?? [])
142+
setAuthMechData(authMechResult?.data ?? [])
143+
setDailySignupsData(dailySignupsResult?.data ?? [])
144+
setDailyLoginFailsData(dailyLoginFailsResult?.data ?? [])
97145
} catch (error) {
98146
console.error('Failed to fetch metrics:', error)
99147
}
100148
}
101149

102150
fetchMetrics()
103-
}, [token])
151+
}, [token, dateRange])
104152

105153
return (
106154
<div className="space-y-8">
107-
{/* Summary Card */}
108-
<div className="grid grid-cols-4 gap-4 p-6 rounded-lg border bg-card text-card-foreground shadow-sm">
109-
<div>
110-
<p className="text-sm font-medium text-muted-foreground">Total Users</p>
111-
<p className="text-2xl font-bold">{summaryMetrics.total_users.toLocaleString()}</p>
112-
</div>
113-
<div>
114-
<p className="text-sm font-medium text-muted-foreground">Applications</p>
115-
<p className="text-2xl font-bold">{summaryMetrics.total_applications.toLocaleString()}</p>
116-
</div>
117-
<div>
118-
<p className="text-sm font-medium text-muted-foreground">APIs</p>
119-
<p className="text-2xl font-bold">{summaryMetrics.total_apis.toLocaleString()}</p>
120-
</div>
121-
<div>
122-
<p className="text-sm font-medium text-muted-foreground">Connections</p>
123-
<p className="text-2xl font-bold">{summaryMetrics.total_connections.toLocaleString()}</p>
124-
</div>
155+
{/* Top row - Total metrics */}
156+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
157+
<MetricCard
158+
title="Total Users"
159+
value={summaryMetrics.total_users}
160+
/>
161+
<MetricCard
162+
title="Applications"
163+
value={summaryMetrics.total_applications}
164+
/>
165+
<MetricCard
166+
title="APIs"
167+
value={summaryMetrics.total_apis}
168+
/>
169+
<MetricCard
170+
title="Connections"
171+
value={summaryMetrics.total_connections}
172+
/>
125173
</div>
126174

127-
{/* Metrics Row */}
128-
<div className="grid gap-4 md:grid-cols-3">
175+
{/* Second row - Monthly metrics */}
176+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
129177
<MetricCard
130178
title="Monthly Sign Ups"
131-
value={monthlySignUps.toLocaleString()}
179+
value={summaryMetrics.monthly_signups}
132180
description="New users signed up in the last 30 days"
133181
/>
134182
<MetricCard
135183
title="Monthly Active Users"
136-
value={monthlyMau.toLocaleString()}
184+
value={summaryMetrics.monthly_active_users}
137185
description="Users active in the last 30 days"
138186
/>
139187
<MetricCard
140188
title="Conversion Rate"
141-
value={`${conversionRate}%`}
189+
value={`${summaryMetrics.conversion_rate}%`}
142190
description="New users who became active in the last 30 days"
143191
/>
144192
</div>
145193

194+
{/* Date Range Picker */}
195+
<div className="flex justify-end">
196+
<DateRangePicker
197+
initialDateRange={dateRange}
198+
onChange={(newRange) => setDateRange(newRange)}
199+
/>
200+
</div>
201+
146202
{/* Charts Grid */}
147203
<div className="grid gap-4 grid-cols-1">
148204
<DauChart data={dauData} />
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"use client"
2+
3+
import * as React from "react"
4+
import { ChevronLeft, ChevronRight } from "lucide-react"
5+
import { DayPicker } from "react-day-picker"
6+
7+
import { cn } from "@/lib/utils"
8+
import { buttonVariants } from "@/components/ui/button"
9+
10+
export type CalendarProps = React.ComponentProps<typeof DayPicker>
11+
12+
function Calendar({
13+
className,
14+
classNames,
15+
showOutsideDays = true,
16+
...props
17+
}: CalendarProps) {
18+
return (
19+
<DayPicker
20+
showOutsideDays={showOutsideDays}
21+
className={cn("p-3", className)}
22+
classNames={{
23+
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
24+
month: "space-y-4",
25+
caption: "flex justify-center pt-1 relative items-center",
26+
caption_label: "text-sm font-medium",
27+
nav: "space-x-1 flex items-center",
28+
nav_button: cn(
29+
buttonVariants({ variant: "outline" }),
30+
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
31+
),
32+
nav_button_previous: "absolute left-1",
33+
nav_button_next: "absolute right-1",
34+
table: "w-full border-collapse space-y-1",
35+
head_row: "flex",
36+
head_cell:
37+
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
38+
row: "flex w-full mt-2",
39+
cell: cn(
40+
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
41+
props.mode === "range"
42+
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
43+
: "[&:has([aria-selected])]:rounded-md"
44+
),
45+
day: cn(
46+
buttonVariants({ variant: "ghost" }),
47+
"h-8 w-8 p-0 font-normal aria-selected:opacity-100"
48+
),
49+
day_range_start: "day-range-start",
50+
day_range_end: "day-range-end",
51+
day_selected:
52+
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
53+
day_today: "bg-accent text-accent-foreground",
54+
day_outside:
55+
"day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
56+
day_disabled: "text-muted-foreground opacity-50",
57+
day_range_middle:
58+
"aria-selected:bg-accent aria-selected:text-accent-foreground",
59+
day_hidden: "invisible",
60+
...classNames,
61+
}}
62+
components={{
63+
IconLeft: ({ className, ...props }) => (
64+
<ChevronLeft className={cn("h-4 w-4", className)} {...props} />
65+
),
66+
IconRight: ({ className, ...props }) => (
67+
<ChevronRight className={cn("h-4 w-4", className)} {...props} />
68+
),
69+
}}
70+
{...props}
71+
/>
72+
)
73+
}
74+
Calendar.displayName = "Calendar"
75+
76+
export { Calendar }
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"use client"
2+
3+
import * as React from "react"
4+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
5+
import { Check } from "lucide-react"
6+
7+
import { cn } from "@/lib/utils"
8+
9+
const Checkbox = React.forwardRef<
10+
React.ElementRef<typeof CheckboxPrimitive.Root>,
11+
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
12+
>(({ className, ...props }, ref) => (
13+
<CheckboxPrimitive.Root
14+
ref={ref}
15+
className={cn(
16+
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
17+
className
18+
)}
19+
{...props}
20+
>
21+
<CheckboxPrimitive.Indicator
22+
className={cn("flex items-center justify-center text-current")}
23+
>
24+
<Check className="h-4 w-4" />
25+
</CheckboxPrimitive.Indicator>
26+
</CheckboxPrimitive.Root>
27+
))
28+
Checkbox.displayName = CheckboxPrimitive.Root.displayName
29+
30+
export { Checkbox }

0 commit comments

Comments
 (0)