Skip to content

Commit

Permalink
feat(ui): create statistcs page
Browse files Browse the repository at this point in the history
  • Loading branch information
mateusfg7 committed Nov 6, 2023
1 parent d7861a1 commit d8a1eda
Show file tree
Hide file tree
Showing 17 changed files with 806 additions and 0 deletions.
25 changes: 25 additions & 0 deletions src/app/statistics/components/age.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client'

import { useState } from 'react'
import { Card } from './card'

export function AgeCard() {
const diffCalc = () => {
const diff =
(new Date().getTime() - new Date('August 22, 2002').getTime()) /
1000 /
60 /
60 /
24 /
365
return diff.toFixed(9)
}

const [age, setAge] = useState(diffCalc())

setInterval(() => {
setAge(diffCalc())
}, 100)

return <Card title="My Age" content={age} />
}
8 changes: 8 additions & 0 deletions src/app/statistics/components/api-error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Warning } from '@/shared/wrappers/phosphor-icons'

export const ApiErrorMessage = () => (
<span className="inline-flex items-center gap-2 text-base text-red-500">
<Warning />
<span>API Error</span>
</span>
)
19 changes: 19 additions & 0 deletions src/app/statistics/components/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ReactNode } from 'react'

type CardProps = {
title: string
content: string | ReactNode
icon?: ReactNode
}

export function Card({ content, title, icon }: CardProps) {
return (
<div className="flex h-full w-full flex-col justify-center gap-2 rounded-3xl bg-neutral-200 p-4 leading-none dark:bg-neutral-950 md:p-7">
<span className="inline-flex items-center gap-2 text-neutral-600">
<span>{title}</span>
{icon && icon}
</span>
<span className="text-xl">{content}</span>
</div>
)
}
41 changes: 41 additions & 0 deletions src/app/statistics/components/github-dashboard/cards/commits.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { GitCommit } from '@/shared/wrappers/phosphor-icons'

import { ApiErrorMessage } from '../../api-error'
import { Card } from '../../card'

type Data = {
total_count: number
}

export async function Commits() {
const githubApiRequest = await fetch(
'https://api.github.com/search/commits?q=author:mateusfg7'
)

if (!githubApiRequest.ok) {
console.log(githubApiRequest)
return <Card title="Commits" content={<ApiErrorMessage />} />
}

const { total_count }: Data = await githubApiRequest.json()

return (
<div className="flex h-full w-full flex-col justify-center gap-3 rounded-3xl bg-neutral-200 p-4 leading-none dark:bg-neutral-950 md:p-7">
<span className="inline-flex items-center gap-2 text-neutral-600">
<span>Commits</span>
<GitCommit weight="duotone" />
</span>
<div className="flex h-full items-center text-2xl">{total_count}</div>
</div>
)
}

export const CommitsSkeleton = () => (
<div className="flex h-full w-full flex-col justify-center gap-3 rounded-3xl bg-neutral-200 p-4 leading-none dark:bg-neutral-950 md:p-7">
<span className="inline-flex items-center gap-2 text-neutral-600">
<span>Repositories</span>
<GitCommit weight="duotone" />
</span>
<div className="h-7 w-1/2 animate-pulse rounded-3xl bg-neutral-400 dark:bg-neutral-800" />
</div>
)
139 changes: 139 additions & 0 deletions src/app/statistics/components/github-dashboard/cards/followers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import Image from 'next/image'

import { Heart } from '@/shared/wrappers/phosphor-icons'
import { placeholder } from '@/shared/lib/placeholder'

import { Card } from '../../card'
import { ApiErrorMessage } from '../../api-error'

type User = {
followers: number
}

type Follower = {
login: string
avatar_url: string
html_url: string
}

const AVATAR_COUNT = 119 // 12x14

export async function Followers() {
const githubUserRequest = await fetch(
'https://api.github.com/users/mateusfg7'
)

if (!githubUserRequest.ok) {
console.log(githubUserRequest)
return (
<Card
title="Followers"
content={
<div className="flex h-full w-full items-center">
<ApiErrorMessage />
</div>
}
/>
)
}

const { followers: followersNumber }: User = await githubUserRequest.json()

const numberOfPages = Math.ceil(followersNumber / 100)

let followers: Follower[] = []
let error = false

for (let index = 1; index <= numberOfPages; index++) {
const githubFollowersRequest = await fetch(
`https://api.github.com/users/mateusfg7/followers?per_page=100&page=${index}`
)

if (!githubFollowersRequest.ok) {
error = true
console.log(githubFollowersRequest)
return
}

const list: Follower[] = await githubFollowersRequest.json()

followers = [...followers, ...list]
}

if (error) {
return (
<Card
title="Followers"
content={
<div className="flex h-full w-full items-center">
<ApiErrorMessage />
</div>
}
/>
)
}

const shuffle = (array: any[]) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[array[i], array[j]] = [array[j], array[i]]
}
return array
}

const slicedFollowers: Follower[] = shuffle(followers).slice(0, AVATAR_COUNT)

return (
<div className="flex h-full w-full flex-col justify-between gap-2 rounded-3xl bg-neutral-200 p-4 leading-none dark:bg-neutral-950 md:p-7">
<span className="inline-flex items-center gap-2 text-neutral-600">
<span>Followers</span>
<Heart weight="duotone" />
</span>
<span className="text-xl">
<div className="grid grid-cols-12 gap-1">
{slicedFollowers.map(follower => (
<a href={follower.html_url} key={follower.login} target="_blank">
<Image
src={follower.avatar_url}
title={follower.login}
alt=""
width={400}
height={400}
placeholder={placeholder(28, 28) as `data:image/${string}`}
className="w-7 rounded-full border-2 border-neutral-200 transition-all hover:border-neutral-600 dark:border-neutral-950 dark:hover:border-neutral-400"
/>
</a>
))}
<a
href="https://github.com/mateusfg7?tab=followers"
target="_blank"
className="flex items-center justify-center text-sm leading-none transition-colors hover:underline"
>
+{followersNumber - AVATAR_COUNT}
</a>
</div>
</span>
</div>
)
}

export const FollowersSkeleton = () => {
return (
<div className="flex h-full w-full flex-col justify-between gap-2 rounded-3xl bg-neutral-200 p-4 leading-none dark:bg-neutral-950 md:p-7">
<span className="inline-flex items-center gap-2 text-neutral-600">
<span>Followers</span>
<Heart weight="duotone" />
</span>
<span className="text-xl">
<div className="grid grid-cols-12 gap-1">
{[...Array(AVATAR_COUNT + 1)].map((e, i) => (
<div
key={i}
className="h-5 w-5 animate-pulse rounded-full border-2 border-neutral-200 bg-neutral-400 dark:border-neutral-950 dark:bg-neutral-800 md:h-7 md:w-7"
/>
))}
</div>
</span>
</div>
)
}
32 changes: 32 additions & 0 deletions src/app/statistics/components/github-dashboard/cards/graph.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Image from 'next/image'
import { CalendarBlank } from '@/shared/wrappers/phosphor-icons'
import { placeholder } from '@/shared/lib/placeholder'

export function Graph() {
return (
<div className="flex h-full w-full flex-col justify-center gap-3 rounded-3xl bg-neutral-200 p-4 leading-none dark:bg-neutral-950 md:p-7">
<span className="inline-flex items-center gap-2 text-neutral-600">
<span>Contribution Graph</span>
<CalendarBlank weight="duotone" />
</span>
<div className="flex items-center">
<Image
src="https://contribution.catsjuice.com/_/mateusfg7?chart=calendar&format=png&quality=10&weeks=40&theme=native&widget_size=small"
placeholder={placeholder(1992, 408) as `data:image/${string}`}
alt="Contribution Graph"
className="dark:hidden"
width={1992}
height={408}
/>
<Image
src="https://contribution.catsjuice.com/_/mateusfg7?chart=calendar&format=png&quality=1&weeks=40&theme=native&widget_size=small&dark=true"
placeholder={placeholder(1992, 408) as `data:image/${string}`}
alt="Contribution Graph"
className="hidden dark:block"
width={1992}
height={408}
/>
</div>
</div>
)
}
47 changes: 47 additions & 0 deletions src/app/statistics/components/github-dashboard/cards/languages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Code } from '@/shared/wrappers/phosphor-icons'

import { ApiErrorMessage } from '../../api-error'
import { Card } from '../../card'

type Repository = {
language?: string
}

export async function Languages() {
const githubApiRequest = await fetch(
'https://api.github.com/users/mateusfg7/repos?per_page=200'
)

if (!githubApiRequest.ok) {
console.log(githubApiRequest)
return <Card title="Languages" content={<ApiErrorMessage />} />
}

const repos: Repository[] = await githubApiRequest.json()

const languages = repos
.map(repo => repo.language)
.filter(language => language)

const languagesLength = [...new Set(languages)].length

return (
<div className="flex h-full w-full flex-col justify-center gap-3 rounded-3xl bg-neutral-200 p-4 leading-none dark:bg-neutral-950 md:p-7">
<span className="inline-flex items-center gap-2 text-neutral-600">
<span>Languages</span>
<Code weight="duotone" />
</span>
<div className="flex h-full items-center text-2xl">{languagesLength}</div>
</div>
)
}

export const LanguagesSkeleton = () => (
<div className="flex h-full w-full flex-col justify-center gap-3 rounded-3xl bg-neutral-200 p-4 leading-none dark:bg-neutral-950 md:p-7">
<span className="inline-flex items-center gap-2 text-neutral-600">
<span>Languages</span>
<Code weight="duotone" />
</span>
<div className="h-7 w-1/2 animate-pulse rounded-3xl bg-neutral-400 dark:bg-neutral-800" />
</div>
)
39 changes: 39 additions & 0 deletions src/app/statistics/components/github-dashboard/cards/repos.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Cube } from '@/shared/wrappers/phosphor-icons'

import { ApiErrorMessage } from '../../api-error'
import { Card } from '../../card'

type User = {
public_repos: number
}

export async function Repos() {
const githubApiRequest = await fetch('https://api.github.com/users/mateusfg7')

if (!githubApiRequest.ok) {
console.log(githubApiRequest)
return <Card title="Repositories" content={<ApiErrorMessage />} />
}

const { public_repos }: User = await githubApiRequest.json()

return (
<div className="flex h-full w-full flex-col justify-center gap-3 rounded-3xl bg-neutral-200 p-4 leading-none dark:bg-neutral-950 md:p-7">
<span className="inline-flex items-center gap-2 text-neutral-600">
<span>Repositories</span>
<Cube weight="duotone" />
</span>
<div className="flex h-full items-center text-2xl">{public_repos}</div>
</div>
)
}

export const ReposSkeleton = () => (
<div className="flex h-full w-full flex-col justify-center gap-3 rounded-3xl bg-neutral-200 p-4 leading-none dark:bg-neutral-950 md:p-7">
<span className="inline-flex items-center gap-2 text-neutral-600">
<span>Repositories</span>
<Cube weight="duotone" />
</span>
<div className="h-7 w-1/2 animate-pulse rounded-3xl bg-neutral-400 dark:bg-neutral-800" />
</div>
)
Loading

0 comments on commit d8a1eda

Please sign in to comment.