Skip to content

Commit 9a33401

Browse files
committed
unify dyn imports & tools, readme backs
1 parent 698ff49 commit 9a33401

File tree

20 files changed

+518
-380
lines changed

20 files changed

+518
-380
lines changed

apps/web/src/app/[id]/page.tsx

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,8 @@
22

33
import { useQueryState } from 'nuqs';
44
import React, { useEffect, useState } from 'react';
5-
import { Card } from '@/components/ui/card';
65
import { listDataSources } from '@/lib/tinybird';
7-
import dynamic from 'next/dynamic';
8-
9-
const TOOLS = {
10-
clerk: {
11-
name: 'Clerk',
12-
Dashboard: dynamic(() => import('@/components/tools/clerk/dashboard')),
13-
Readme: dynamic(() => import('@/components/tools/clerk/readme')),
14-
},
15-
resend: {
16-
name: 'Resend',
17-
Dashboard: dynamic(() => import('@/components/tools/resend/dashboard')),
18-
Readme: dynamic(() => import('@/components/tools/resend/readme')),
19-
},
20-
auth0: {
21-
name: 'Auth0',
22-
Dashboard: dynamic(() => import('@/components/tools/auth0/dashboard')),
23-
Readme: dynamic(() => import('@/components/tools/auth0/readme')),
24-
},
25-
} as const;
26-
27-
type ToolId = keyof typeof TOOLS;
6+
import { TOOL_IMPORTS, type ToolId, TOOLS } from '@/lib/constants';
287

298
export default function AppPage({ params }: { params: Promise<{ id: string }> }) {
309
const [token, setToken] = useQueryState('token');
@@ -35,22 +14,25 @@ export default function AppPage({ params }: { params: Promise<{ id: string }> })
3514
async function checkInstallation() {
3615
if (!token) return;
3716
const sources = await listDataSources(token);
38-
setIsInstalled(sources.some(source => source.name.startsWith(id)));
17+
setIsInstalled(sources.some(source => source.name === id));
3918
}
4019
checkInstallation();
4120
}, [token, id]);
4221

43-
const tool = TOOLS[id];
44-
if (!tool) {
22+
if (!(id in TOOLS)) {
4523
return <div>Tool not found</div>;
4624
}
25+
const tool_comps = TOOL_IMPORTS[id];
26+
if (!tool_comps) {
27+
return <div>Tool not implemented</div>;
28+
}
29+
30+
const Component = isInstalled ? tool_comps.Dashboard : tool_comps.Readme;
4731

48-
const Component = isInstalled ? tool.Dashboard : tool.Readme;
49-
5032
return (
5133
<div className="container py-6">
5234
<div className="flex flex-col gap-6">
53-
<h1 className="text-2xl font-bold">{tool.name}</h1>
35+
<h1 className="text-2xl font-bold">{TOOLS[id].name}</h1>
5436
<Component />
5537
</div>
5638
</div>

apps/web/src/app/orb/page.tsx

Lines changed: 0 additions & 5 deletions
This file was deleted.

apps/web/src/app/page.tsx

Lines changed: 3 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,59 +7,7 @@ import { useState, useEffect } from 'react';
77
import { Card } from '@/components/ui/card';
88
import Link from 'next/link';
99
import { listDataSources } from '@/lib/tinybird';
10-
11-
interface AppGridItem {
12-
id: string;
13-
ds: string;
14-
name: string;
15-
description: string;
16-
icon: string;
17-
}
18-
19-
const KNOWN_APPS: AppGridItem[] = [
20-
{
21-
id: 'clerk',
22-
ds: 'clerk',
23-
name: 'Clerk',
24-
description: 'Authentication and user management',
25-
icon: '🔐'
26-
},
27-
{
28-
id: 'resend',
29-
ds: 'resend',
30-
name: 'Resend',
31-
description: 'Email delivery service',
32-
icon: '✉️'
33-
},
34-
{
35-
id: 'auth0',
36-
ds: 'auth0_logs',
37-
name: 'Auth0',
38-
description: 'Identity platform',
39-
icon: '🔑'
40-
},
41-
{
42-
id: 'vercel',
43-
ds: 'vercel_logs',
44-
name: 'Vercel Logs',
45-
description: 'Deployment and serverless logs',
46-
icon: '📊'
47-
},
48-
{
49-
id: 'gitlab',
50-
ds: 'gitlab',
51-
name: 'Gitlab',
52-
description: 'Source code management',
53-
icon: '🦊'
54-
},
55-
{
56-
id: 'orb',
57-
ds: 'orb',
58-
name: 'Orb',
59-
description: 'Usage-based billing',
60-
icon: '💰'
61-
}
62-
];
10+
import { TOOLS, type AppGridItem } from '@/lib/constants';
6311

6412
function AppCard({ app, isInstalled, token }: { app: AppGridItem; isInstalled: boolean; token?: string }) {
6513
return (
@@ -135,8 +83,8 @@ export default function Home() {
13583
);
13684
}
13785

138-
const installedAppsList = KNOWN_APPS.filter(app => installedApps.includes(app.ds));
139-
const uninstalledAppsList = KNOWN_APPS.filter(app => !installedApps.includes(app.ds));
86+
const installedAppsList = Object.values(TOOLS).filter(app => installedApps.includes(app.ds));
87+
const uninstalledAppsList = Object.values(TOOLS).filter(app => !installedApps.includes(app.ds));
14088

14189
return (
14290
<div className="space-y-8">

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

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

3+
import Link from 'next/link'
4+
import { useQueryState } from 'nuqs'
5+
6+
export default function Auth0Readme() {
7+
const [token] = useQueryState('token')
38

4-
export default function ClerkDashboard() {
59
return (
610
<div>
7-
<h1>Readme</h1>
11+
<div>
12+
<h1 className="text-2xl font-bold">Auth0 Analytics</h1>
13+
<Link
14+
href={token ? `/?token=${token}` : '/'}
15+
className="text-sm text-muted-foreground hover:text-primary"
16+
>
17+
← Back to Apps
18+
</Link>
19+
</div>
820
<pre>
921
<code>
1022
1. do something
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"use client"
2+
3+
import {
4+
Card,
5+
CardContent,
6+
CardHeader,
7+
CardTitle,
8+
} from "@/components/ui/card"
9+
import {
10+
ChartContainer,
11+
ChartTooltip,
12+
ChartConfig,
13+
ChartTooltipContent
14+
} from "@/components/ui/chart"
15+
import { Bar, BarChart, XAxis, YAxis } from "recharts"
16+
17+
interface AuthMechData {
18+
mech: string
19+
logins: number
20+
}
21+
22+
interface AuthMechChartProps {
23+
data: AuthMechData[]
24+
}
25+
26+
const chartConfig = {
27+
logins: {
28+
color: "hsl(var(--primary))",
29+
label: "Logins",
30+
},
31+
} satisfies ChartConfig
32+
33+
export function AuthMechChart({ data }: AuthMechChartProps) {
34+
// Sort data by number of logins in descending order
35+
const sortedData = [...data].sort((a, b) => b.logins - a.logins)
36+
37+
return (
38+
<Card>
39+
<CardHeader>
40+
<CardTitle>Authentication Methods</CardTitle>
41+
</CardHeader>
42+
<CardContent>
43+
<ChartContainer config={chartConfig}>
44+
<BarChart
45+
data={sortedData}
46+
layout="vertical"
47+
margin={{
48+
left: 80,
49+
right: 12,
50+
top: 12,
51+
bottom: 12,
52+
}}
53+
>
54+
<XAxis
55+
type="number"
56+
tickLine={false}
57+
axisLine={false}
58+
tickMargin={8}
59+
/>
60+
<YAxis
61+
type="category"
62+
dataKey="mech"
63+
tickLine={false}
64+
axisLine={false}
65+
tickMargin={8}
66+
width={70}
67+
/>
68+
<ChartTooltip
69+
content={<ChartTooltipContent />}
70+
cursor={{ fill: "hsl(var(--muted))", opacity: 0.2 }}
71+
/>
72+
<Bar
73+
dataKey="logins"
74+
radius={[4, 4, 4, 4]}
75+
fill="hsl(var(--primary))"
76+
/>
77+
</BarChart>
78+
</ChartContainer>
79+
</CardContent>
80+
</Card>
81+
)
82+
}

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

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,49 @@ import { useQueryState } from 'nuqs'
44
import { useEffect, useState } from 'react'
55
import Link from 'next/link'
66
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
7-
import { query } from '@/lib/tinybird'
8-
import MetricCard from './metric'
7+
import { pipe } from '@/lib/tinybird'
8+
import MetricCard from '../auth0/metric'
9+
import { DauChart } from './dau-chart'
10+
import { AuthMechChart } from './auth-mech-chart'
11+
12+
interface DauDataPoint {
13+
day: string
14+
active: number
15+
}
16+
17+
interface AuthMechData {
18+
mech: string
19+
logins: number
20+
}
921

1022
export default function ClerkDashboard() {
1123
const [token] = useQueryState('token')
12-
const [totalUsers, setTotalUsers] = useState<number>(0)
13-
const [activeUsers, setActiveUsers] = useState<number>(0)
24+
const [monthlySignUps, setMonthlySignUps] = useState<number>(0)
25+
const [monthlyMau, setMonthlyMau] = useState<number>(0)
1426
const [conversionRate, setConversionRate] = useState<number>(0)
27+
const [dauData, setDauData] = useState<DauDataPoint[]>([])
28+
const [authMechData, setAuthMechData] = useState<AuthMechData[]>([])
1529

1630
useEffect(() => {
1731
async function fetchMetrics() {
1832
if (!token) return
1933

2034
try {
21-
const [totalResult, activeResult] = await Promise.all([
22-
query(token, "SELECT count() as total FROM clerk WHERE type = 'user.created' FORMAT JSON"),
23-
query(token, "SELECT count(DISTINCT data.user_id) as active FROM clerk WHERE time >= now() - INTERVAL 30 DAY AND type = 'session.created' FORMAT JSON")
35+
const [monthlySignUpsResult, monthlyMauResult, dauResult, authMechResult] = await Promise.all([
36+
pipe(token, 'clerk_signups'),
37+
pipe(token, 'clerk_mau'),
38+
pipe<{ data: DauDataPoint[] }>(token, 'clerk_dau_ts'),
39+
pipe<{ data: AuthMechData[] }>(token, 'clerk_mech_usage')
2440
])
2541

26-
const total = totalResult.data[0]?.total || 0
27-
const active = activeResult.data[0]?.active || 0
42+
const total = monthlySignUpsResult.data[0]?.total || 0
43+
const active = monthlyMauResult.data[0]?.active || 0
2844

29-
setTotalUsers(total)
30-
setActiveUsers(active)
45+
setMonthlySignUps(total)
46+
setMonthlyMau(active)
3147
setConversionRate(total > 0 ? Math.round((active / total) * 100) : 0)
48+
setDauData(dauResult.data)
49+
setAuthMechData(authMechResult.data)
3250
} catch (error) {
3351
console.error('Failed to fetch metrics:', error)
3452
}
@@ -52,13 +70,13 @@ export default function ClerkDashboard() {
5270
{/* Metrics Row */}
5371
<div className="grid gap-4 md:grid-cols-3">
5472
<MetricCard
55-
title="Total Users"
56-
value={totalUsers.toLocaleString()}
57-
description="Total registered users"
73+
title="Monthly Sign Ups"
74+
value={monthlySignUps.toLocaleString()}
75+
description="New users signed up in the last 30 days"
5876
/>
5977
<MetricCard
60-
title="Active Users (30d)"
61-
value={activeUsers.toLocaleString()}
78+
title="Monthly Active Users"
79+
value={monthlyMau.toLocaleString()}
6280
description="Users active in the last 30 days"
6381
/>
6482
<MetricCard
@@ -70,39 +88,23 @@ export default function ClerkDashboard() {
7088

7189
{/* Charts Grid */}
7290
<div className="grid gap-4 md:grid-cols-2">
91+
<DauChart data={dauData} />
92+
<AuthMechChart data={authMechData} />
7393
<Card className="col-span-1">
7494
<CardHeader>
75-
<CardTitle>User Growth</CardTitle>
76-
</CardHeader>
77-
<CardContent className="h-[300px]">
78-
{/* User Growth Chart will go here */}
79-
</CardContent>
80-
</Card>
81-
<Card className="col-span-1">
82-
<CardHeader>
83-
<CardTitle>Daily Active Users</CardTitle>
84-
</CardHeader>
85-
<CardContent className="h-[300px]">
86-
{/* DAU Chart will go here */}
87-
</CardContent>
88-
</Card>
89-
<Card className="col-span-1">
90-
<CardHeader>
91-
<CardTitle>User Actions</CardTitle>
95+
<CardTitle>Something else</CardTitle>
9296
</CardHeader>
9397
<CardContent className="h-[300px]">
94-
{/* User Actions Chart will go here */}
9598
</CardContent>
9699
</Card>
97100
<Card className="col-span-1">
98101
<CardHeader>
99-
<CardTitle>Session Duration</CardTitle>
102+
<CardTitle>Something else</CardTitle>
100103
</CardHeader>
101104
<CardContent className="h-[300px]">
102-
{/* Session Duration Chart will go here */}
103105
</CardContent>
104106
</Card>
105107
</div>
106108
</div>
107109
)
108-
}
110+
}

0 commit comments

Comments
 (0)