'use client'; import { useEffect, useState } from 'react'; import Link from 'next/link'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { StatusDot } from '@/components/status-badge'; import { ProgressRing } from '@/components/progress-ring'; import { useUser } from '@/components/user-context'; import { calculateProgress, getDaysRemaining, isKRActive } from '@/lib/kr-utils'; import { REASON_CODES, ROLE_LABELS } from '@/lib/constants'; import type { Project, KR, User } from '@/types/database'; import { FolderKanban, Target, AlertTriangle, Clock, ArrowRight, AlertCircle, HandHelping, } from 'lucide-react'; import { fetchApi } from '@/lib/api-client'; interface ProjectWithRelations extends Project { owner: User | null; pm: User | null; } interface KRWithRelations extends KR { owner: User | null; project: { id: string; name: string; status: string } | null; } export default function DashboardPage() { const { currentUser } = useUser(); const [projects, setProjects] = useState([]); const [krs, setKRs] = useState([]); const [isLoading, setIsLoading] = useState(true); useEffect(() => { async function fetchData() { try { const [projectsRes, krsRes] = await Promise.all([ fetchApi('/api/projects'), fetchApi('/api/krs'), ]); if (projectsRes.ok) { const projectsData = await projectsRes.json(); setProjects(projectsData); } if (krsRes.ok) { const krsData = await krsRes.json(); setKRs(krsData); } } catch (error) { console.error('Failed to fetch data:', error); } finally { setIsLoading(false); } } fetchData(); }, []); const activeKRs = krs.filter(isKRActive); const redKRs = activeKRs.filter(kr => kr.status === 'red'); const yellowKRs = activeKRs.filter(kr => kr.status === 'yellow'); const helpNeededKRs = activeKRs.filter(kr => kr.need_owner_help); const urgentKRs = activeKRs.filter(kr => getDaysRemaining(kr.period_end) <= 3 && kr.status !== 'green'); const stats = { totalProjects: projects.length, greenProjects: projects.filter(p => p.status === 'green').length, yellowProjects: projects.filter(p => p.status === 'yellow').length, redProjects: projects.filter(p => p.status === 'red').length, totalKRs: activeKRs.length, completedKRs: activeKRs.filter(kr => calculateProgress(kr) >= 1).length, }; if (isLoading) { return (
{[1, 2, 3, 4].map(i => (
))}
); } return (

目标总览

{currentUser?.role === 'owner' ? '全局视角 - 所有项目健康度一览' : `${ROLE_LABELS[currentUser?.role || 'member']}视角`}

项目总数

{stats.totalProjects}

{stats.greenProjects} 正常 {stats.yellowProjects} 警告 {stats.redProjects} 风险

进行中 KR

{stats.totalKRs}

{stats.completedKRs} 已达标 {stats.totalKRs - stats.completedKRs} 进行中
0 ? 'border-rose-200 bg-rose-50/50' : ''}>

风险 KR

{redKRs.length}

0 ? 'text-rose-300' : 'text-slate-300'}`} />

需要立即关注

0 ? 'border-amber-200 bg-amber-50/50' : ''}>

需要帮助

{helpNeededKRs.length}

0 ? 'text-amber-300' : 'text-slate-300'}`} />

成员请求老板支援

{(redKRs.length > 0 || urgentKRs.length > 0 || helpNeededKRs.length > 0) && ( 风险预警 {redKRs.map(kr => (

{kr.title}

{kr.project?.name} | {kr.owner?.name || '未分配'} {kr.reason_code && ` | ${REASON_CODES[kr.reason_code]}`}

))} {urgentKRs.filter(kr => kr.status !== 'red').map(kr => (

{kr.title}

{kr.project?.name} | 剩余 {getDaysRemaining(kr.period_end)} 天

))} {helpNeededKRs.filter(kr => kr.status === 'green').map(kr => (

{kr.title}

{kr.owner?.name} 请求支援

))}
)}
项目健康度 {projects.slice(0, 5).map(project => { const projectKRs = krs.filter(kr => kr.project_id === project.id && isKRActive(kr)); const completedCount = projectKRs.filter(kr => calculateProgress(kr) >= 1).length; return (

{project.name}

{project.pm?.name || '未指定PM'} | {projectKRs.length} 个 KR

{completedCount}/{projectKRs.length}

已达标

); })} {projects.length === 0 && (

暂无项目

)}
近期 KR 动态 {activeKRs.slice(0, 5).map(kr => { const progress = calculateProgress(kr); const daysLeft = getDaysRemaining(kr.period_end); return (

{kr.title}

{kr.project?.name} | {kr.owner?.name || '未分配'}

{kr.current_value}/{kr.target_value} {kr.unit}

剩余 {daysLeft} 天

); })} {activeKRs.length === 0 && (

暂无进行中的 KR

)}
); }