Skip to content

Commit fe302db

Browse files
Yinch-panclaude
andcommitted
feat: redesign frontend UI with red-white theme inspired by HuggingFace
Overhaul the entire frontend color scheme from dark charcoal/amber to a clean red-white palette. Redesign Header (sticky glassmorphism, mobile menu), Footer (multi-column), HomePage (gradient hero, sidebar quick links), and all list/detail pages with consistent pill filters, hover-lift cards, and theme-aware CSS variables. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4bddce8 commit fe302db

38 files changed

Lines changed: 938 additions & 678 deletions

frontend/src/components/bounty/BountyCard.tsx

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import { Clock } from 'lucide-react'
2+
13
import { Badge } from '@/components/ui/badge'
2-
import { Button } from '@/components/ui/button'
34
import { Card, CardContent } from '@/components/ui/card'
45
import type { BountySummary } from '@/lib/bounties'
56
import { formatCurrency, formatDate } from '@/lib/utils'
@@ -21,6 +22,13 @@ const STATUS_LABELS: Record<string, string> = {
2122
CANCELLED: '已取消',
2223
}
2324

25+
const STATUS_STYLES: Record<string, string> = {
26+
OPEN: 'border-emerald-200 bg-emerald-50 text-emerald-700',
27+
IN_PROGRESS: 'border-blue-200 bg-blue-50 text-blue-700',
28+
COMPLETED: 'border-gray-200 bg-gray-50 text-gray-700',
29+
ARBITRATING: 'border-amber-200 bg-amber-50 text-amber-700',
30+
}
31+
2432
const TYPE_LABELS: Record<string, string> = {
2533
SKILL_CUSTOM: 'Skill 定制',
2634
DATA_PROCESSING: '数据处理',
@@ -31,32 +39,33 @@ const TYPE_LABELS: Record<string, string> = {
3139

3240
export default function BountyCard({ bounty, onClick }: BountyCardProps) {
3341
return (
34-
<Card className="cursor-pointer border-border/70 transition-shadow hover:shadow-md" onClick={onClick}>
35-
<CardContent className="space-y-4 p-5">
42+
<Card className="group cursor-pointer transition-all hover:-translate-y-0.5 hover:shadow-md" onClick={onClick}>
43+
<CardContent className="space-y-3 p-5">
3644
<div className="flex items-center justify-between gap-3">
37-
<Badge variant="secondary">{TYPE_LABELS[bounty.bounty_type] || bounty.bounty_type}</Badge>
38-
<span className="text-lg font-semibold text-emerald-600">{formatCurrency(bounty.reward)}</span>
45+
<Badge variant="secondary" className="text-xs">{TYPE_LABELS[bounty.bounty_type] || bounty.bounty_type}</Badge>
46+
<span className="text-base font-semibold text-primary">{formatCurrency(bounty.reward)}</span>
3947
</div>
4048

41-
<div className="space-y-2">
42-
<h3 className="line-clamp-1 text-lg font-semibold">{bounty.title}</h3>
43-
<p className="line-clamp-3 text-sm text-muted-foreground">{bounty.description}</p>
49+
<div className="space-y-1.5">
50+
<h3 className="line-clamp-1 text-base font-semibold group-hover:text-primary">{bounty.title}</h3>
51+
<p className="line-clamp-2 text-sm leading-relaxed text-muted-foreground">{bounty.description}</p>
4452
</div>
4553

46-
<div className="flex flex-wrap gap-2">
47-
<Badge variant="outline">{STATUS_LABELS[bounty.status] || bounty.status}</Badge>
48-
{bounty.is_cold ? <Badge className="bg-amber-100 text-amber-800 hover:bg-amber-100">冷门</Badge> : null}
49-
<Badge variant="outline">{bounty.application_count} 人申请</Badge>
54+
<div className="flex flex-wrap gap-1.5">
55+
<Badge variant="outline" className={STATUS_STYLES[bounty.status] || ''}>
56+
{STATUS_LABELS[bounty.status] || bounty.status}
57+
</Badge>
58+
{bounty.is_cold && <Badge variant="outline" className="border-amber-200 bg-amber-50 text-amber-700">冷门</Badge>}
59+
<Badge variant="outline" className="text-xs">{bounty.application_count} 人申请</Badge>
5060
</div>
5161

52-
<div className="flex items-center justify-between text-sm text-muted-foreground">
62+
<div className="flex items-center justify-between border-t pt-3 text-xs text-muted-foreground">
5363
<span>{bounty.creator.display_name}</span>
54-
<span>截止 {formatDate(bounty.deadline)}</span>
64+
<span className="inline-flex items-center gap-1">
65+
<Clock className="h-3 w-3" />
66+
{formatDate(bounty.deadline)}
67+
</span>
5568
</div>
56-
57-
<Button className="w-full" variant="outline">
58-
查看详情
59-
</Button>
6069
</CardContent>
6170
</Card>
6271
)

frontend/src/components/layout/AdminLayout.tsx

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,17 @@ const NAV_ITEMS = [
2626
export function AdminLayout() {
2727
const { user, isAuthenticated } = useAuth();
2828

29-
// Route guard: require admin role
3029
if (!isAuthenticated) {
3130
return <Navigate to="/login" replace />;
3231
}
3332

3433
if (user?.role !== 'ADMIN' && user?.role !== 'MODERATOR') {
3534
return (
36-
<div className="flex items-center justify-center min-h-[60vh]">
37-
<div className="text-center space-y-4">
38-
<ShieldAlert className="h-16 w-16 mx-auto text-muted-foreground" />
39-
<h1 className="text-2xl font-bold">权限不足</h1>
40-
<p className="text-muted-foreground">
35+
<div className="flex min-h-[60vh] items-center justify-center">
36+
<div className="space-y-3 text-center">
37+
<ShieldAlert className="mx-auto h-12 w-12 text-muted-foreground" />
38+
<h1 className="text-xl font-bold">权限不足</h1>
39+
<p className="text-sm text-muted-foreground">
4140
您没有访问管理后台的权限,请联系管理员。
4241
</p>
4342
</div>
@@ -47,26 +46,25 @@ export function AdminLayout() {
4746

4847
return (
4948
<div className="flex min-h-[calc(100vh-8rem)]">
50-
{/* Sidebar */}
51-
<aside className="w-56 border-r bg-muted/30 shrink-0">
52-
<div className="p-4 border-b">
53-
<h2 className="font-semibold text-lg">管理后台</h2>
54-
<p className="text-xs text-muted-foreground mt-1">
49+
<aside className="w-52 shrink-0 border-r bg-muted/20">
50+
<div className="border-b p-4">
51+
<h2 className="text-sm font-semibold">管理后台</h2>
52+
<p className="mt-0.5 text-xs text-muted-foreground">
5553
{user?.role === 'ADMIN' ? '管理员' : '版主'}
5654
</p>
5755
</div>
58-
<nav className="p-2 space-y-1">
56+
<nav className="space-y-0.5 p-2">
5957
{NAV_ITEMS.map((item) => (
6058
<NavLink
6159
key={item.to}
6260
to={item.to}
6361
end={item.end}
6462
className={({ isActive }) =>
6563
cn(
66-
'flex items-center gap-3 px-3 py-2 rounded-md text-sm transition-colors',
64+
'flex items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors',
6765
isActive
6866
? 'bg-primary text-primary-foreground'
69-
: 'text-muted-foreground hover:text-foreground hover:bg-muted',
67+
: 'text-muted-foreground hover:bg-muted hover:text-foreground',
7068
)
7169
}
7270
>
@@ -77,8 +75,7 @@ export function AdminLayout() {
7775
</nav>
7876
</aside>
7977

80-
{/* Main content */}
81-
<div className="flex-1 p-6 overflow-auto">
78+
<div className="flex-1 overflow-auto p-6">
8279
<Outlet />
8380
</div>
8481
</div>
Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,61 @@
1+
import { Link } from 'react-router-dom'
2+
3+
const FOOTER_LINKS = [
4+
{
5+
title: '平台',
6+
links: [
7+
{ label: '技能市场', to: '/marketplace' },
8+
{ label: '悬赏任务', to: '/bounty' },
9+
{ label: '知识工坊', to: '/workshop' },
10+
{ label: '排行榜', to: '/rankings/credit' },
11+
],
12+
},
13+
{
14+
title: '社区',
15+
links: [
16+
{ label: '关于我们', to: '#' },
17+
{ label: '使用条款', to: '#' },
18+
{ label: '隐私政策', to: '#' },
19+
],
20+
},
21+
]
22+
123
export function Footer() {
224
return (
3-
<footer className="border-t py-6 text-center text-sm text-gray-500">
4-
© 2026 CaMeL Community
25+
<footer className="border-t bg-muted/30">
26+
<div className="mx-auto max-w-7xl px-4 py-10 sm:px-6">
27+
<div className="grid gap-8 sm:grid-cols-3">
28+
<div className="space-y-3">
29+
<div className="flex items-center gap-2 text-lg font-bold">
30+
<span className="inline-flex h-6 w-6 items-center justify-center rounded-md bg-primary text-[10px] font-black text-white">C</span>
31+
CaMeL Community
32+
</div>
33+
<p className="text-sm leading-6 text-muted-foreground">
34+
AI 社区平台 — 技能交易、悬赏任务、知识沉淀
35+
</p>
36+
</div>
37+
{FOOTER_LINKS.map((group) => (
38+
<div key={group.title}>
39+
<h3 className="mb-3 text-sm font-semibold">{group.title}</h3>
40+
<ul className="space-y-2">
41+
{group.links.map((link) => (
42+
<li key={link.label}>
43+
<Link
44+
to={link.to}
45+
className="text-sm text-muted-foreground transition-colors hover:text-foreground"
46+
>
47+
{link.label}
48+
</Link>
49+
</li>
50+
))}
51+
</ul>
52+
</div>
53+
))}
54+
</div>
55+
<div className="mt-8 border-t pt-6 text-center text-xs text-muted-foreground">
56+
&copy; {new Date().getFullYear()} CaMeL Community. All rights reserved.
57+
</div>
58+
</div>
559
</footer>
660
)
761
}

0 commit comments

Comments
 (0)