Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 統計情報を表示 #12

Merged
merged 17 commits into from
Dec 29, 2024
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 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
Copy link
Preview

Copilot AI Dec 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Label component in the PieChart has a hardcoded value of 100. This should be dynamically calculated based on the data to avoid confusion.

Suggested change
100
{totalCommits}

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
</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
Loading