Skip to content

Commit

Permalink
Merge pull request #11 from YoubetDao/feature/dashboard-total-issue
Browse files Browse the repository at this point in the history
feat: issue completion leaderboard
  • Loading branch information
Amateur0x1 authored Jul 18, 2024
2 parents a28960f + 25a9fa9 commit 37ce161
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 113 deletions.
33 changes: 0 additions & 33 deletions src/components/forms/user-auth-form.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
import { Button } from '@/components/ui/button'
import { zodResolver } from '@hookform/resolvers/zod'
// import { signIn } from 'next-auth/react'
// import { useSearchParams } from 'next/navigation'
import { useLocation } from 'react-router-dom'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
import { Icons } from '@/components/icons'
// import GithubSignInButton from '../github-auth-button'

const CLIENT_ID = 'Ov23li86Nz0RcXbj54Z5'
const REDIRECT_URI = 'http://localhost:3000/auth/github/callback'

const signIn = (...args: unknown[]) => {
location.href = '/'
}

const githubOAuth = () => {
window.location.href = `http://github.com/login/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=user:email`
}
Expand All @@ -32,31 +23,7 @@ const GithubSignInButton = () => {
)
}

const formSchema = z.object({
email: z.string().email({ message: 'Enter a valid email address' }),
})

type UserFormValue = z.infer<typeof formSchema>

export default function UserAuthForm() {
const searchParams = new URLSearchParams(useLocation().search)
const callbackUrl = searchParams.get('callbackUrl')
const [loading, setLoading] = useState(false)
const defaultValues = {
email: '[email protected]',
}
const form = useForm<UserFormValue>({
resolver: zodResolver(formSchema),
defaultValues,
})

const onSubmit = async (data: UserFormValue) => {
signIn('credentials', {
email: data.email,
callbackUrl: callbackUrl ?? '/dashboard',
})
}

return (
<>
<div className="relative">
Expand Down
74 changes: 74 additions & 0 deletions src/components/issue-completion-leaderboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import api from '@/service'
import { Issue } from '@/types'
import { useEffect, useState } from 'react'
import LeaderboardRow from './leaderboard-row'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
export function IssueCompletionLeaderboard() {
const [issues, setIssues] = useState<Issue[]>([])
const [assigneeStats, setAssigneeStats] = useState<{
[key: string]: { avatarSrc: string; name: string; html: string; completedTasks: number }
}>({})
const [openedCount, setOpenedCount] = useState<number>(0)
const [closedCount, setClosedCount] = useState<number>(0)

useEffect(() => {
api.fetchIssues('YoubetDao', 'youbet-test-repo').then((data) => {
if (data) {
setIssues(data)

const stats: { [key: string]: { avatarSrc: string; name: string; html: string; completedTasks: number } } = {}
let opened = 0
let closed = 0

data.forEach((issue) => {
issue.assignees.forEach((assignee) => {
if (!stats[assignee.login]) {
stats[assignee.login] = {
avatarSrc: assignee.avatar_url,
name: assignee.login,
html: assignee.html_url, // 将 email 改为 html 字段
completedTasks: 0,
}
}
if (issue.state === 'closed') {
stats[assignee.login].completedTasks += 1
}
})
if (issue.state === 'closed') {
closed++
} else {
opened++
}
})

setAssigneeStats(stats)
setOpenedCount(opened)
setClosedCount(closed)
}
})
}, [])

return (
<Card className="col-span-4 md:col-span-3">
<CardHeader>
<CardTitle>Issue Completion Leaderboard</CardTitle>
<CardDescription>
Opened: {openedCount} Closed: {closedCount}
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-8">
{Object.values(assigneeStats).map((assignee, index) => (
<LeaderboardRow
key={index}
avatarSrc={assignee.avatarSrc}
name={assignee.name}
html={assignee.html}
completedTasks={assignee.completedTasks}
/>
))}
</div>
</CardContent>
</Card>
)
}
26 changes: 26 additions & 0 deletions src/components/leaderboard-row.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
interface LeaderboardRowProps {
avatarSrc: string
name: string
html: string
completedTasks: number
}

const LeaderboardRow: React.FC<LeaderboardRowProps> = ({ avatarSrc, name, html, completedTasks }) => {
return (
<div className="flex items-center">
<Avatar className="h-9 w-9">
<AvatarImage src={avatarSrc} alt="Avatar" />
<AvatarFallback>{name.charAt(0)}</AvatarFallback>
</Avatar>
<div className="ml-4 space-y-1">
<p className="text-sm font-medium leading-none">{name}</p>
<p className="text-sm text-muted-foreground">{html}</p>
</div>
<div className="ml-auto font-medium">{completedTasks} </div>
</div>
)
}

export default LeaderboardRow
63 changes: 0 additions & 63 deletions src/components/recent-sales.tsx

This file was deleted.

20 changes: 3 additions & 17 deletions src/pages/dashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Button } from '@/components/ui/button'
import { CalendarDateRangePicker } from '@/components/date-range-picker'
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { AreaGraph } from '@/components/charts/AreaGraph'
import { BarGraph } from '@/components/charts/BarGraph'
import { PieGraph } from '@/components/charts/PieGraph'
import { RecentSales } from '@/components/recent-sales'
import { IssueCompletionLeaderboard } from '@/components/issue-completion-leaderboard'

export default function Dashboard() {
return (
Expand All @@ -15,10 +13,6 @@ export default function Dashboard() {
<div className="flex-1 p-4 pt-6 space-y-4 md:p-8">
<div className="flex items-center justify-between space-y-2">
<h2 className="text-3xl font-bold tracking-tight">Hi, Welcome back 👋</h2>
<div className="items-center hidden space-x-2 md:flex">
<CalendarDateRangePicker />
<Button>Download</Button>
</div>
</div>
<Tabs defaultValue="overview" className="space-y-4">
<TabsList>
Expand Down Expand Up @@ -121,15 +115,7 @@ export default function Dashboard() {
<div className="col-span-4">
<BarGraph />
</div>
<Card className="col-span-4 md:col-span-3">
<CardHeader>
<CardTitle>Recent Sales</CardTitle>
<CardDescription>You made 265 sales this month.</CardDescription>
</CardHeader>
<CardContent>
<RecentSales />
</CardContent>
</Card>
<IssueCompletionLeaderboard />
<div className="col-span-4">
<AreaGraph />
</div>
Expand Down
16 changes: 16 additions & 0 deletions src/service/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Cookies from 'js-cookie'
import http from './instance'
import { Issue } from '@/types'

const api = {
fetchUserInfo: async (code: string) => {
Expand All @@ -19,6 +20,21 @@ const api = {
console.error('Error fetching user info:', error)
}
},
fetchIssues: async (org: string, project: string): Promise<Issue[] | null> => {
try {
const response = await http.get('/tasks', {
params: { org, project },
})

if (response.data) {
return response.data
}
return null
} catch (error) {
console.error('Error fetching user info:', error)
return null
}
},
}

export default api

0 comments on commit 37ce161

Please sign in to comment.