Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions apps/web/src/components/tools/auth0/auth-mech-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import {
} from "@/components/ui/chart"
import { Bar, BarChart, XAxis, YAxis } from "recharts"

interface AuthMechData {
export interface AuthMechDataPoint {
mech: string
logins: number
}

interface AuthMechChartProps {
data: AuthMechData[]
export interface AuthMechChartData {
data: AuthMechDataPoint[]
}

const chartConfig = {
Expand All @@ -30,7 +30,7 @@ const chartConfig = {
},
} satisfies ChartConfig

export function AuthMechChart({ data }: AuthMechChartProps) {
export function AuthMechChart({ data }: AuthMechChartData) {
// Sort data by number of logins in descending order
const sortedData = [...data].sort((a, b) => b.logins - a.logins)

Expand All @@ -45,7 +45,7 @@ export function AuthMechChart({ data }: AuthMechChartProps) {
data={sortedData}
layout="vertical"
margin={{
left: 80,
left: 24,
right: 12,
top: 12,
bottom: 12,
Expand Down
95 changes: 95 additions & 0 deletions apps/web/src/components/tools/auth0/daily-login-fails-chart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use client"

import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
ChartContainer,
ChartTooltip,
ChartConfig,
ChartTooltipContent
} from "@/components/ui/chart"
import { Line, LineChart, XAxis, YAxis } from "recharts"

export interface DailyLoginFailsDataPoint {
day: string
fails: number
}

export interface DailyLoginFailsChartData {
data: DailyLoginFailsDataPoint[]
}

const chartConfig = {
fails: {
color: "hsl(var(--primary))",
label: "Login Fails",
},
} satisfies ChartConfig

export function DailyLoginFailsChart({ data }: DailyLoginFailsChartData) {
return (
<Card>
<CardHeader>
<CardTitle>Daily Login Fails</CardTitle>
</CardHeader>
<CardContent className="">
<ChartContainer config={chartConfig} className="h-[400px] w-full">
<LineChart
data={data}
margin={{
left: 48,
right: 12,
top: 12,
bottom: 32
}}
>
<XAxis
dataKey="day"
tickLine={false}
axisLine={false}
tickMargin={8}
interval="equidistantPreserveStart"
tickFormatter={(value) => value.split('-')[2]}
label={{
value: "Day of Month",
position: "bottom",
offset: 20
}}
/>
<YAxis
tickLine={false}
axisLine={false}
tickMargin={8}
label={{
value: "Login Fails",
angle: -90,
position: "left",
offset: 32
}}
/>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent indicator="dot" />}
/>
<Line
type="monotone"
dataKey="fails"
strokeWidth={2}
activeDot={{
r: 4,
style: { fill: "hsl(var(--primary))" },
}}
style={{
stroke: "hsl(var(--primary))",
}}
/>
</LineChart>
</ChartContainer>
</CardContent>
</Card>
)
}
95 changes: 95 additions & 0 deletions apps/web/src/components/tools/auth0/daily-signups-chart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use client"

import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
ChartContainer,
ChartTooltip,
ChartConfig,
ChartTooltipContent
} from "@/components/ui/chart"
import { Line, LineChart, XAxis, YAxis } from "recharts"

export interface DailySignupsDataPoint {
day: string
signups: number
}

export interface DailySignupsChartData {
data: DailySignupsDataPoint[]
}

const chartConfig = {
signups: {
color: "hsl(var(--primary))",
label: "Signups",
},
} satisfies ChartConfig

export function DailySignupsChart({ data }: DailySignupsChartData) {
return (
<Card>
<CardHeader>
<CardTitle>Daily Signups</CardTitle>
</CardHeader>
<CardContent className="">
<ChartContainer config={chartConfig} >
<LineChart
data={data}
margin={{
left: 48,
right: 12,
top: 12,
bottom: 32
}}
>
<XAxis
dataKey="day"
tickLine={false}
axisLine={false}
tickMargin={8}
interval="equidistantPreserveStart"
tickFormatter={(value) => value.split('-')[2]}
label={{
value: "Day of Month",
position: "bottom",
offset: 20
}}
/>
<YAxis
tickLine={false}
axisLine={false}
tickMargin={8}
label={{
value: "Signups",
angle: -90,
position: "left",
offset: 32
}}
/>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent indicator="dot" />}
/>
<Line
type="monotone"
dataKey="signups"
strokeWidth={2}
activeDot={{
r: 4,
style: { fill: "hsl(var(--primary))" },
}}
style={{
stroke: "hsl(var(--primary))",
}}
/>
</LineChart>
</ChartContainer>
</CardContent>
</Card>
)
}
105 changes: 88 additions & 17 deletions apps/web/src/components/tools/auth0/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,96 @@ import { useQueryState } from 'nuqs'
import { useEffect, useState } from 'react'
import { pipe } from '@/lib/tinybird'
import MetricCard from './metric'
import { DauChart } from './dau-chart'
import { AuthMechChart } from './auth-mech-chart'

interface DauDataPoint {
day: string
active: number
}

interface AuthMechData {
mech: string
logins: number
}
import { DauChart, DauDataPoint } from './dau-chart'
import { AuthMechChart, AuthMechDataPoint } from './auth-mech-chart'
import { DailySignupsChart, DailySignupsDataPoint } from './daily-signups-chart'
import { DailyLoginFailsChart, DailyLoginFailsDataPoint } from './daily-login-fails-chart'

interface ConversionData {
new_signups: number
active_new_users: number
conversion_rate: number
}

interface SummaryMetrics {
total_users: number
total_applications: number
total_apis: number
total_connections: number
}

interface UsersResult {
data: {
total_users: number
total_signups: number
total_active_users: number
total_failed_users: number
first_seen: string
last_seen: string
}[]
}

export default function Auth0Dashboard() {
const [token] = useQueryState('token')
const [summaryMetrics, setSummaryMetrics] = useState<SummaryMetrics>({
total_users: 0,
total_applications: 0,
total_apis: 0,
total_connections: 0
})
const [monthlySignUps, setMonthlySignUps] = useState<number>(0)
const [monthlyMau, setMonthlyMau] = useState<number>(0)
const [conversionRate, setConversionRate] = useState<number>(0)
const [dauData, setDauData] = useState<DauDataPoint[]>([])
const [authMechData, setAuthMechData] = useState<AuthMechData[]>([])
const [authMechData, setAuthMechData] = useState<AuthMechDataPoint[]>([])
const [dailySignupsData, setDailySignupsData] = useState<DailySignupsDataPoint[]>([])
const [dailyLoginFailsData, setDailyLoginFailsData] = useState<DailyLoginFailsDataPoint[]>([])


useEffect(() => {
async function fetchMetrics() {
if (!token) return

try {
const [monthlySignUpsResult, monthlyMauResult, dauResult, authMechResult, conversionResult] = await Promise.all([
const [
usersResult,
applicationsResult,
apisResult,
connectionsResult,
monthlySignUpsResult,
monthlyMauResult,
dauResult,
authMechResult,
conversionResult,
dailySignupsResult,
dailyLoginFailsResult
] = await Promise.all([
pipe<UsersResult>(token, 'auth0_users_total'),
pipe(token, 'auth0_applications'),
pipe(token, 'auth0_apis'),
pipe(token, 'auth0_connections'),
pipe(token, 'auth0_signups'),
pipe(token, 'auth0_mau'),
pipe<{ data: DauDataPoint[] }>(token, 'auth0_dau_ts'),
pipe<{ data: AuthMechData[] }>(token, 'auth0_mech_usage'),
pipe<{ data: ConversionData[] }>(token, 'auth0_conversion_rate')
pipe<{ data: AuthMechDataPoint[] }>(token, 'auth0_mech_usage'),
pipe<{ data: ConversionData[] }>(token, 'auth0_conversion_rate'),
pipe<{ data: DailySignupsDataPoint[] }>(token, 'auth0_daily_signups'),
pipe<{ data: DailyLoginFailsDataPoint[] }>(token, 'auth0_daily_login_fails')
])

setSummaryMetrics({
total_users: usersResult?.data?.[0]?.total_users || 0,
total_applications: applicationsResult?.data?.length || 0,
total_apis: apisResult?.data?.length || 0,
total_connections: connectionsResult?.data?.length || 0
})
setMonthlySignUps(monthlySignUpsResult.data[0]?.total || 0)
setMonthlyMau(monthlyMauResult.data[0]?.active || 0)
setConversionRate(conversionResult.data[0]?.conversion_rate || 0)
setDauData(dauResult.data)
setAuthMechData(authMechResult.data)
setDailySignupsData(dailySignupsResult.data)
setDailyLoginFailsData(dailyLoginFailsResult.data)
} catch (error) {
console.error('Failed to fetch metrics:', error)
}
Expand All @@ -59,6 +104,26 @@ export default function Auth0Dashboard() {

return (
<div className="space-y-8">
{/* Summary Card */}
<div className="grid grid-cols-4 gap-4 p-6 rounded-lg border bg-card text-card-foreground shadow-sm">
<div>
<p className="text-sm font-medium text-muted-foreground">Total Users</p>
<p className="text-2xl font-bold">{summaryMetrics.total_users.toLocaleString()}</p>
</div>
<div>
<p className="text-sm font-medium text-muted-foreground">Applications</p>
<p className="text-2xl font-bold">{summaryMetrics.total_applications.toLocaleString()}</p>
</div>
<div>
<p className="text-sm font-medium text-muted-foreground">APIs</p>
<p className="text-2xl font-bold">{summaryMetrics.total_apis.toLocaleString()}</p>
</div>
<div>
<p className="text-sm font-medium text-muted-foreground">Connections</p>
<p className="text-2xl font-bold">{summaryMetrics.total_connections.toLocaleString()}</p>
</div>
</div>

{/* Metrics Row */}
<div className="grid gap-4 md:grid-cols-3">
<MetricCard
Expand All @@ -79,9 +144,15 @@ export default function Auth0Dashboard() {
</div>

{/* Charts Grid */}
<div className="grid gap-4 md:grid-cols-2">
<div className="grid gap-4 grid-cols-1">
<DauChart data={dauData} />
</div>
<div className="grid gap-4 md:grid-cols-1 xl:grid-cols-2">
<AuthMechChart data={authMechData} />
<DailySignupsChart data={dailySignupsData} />
</div>
<div className="grid gap-4 grid-cols-1">
<DailyLoginFailsChart data={dailyLoginFailsData} />
</div>
</div>
)
Expand Down
Loading
Loading