Add Human vs AI chart

This commit is contained in:
2026-04-13 21:56:25 +02:00
parent 1b95bb2a82
commit 4258d99c25
2 changed files with 75 additions and 83 deletions

View File

@@ -1,20 +1,10 @@
"use client" "use client"
import { Bar, BarChart, LabelList, XAxis, YAxis, Cell } from "recharts"; import { Bar, BarChart, LabelList, XAxis, YAxis, Cell } from "recharts"
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils"
import { ChartContainer, type ChartConfig } from "@/components/ui/chart"
import { const COLORS = ["#4ade80", "#60a5fa", "#f87171", "#fb923c", "#c084fc"]
ChartContainer,
type ChartConfig,
} from "@/components/ui/chart";
const COLORS = [
"#4ade80",
"#60a5fa",
"#f87171",
"#fb923c",
"#c084fc",
]
const chartConfig = { const chartConfig = {
total_seconds: { total_seconds: {
@@ -33,8 +23,9 @@ interface MyBarChartProps {
className?: string className?: string
} }
interface MyBarChartSkeletonProps { interface MyBarChartStateProps {
className?: string className?: string
bars?: number
} }
export function MyBarChart({ data, className }: MyBarChartProps) { export function MyBarChart({ data, className }: MyBarChartProps) {
@@ -83,14 +74,11 @@ export function MyBarChart({ data, className }: MyBarChartProps) {
) )
} }
export function MyBarChartSkeleton({ className }: MyBarChartSkeletonProps) { export function MyBarChartSkeleton({ className, bars = 5 }: MyBarChartStateProps) {
const fakeData = [ const fakeData = Array.from({ length: bars }).map((_, i) => ({
{ language: "a", total_seconds: 102000 }, language: String.fromCharCode(97 + i),
{ language: "b", total_seconds: 59000 }, total_seconds: 100000 - i * 15000,
{ language: "c", total_seconds: 41000 }, }))
{ language: "d", total_seconds: 28000 },
{ language: "e", total_seconds: 17000 },
]
return ( return (
<ChartContainer <ChartContainer
@@ -98,61 +86,28 @@ export function MyBarChartSkeleton({ className }: MyBarChartSkeletonProps) {
className={cn("w-full overflow-visible animate-pulse", className)} className={cn("w-full overflow-visible animate-pulse", className)}
style={{ height: fakeData.length * 28 }} style={{ height: fakeData.length * 28 }}
> >
<BarChart <BarChart data={fakeData} layout="vertical" margin={{ right: 0, left: 0, top: 0, bottom: 0 }} barSize={20}>
data={fakeData}
layout="vertical"
margin={{ right: 0, left: 0, top: 0, bottom: 0 }}
barSize={20}
barCategoryGap="10%"
>
<YAxis dataKey="language" type="category" hide /> <YAxis dataKey="language" type="category" hide />
<XAxis dataKey="total_seconds" type="number" hide /> <XAxis dataKey="total_seconds" type="number" hide />
<Bar <Bar dataKey="total_seconds" layout="vertical" fill="#6b7280" radius={4} opacity={0.3} isAnimationActive={false} />
dataKey="total_seconds"
layout="vertical"
fill="#6b7280"
radius={4}
opacity={0.3}
isAnimationActive={false}
/>
</BarChart> </BarChart>
</ChartContainer> </ChartContainer>
) )
} }
export function MyBarChartError({ className }: MyBarChartSkeletonProps) { export function MyBarChartError({ className, bars = 5 }: MyBarChartStateProps) {
const fakeData = [ const fakeData = Array.from({ length: bars }).map((_, i) => ({
{ language: "a", total_seconds: 102000 }, language: String.fromCharCode(97 + i),
{ language: "b", total_seconds: 59000 }, total_seconds: 100000 - i * 15000,
{ language: "c", total_seconds: 41000 }, }))
{ language: "d", total_seconds: 28000 },
{ language: "e", total_seconds: 17000 },
]
return ( return (
<div className={cn("relative w-full overflow-hidden", className)} style={{ height: fakeData.length * 28 }}> <div className={cn("relative w-full overflow-hidden", className)} style={{ height: fakeData.length * 28 }}>
<ChartContainer <ChartContainer config={chartConfig} className="w-full overflow-visible" style={{ height: fakeData.length * 28 }}>
config={chartConfig} <BarChart data={fakeData} layout="vertical" margin={{ right: 0, left: 0, top: 0, bottom: 0 }} barSize={20}>
className="w-full overflow-visible"
style={{ height: fakeData.length * 28 }}
>
<BarChart
data={fakeData}
layout="vertical"
margin={{ right: 0, left: 0, top: 0, bottom: 0 }}
barSize={20}
barCategoryGap="10%"
>
<YAxis dataKey="language" type="category" hide /> <YAxis dataKey="language" type="category" hide />
<XAxis dataKey="total_seconds" type="number" hide /> <XAxis dataKey="total_seconds" type="number" hide />
<Bar <Bar dataKey="total_seconds" layout="vertical" fill="#7f1d1d" radius={4} opacity={0.6} isAnimationActive={false} />
dataKey="total_seconds"
layout="vertical"
fill="#7f1d1d"
radius={4}
opacity={0.6}
isAnimationActive={false}
/>
</BarChart> </BarChart>
</ChartContainer> </ChartContainer>
<div className="absolute inset-0 flex items-center justify-center pointer-events-none"> <div className="absolute inset-0 flex items-center justify-center pointer-events-none">

View File

@@ -1,4 +1,4 @@
import { MyBarChart, MyBarChartError } from "./bar-chart"; import { MyBarChart, MyBarChartError, MyBarChartSkeleton } from "./bar-chart"
type WakatimeLanguage = { type WakatimeLanguage = {
name: string name: string
@@ -7,27 +7,64 @@ type WakatimeLanguage = {
} }
export async function Wakatime({ className }: { className?: string }) { export async function Wakatime({ className }: { className?: string }) {
const response = await fetch("https://wakatime.com/api/v1/users/alixz/stats/last_30_days", { const response = await fetch(
"https://wakatime.com/api/v1/users/alixz/stats/last_30_days",
{
method: "GET", method: "GET",
headers: { headers: {
"Authorization": `Basic ${process.env.WAKATIME}` Authorization: `Basic ${process.env.WAKATIME}`,
}, },
next: { revalidate: 3600 } next: { revalidate: 3600 },
}); }
)
if (!response.ok) return <MyBarChartError className={className} />; if (!response.ok) {
return (
const res = await response.json(); <div className={className}>
<MyBarChartError bars={5} />
if (!res?.data?.languages?.length) { <div className="h-6" />
return <MyBarChartError className={className} />; <MyBarChartError bars={2} />
</div>
)
} }
const data = res.data.languages.slice(0, 5).map((lang: WakatimeLanguage) => ({ const res = await response.json()
const stats = res?.data
if (!stats?.languages?.length) {
return (
<div className={className}>
<MyBarChartError bars={5} />
<div className="h-6" />
<MyBarChartError bars={2} />
</div>
)
}
const languagesData = stats.languages.slice(0, 5).map((lang: WakatimeLanguage) => ({
language: lang.name, language: lang.name,
total_seconds: lang.total_seconds, total_seconds: lang.total_seconds,
label: `${lang.name} (${lang.text})`, label: `${lang.name} (${lang.text})`,
})); }))
return <MyBarChart className={className} data={data} />; const additionsData = [
{
language: "Human",
total_seconds: stats.human_additions,
label: `Human (${stats.human_additions.toLocaleString()} lines written)`,
},
{
language: "AI",
total_seconds: stats.ai_additions,
label: `AI (${stats.ai_additions.toLocaleString()} lines written)`,
},
]
return (
<div className={className}>
<MyBarChart data={languagesData} />
<div className="h-6" />
<MyBarChart data={additionsData} />
</div>
)
} }