Skip to content

Commit

Permalink
Merge pull request #12 from namidapoo/feat/recap
Browse files Browse the repository at this point in the history
feat: 統計情報を表示
  • Loading branch information
namidapoo authored Dec 29, 2024
2 parents f83daab + 0134396 commit 56e78ae
Show file tree
Hide file tree
Showing 30 changed files with 2,319 additions and 259 deletions.
10 changes: 5 additions & 5 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ body {
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
--radius: 0.5rem;
}
.dark {
Expand Down
55 changes: 27 additions & 28 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,38 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { cn } from "@/lib/utils";

const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});

const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
import type { FC } from "react";
import { Header } from "./recap/components/header/header";
import { ThemeProvider } from "./recap/components/theme-toggle/theme-provider";

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "Dev Recap 2024",
description: "A recap of the latest in web development",
};

export default function RootLayout({
children,
}: Readonly<{
type Props = {
children: React.ReactNode;
}>) {
};

const RootLayout: FC<Readonly<Props>> = ({ children }) => {
return (
<html lang="en">
<body
className={cn(
geistSans.variable,
geistMono.variable,
"antialiased text-gray-900",
)}
>
{children}
<html lang="ja" suppressHydrationWarning>
<body className="antialiased text-gray-900">
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<div className="flex flex-col h-dvh">
<Header />
<div className="flex-grow p-4 md:px-6 overflow-auto">
{children}
</div>
</div>
</ThemeProvider>
</body>
</html>
);
}
};

export default RootLayout;
52 changes: 42 additions & 10 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Button } from "@/components/ui/button";
import { auth } from "@/lib/auth";
import GitHub from "@/public/github-mark-white.svg";
import GitHubWhite from "@/public/github-mark-white.png";
import GitHubBlack from "@/public/github-mark.png";
import Image from "next/image";
import Link from "next/link";

Expand All @@ -13,18 +14,26 @@ export default async function Home() {
console.log(session);

return (
<div className="flex min-h-dvh items-center justify-center bg-gradient-to-b from-pink-300 to-blue-300 p-4">
<div className="w-full max-w-md space-y-8 text-center">
<h1 className="font-bold text-4xl text-blue-600 lg:text-5xl">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 h-full pt-24 px-4 text-accent-foreground md:items-center justify-items-center">
<div className="col-span-1 w-full max-w-md space-y-8 text-center">
<h1 className="font-bold text-2xl text-blue-600 lg:text-5xl">
Dev Recap 2024
</h1>

<div className="space-y-3">
<p className="font-semibold text-2xl">Hi, @{session.user.login} !</p>
<p className="text-base text-gray-600 lg:text-lg">
1年間おつかれさまでした。
</p>
<p className="text-base text-gray-600 lg:text-lg">
{session.user.image && (
<Image
src={session.user.image}
alt={session.user.login}
width={100}
height={100}
className="rounded-full mx-auto shadow"
priority
/>
)}
<p className="font-semibold text-xl">@{session.user.login}</p>
<p className="text-base lg:text-lg">今年もおつかれさまでした。</p>
<p className="text-base lg:text-lg">
あなたの1年間を振り返りましょう🥳
</p>
</div>
Expand All @@ -34,11 +43,34 @@ export default async function Home() {
href="/recap"
className="flex items-center justify-center gap-4 text-base"
>
<Image src={GitHub} alt="GitHub" width={20} height={20} />
<Image
src={GitHubWhite}
alt="GitHub"
width={20}
height={20}
className="dark:hidden"
/>
<Image
src={GitHubBlack}
alt="GitHub"
width={20}
height={20}
className="hidden dark:block"
/>
振り返りを見る
</Link>
</Button>
</div>

<div className="hidden md:block col-span-2">
<Image
src="/overview.png"
alt="Overview"
width={700}
height={800}
className="w-full h-auto mx-auto rounded-md"
/>
</div>
</div>
);
}
155 changes: 155 additions & 0 deletions app/recap/components/graph/languages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
"use client";
import type { Stats } from "@/app/recap/fetchGitHubStats";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
type ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import { useMemo } from "react";
import type { FC } from "react";
import { Label, Pie, PieChart } from "recharts";

type Props = {
data: Stats["languagesByCommitCount"];
};

export const LanguagesUsageGraph: FC<Props> = ({ data }) => {
const limitedData = useMemo(() => {
if (data.length <= 6) {
return data;
}
const mainItems = data.slice(0, 5);
const others = data.slice(5);

const othersCommitSum = others.reduce(
(acc, item) => acc + item.commitCount,
0,
);

return [
...mainItems,
{
language: "Others",
commitCount: othersCommitSum,
},
];
}, [data]);

const colorPalette = useMemo(
() => [
"hsl(var(--chart-1))",
"hsl(var(--chart-2))",
"hsl(var(--chart-3))",
"hsl(var(--chart-4))",
"hsl(var(--chart-5))",
"hsl(var(--chart-6))",
],
[],
);

const totalCommits = useMemo(() => {
return limitedData.reduce((acc, curr) => acc + curr.commitCount, 0);
}, [limitedData]);

const chartData = useMemo(() => {
if (totalCommits === 0) {
return limitedData.map((item, index) => ({
language: item.language,
share: 0,
fill: colorPalette[index % colorPalette.length],
}));
}
return limitedData.map((item, index) => ({
language: item.language,
share: (item.commitCount / totalCommits) * 100,
fill: colorPalette[index % colorPalette.length],
}));
}, [limitedData, totalCommits, colorPalette]);

const chartConfig = useMemo<ChartConfig>(() => {
const dynamicLangConfig = limitedData.reduce((acc, item, index) => {
acc[item.language] = {
label: item.language,
color: colorPalette[index % colorPalette.length],
};
return acc;
}, {} as ChartConfig);

return {
visitors: {
label: "Share",
},
...dynamicLangConfig,
};
}, [limitedData, colorPalette]);

return (
<Card className="flex flex-col py-[0.42rem]">
<CardHeader className="items-start pb-0">
<CardTitle>言語の使用率</CardTitle>
<CardDescription>
あなたが最も使用した言語は <b>{limitedData[0].language}</b> です。
</CardDescription>
</CardHeader>
<CardContent className="flex-1 pb-0">
<ChartContainer
config={chartConfig}
className="mx-auto aspect-square max-h-[360px]"
>
<PieChart>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent hideLabel />}
/>
<Pie
data={chartData}
dataKey="share"
nameKey="language"
innerRadius={60}
strokeWidth={5}
>
<Label
content={({ viewBox }) => {
if (viewBox && "cx" in viewBox && "cy" in viewBox) {
return (
<text
x={viewBox.cx}
y={viewBox.cy}
textAnchor="middle"
dominantBaseline="middle"
>
<tspan
x={viewBox.cx}
y={viewBox.cy}
className="fill-foreground text-3xl font-bold"
>
100
</tspan>
<tspan
x={viewBox.cx}
y={(viewBox.cy || 0) + 24}
className="fill-muted-foreground text-sm"
>
%
</tspan>
</text>
);
}
return null;
}}
/>
</Pie>
</PieChart>
</ChartContainer>
</CardContent>
</Card>
);
};
Loading

0 comments on commit 56e78ae

Please sign in to comment.