Backend — GET /api/v1/dashboard (single authenticated request):

Aggregates repos, open PRs, review queue, open issues server-side
Per-repo PR and issue counts computed in one pass
Review queue pulls PRs where the user is an assigned reviewer (from pr_reviewers table), excluding their own PRs
Frontend — complete redesign of DashboardPage.tsx:

Section	What it shows
Stats bar	Repo count · My PRs · Reviews awaiting · Open issues — each a clickable nav pill
⌘K Command palette	Fuzzy search across repos, PRs, issues with keyboard nav (↑↓ / Enter / Esc), quick-nav shortcuts when empty
Needs attention	Only appears when review queue is non-empty; badges each PR as "Review requested"
My pull requests	Open PRs I authored, with source→target branch, repo context, relative timestamp
My open issues	Issues I filed, linked to the repo issue list
Workspaces	My repos, prioritising recently visited (from useRecentRepos), with PR/issue count badges
CI/CD	Honest placeholder until pipeline integration lands
Quick actions	New repo · Import · Explore · Settings — always one click away
Empty state	Shows only when user has zero repos
This commit is contained in:
2026-05-07 16:36:45 +02:00
parent b624337b4a
commit 7436679eac
4 changed files with 857 additions and 109 deletions
+71
View File
@@ -0,0 +1,71 @@
import { useQuery } from '@tanstack/react-query'
import { z } from 'zod'
import { api } from '../client'
const statsSchema = z.object({
repoCount: z.number(),
openPRs: z.number(),
reviewQueue: z.number(),
openIssues: z.number(),
})
const dashPRSchema = z.object({
id: z.number(),
title: z.string(),
sourceBranch: z.string(),
targetBranch: z.string(),
status: z.string(),
createdAt: z.string(),
updatedAt: z.string(),
repoId: z.number(),
repoName: z.string(),
ownerName: z.string(),
authorId: z.number(),
})
const dashIssueSchema = z.object({
id: z.number(),
number: z.number(),
title: z.string(),
state: z.string(),
createdAt: z.string(),
updatedAt: z.string(),
repoId: z.number(),
repoName: z.string(),
ownerName: z.string(),
})
const dashRepoSchema = z.object({
id: z.number(),
name: z.string(),
description: z.string(),
isPrivate: z.boolean(),
defaultBranch: z.string(),
updatedAt: z.string(),
ownerName: z.string(),
avatarUrl: z.string(),
openPrCount: z.number(),
openIssueCount: z.number(),
})
const dashboardSchema = z.object({
stats: statsSchema,
reviewQueue: z.array(dashPRSchema),
myOpenPRs: z.array(dashPRSchema),
myOpenIssues: z.array(dashIssueSchema),
repos: z.array(dashRepoSchema),
})
export type DashboardData = z.infer<typeof dashboardSchema>
export type DashPR = z.infer<typeof dashPRSchema>
export type DashIssue = z.infer<typeof dashIssueSchema>
export type DashRepo = z.infer<typeof dashRepoSchema>
export function useDashboard() {
return useQuery({
queryKey: ['dashboard'],
queryFn: () => api.get<DashboardData>('/api/v1/dashboard', dashboardSchema),
refetchInterval: 60_000,
staleTime: 30_000,
})
}