feat: remove nginx, add projects/milestones/notifications pages

- Dockerfile: replace nginx with serve for static files
- Fix auth endpoint: /auth/login → /auth/token
- Add ProjectsPage, ProjectDetailPage
- Add MilestonesPage, MilestoneDetailPage with progress bar
- Add NotificationsPage with unread count
- Sidebar: add milestones/notifications nav, live unread badge
- API: configurable VITE_API_BASE for host nginx proxy
- Types: add Milestone, MilestoneProgress, Notification, ProjectMember
This commit is contained in:
zhi
2026-03-06 13:05:19 +00:00
parent 853594f447
commit 54d4c4379a
13 changed files with 529 additions and 25 deletions

View File

@@ -1,4 +1,6 @@
import { useState, useEffect } from 'react'
import { Link, useLocation } from 'react-router-dom'
import api from '@/services/api'
import type { User } from '@/types'
interface Props {
@@ -8,10 +10,26 @@ interface Props {
export default function Sidebar({ user, onLogout }: Props) {
const { pathname } = useLocation()
const [unreadCount, setUnreadCount] = useState(0)
useEffect(() => {
api.get<{ count: number }>('/notifications/count')
.then(({ data }) => setUnreadCount(data.count))
.catch(() => {})
const timer = setInterval(() => {
api.get<{ count: number }>('/notifications/count')
.then(({ data }) => setUnreadCount(data.count))
.catch(() => {})
}, 30000)
return () => clearInterval(timer)
}, [])
const links = [
{ to: '/', icon: '📊', label: '仪表盘' },
{ to: '/issues', icon: '📋', label: 'Issues' },
{ to: '/projects', icon: '📁', label: '项目' },
{ to: '/milestones', icon: '🏁', label: '里程碑' },
{ to: '/notifications', icon: '🔔', label: `通知${unreadCount > 0 ? ` (${unreadCount})` : ''}` },
]
return (
@@ -21,7 +39,7 @@ export default function Sidebar({ user, onLogout }: Props) {
</div>
<ul className="nav-links">
{links.map((l) => (
<li key={l.to} className={pathname === l.to ? 'active' : ''}>
<li key={l.to} className={pathname === l.to || (l.to !== '/' && pathname.startsWith(l.to)) ? 'active' : ''}>
<Link to={l.to}>{l.icon} {l.label}</Link>
</li>
))}