Skip to content

Commit 538e205

Browse files
authored
Merge pull request #12 from ZJUSCT/style
Add difficulty badge, adjust ban alert style
2 parents 301e0ca + 8f9f36c commit 538e205

File tree

18 files changed

+436
-186
lines changed

18 files changed

+436
-186
lines changed

app/(main)/contests/page.tsx

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ import { UserProfileCard } from '@/components/shared/user-profile-card';
2323
import { getInitials } from '@/lib/utils';
2424
import EchartsTrendChart from '@/components/charts/echarts-trend-chart';
2525
import { AnnouncementsCard } from '@/components/contests/announcements-card';
26-
import { UserScoreCard } from '@/components/contests/user-score-card';
26+
import { DifficultyBadge } from '@/components/contests/difficulty-badge';
27+
import UserScoreCard from '@/components/contests/user-score-card';
28+
import { Search, List } from 'lucide-react';
2729

2830
const fetcher = (url: string) => api.get(url).then(res => res.data.data);
2931

@@ -254,10 +256,10 @@ function ProblemCard({ problemId, index }: { problemId: string; index: number })
254256
const t = useTranslations('contests');
255257
const { data: problem, isLoading } = useSWR<Problem>(`/problems/${problemId}`, fetcher);
256258

257-
if (isLoading) return <Skeleton className="h-24 w-full" />;
259+
if (isLoading) return <Skeleton className="h-28 w-full" />;
258260

259261
return (
260-
<Link href={`/problems?id=${problemId}`} className="relative block overflow-hidden hover:shadow-lg transition-shadow duration-300">
262+
<Link href={`/problems?id=${problemId}`} className="relative block overflow-hidden rounded-lg hover:shadow-lg transition-shadow duration-300">
261263
<Card className="h-full">
262264
<div className="relative z-10">
263265
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
@@ -266,6 +268,7 @@ function ProblemCard({ problemId, index }: { problemId: string; index: number })
266268
</CardHeader>
267269
<CardContent>
268270
<div className="text-xs text-muted-foreground mb-4">{t('problemCard.id')}: {problemId}</div>
271+
<DifficultyBadge level={problem?.level || ""} />
269272
</CardContent>
270273
</div>
271274

@@ -275,7 +278,7 @@ function ProblemCard({ problemId, index }: { problemId: string; index: number })
275278
absolute bottom-0 right-0 z-0
276279
transform translate-x-1/5 translate-y-1/4
277280
text-9xl font-extrabold
278-
text-neutral-500/10 dark:text-white/5
281+
text-gray-100 dark:text-zinc-700
279282
pointer-events-none select-none
280283
"
281284
>
@@ -289,34 +292,52 @@ function ProblemCard({ problemId, index }: { problemId: string; index: number })
289292
function ContestProblems({ contestId }: { contestId: string }) {
290293
const t = useTranslations('contests');
291294
const { data: contest, error, isLoading } = useSWR<Contest>(`/contests/${contestId}`, fetcher);
295+
292296
if (isLoading) return <Skeleton className="h-64 w-full" />;
293297
if (error) return <div>{t('detail.loadFail')}</div>;
294298
if (!contest) return <div>{t('detail.notFound')}</div>;
299+
295300
return (
296301
<div className="space-y-6">
297302
<Card>
298-
<CardHeader><CardTitle>{t('description.title')}</CardTitle></CardHeader>
303+
<CardHeader>
304+
<div className="flex items-center space-x-2">
305+
<Search className="w-5 h-5" />
306+
<CardTitle className="font-bold">{t('description.title')}</CardTitle>
307+
</div>
308+
</CardHeader>
299309
<CardContent>
300-
<MarkdownViewer
301-
content={contest.description}
310+
<MarkdownViewer
311+
content={contest.description}
302312
assetContext="contest"
303313
assetContextId={contest.id}
304314
/>
305315
</CardContent>
306316
</Card>
317+
307318
<Card>
308319
<CardHeader>
309-
<CardTitle>{t('problems.title')}</CardTitle>
310-
<CardDescription>{contest.problem_ids.length > 0 ? t('problems.instruction') : t('problems.none')}</CardDescription>
320+
<div className="flex items-center space-x-2">
321+
<List className="w-5 h-5" />
322+
<CardTitle className="font-bold">{t('problems.title')}</CardTitle>
323+
</div>
324+
<CardDescription>
325+
{contest.problem_ids.length > 0
326+
? t('problems.instruction')
327+
: t('problems.none')}
328+
</CardDescription>
311329
</CardHeader>
312330
<CardContent className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
313-
{contest.problem_ids.map((problemId, i) => <ProblemCard key={problemId} problemId={problemId} index={i + 1} />)}
331+
{contest.problem_ids.map((problemId, i) => (
332+
<ProblemCard key={problemId} problemId={problemId} index={i + 1} />
333+
))}
314334
</CardContent>
315335
</Card>
316336
</div>
317337
);
318338
}
319339

340+
320341
function ContestTrend({ contest }: { contest: Contest }) {
321342
const t = useTranslations('contests');
322343
const { data: trendData, error, isLoading } = useSWR<TrendEntry[]>(`/contests/${contest.id}/trend`, fetcher, { refreshInterval: 30000 });
@@ -536,8 +557,8 @@ function ContestDetailView({ contestId, view }: { contestId: string, view: strin
536557
</div>
537558

538559
<div className="space-y-6 lg:sticky lg:top-20">
539-
<AnnouncementsCard contestId={contestId} />
540560
<UserScoreCard contestId={contestId} />
561+
<AnnouncementsCard contestId={contestId} />
541562
</div>
542563
</div>
543564
</div>

app/(main)/problems/page.tsx

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
1111
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
1212
import { Skeleton } from '@/components/ui/skeleton';
1313
import { format } from 'date-fns';
14-
import Link from 'next/link';
14+
import { useRouter } from 'next/navigation';
15+
import { getScoreColor } from '@/lib/utils';
16+
import { CopyButton } from '@/components/ui/shadcn-io/copy-button';
1517
import { Suspense } from 'react';
1618

1719
const fetcher = (url: string) => api.get(url).then(res => res.data.data);
1820

1921
function UserSubmissionsForProblem({ problemId }: { problemId: string }) {
22+
const router = useRouter();
2023
const t = useTranslations('ProblemDetails');
2124
const { data: allSubmissions, isLoading } = useSWR<Submission[]>('/submissions', fetcher);
2225

@@ -32,23 +35,42 @@ function UserSubmissionsForProblem({ problemId }: { problemId: string }) {
3235
<Table>
3336
<TableHeader>
3437
<TableRow>
35-
<TableHead>{t('submissions.id')}</TableHead>
3638
<TableHead>{t('submissions.status')}</TableHead>
3739
<TableHead>{t('submissions.score')}</TableHead>
3840
<TableHead>{t('submissions.date')}</TableHead>
41+
<TableHead className="text-right">{t('submissions.id')}</TableHead>
3942
</TableRow>
4043
</TableHeader>
4144
<TableBody>
4245
{problemSubmissions.map(sub => (
43-
<TableRow key={sub.id}>
46+
<TableRow key={sub.id}
47+
className="cursor-pointer transition-colors hover:bg-muted/50"
48+
onClick={() => router.push(`/submissions?id=${sub.id}`)}
49+
>
50+
<TableCell><SubmissionStatusBadge status={sub.status} /></TableCell>
4451
<TableCell>
45-
<Link href={`/submissions?id=${sub.id}`} className="font-mono text-primary hover:underline">
46-
{sub.id.substring(0, 8)}...
47-
</Link>
52+
<span
53+
className="font-bold font-mono"
54+
style={{
55+
color: getScoreColor(sub.score ?? 0),
56+
}}
57+
>
58+
{sub.score}
59+
</span>
60+
</TableCell>
61+
<TableCell>{format(new Date(sub.CreatedAt), "MM/dd HH:mm:ss")}</TableCell>
62+
<TableCell className="text-right font-mono text-sm text-muted-foreground">
63+
<div className="flex items-center justify-end space-x-2">
64+
<span className="mx-2">{sub.id.substring(0, 8)}</span>
65+
<div
66+
onClick={(e) => {
67+
e.stopPropagation();
68+
}}
69+
>
70+
<CopyButton content={sub.id} size="sm" />
71+
</div>
72+
</div>
4873
</TableCell>
49-
<TableCell><SubmissionStatusBadge status={sub.status} /></TableCell>
50-
<TableCell>{sub.score}</TableCell>
51-
<TableCell>{format(new Date(sub.CreatedAt), "Pp")}</TableCell>
5274
</TableRow>
5375
))}
5476
</TableBody>
@@ -78,7 +100,7 @@ function ProblemDetails() {
78100
if (!problem) return <div>{t('details.notFound')}</div>;
79101

80102
return (
81-
<div className="grid gap-6 lg:grid-cols-2">
103+
<div className="grid gap-6 lg:grid-cols-[6fr_4fr]">
82104
<div className="space-y-6">
83105
<Card>
84106
<CardHeader>

app/(main)/submissions/page.tsx

Lines changed: 22 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,15 @@ import { Separator } from '@/components/ui/separator';
2121
import { useTranslations } from 'next-intl';
2222
import { cn } from '@/lib/utils';
2323
import { CopyButton } from "@/components/ui/shadcn-io/copy-button";
24+
import { useRouter } from "next/navigation";
25+
import { getScoreColor } from '@/lib/utils';
2426
import MarkdownViewer from '@/components/shared/markdown-viewer';
2527

2628
const fetcher = (url: string) => api.get(url).then(res => res.data.data);
2729

28-
function getScoreColor(score: number): string {
29-
const clamp = (n: number, min: number, max: number) => Math.max(min, Math.min(n, max));
30-
const s = clamp(score, 0, 100);
31-
32-
const startHue = 0;
33-
const endHue = 130;
34-
const hue = startHue + ((endHue - startHue) * s) / 100;
35-
36-
const saturation = 50;
37-
const lightness = 50;
38-
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
39-
}
40-
4130
function MySubmissionsList() {
4231
const t = useTranslations('submissions');
32+
const router = useRouter();
4333

4434
const { data: submissions, error, isLoading } = useSWR<Submission[]>('/submissions', fetcher, {
4535
refreshInterval: 5000,
@@ -67,8 +57,7 @@ function MySubmissionsList() {
6757
return (
6858
<Card>
6959
<CardHeader>
70-
<CardTitle>{t('list.title')}</CardTitle>
71-
<CardDescription>{t('list.description')}</CardDescription>
60+
<CardTitle className="text-2xl font-bold">{t('list.title')}</CardTitle>
7261
</CardHeader>
7362
<CardContent>
7463
<Table>
@@ -118,47 +107,37 @@ function MySubmissionsList() {
118107
<TableRow
119108
key={sub.id}
120109
className={cn(
121-
"transition-colors hover:bg-muted/50"
110+
"cursor-pointer transition-colors hover:bg-muted/50"
122111
)}
112+
onClick={() => router.push(`/submissions?id=${sub.id}`)}
123113
>
124114
<TableCell>
125-
<Link href={`/submissions?id=${sub.id}`} >
126-
<SubmissionStatusBadge status={sub.status} />
127-
</Link>
115+
<SubmissionStatusBadge status={sub.status} />
128116
</TableCell>
129117
<TableCell>
130-
<Link href={`/submissions?id=${sub.id}`} >
131-
<span
132-
className="font-semibold font-mono"
133-
style={{
134-
color: getScoreColor(sub.score ?? 0),
135-
}}
136-
>
137-
{sub.score}
138-
</span>
139-
</Link>
118+
<span
119+
className="font-bold font-mono"
120+
style={{
121+
color: getScoreColor(sub.score ?? 0),
122+
}}
123+
>
124+
{sub.score}
125+
</span>
140126
</TableCell>
141127
<TableCell>
142-
<Link href={`/problems?id=${sub.problem_id}`} >
143-
<span className="text-primary hover:underline">{sub.problem_id}</span>
144-
</Link>
128+
{sub.problem_id}
145129
</TableCell>
130+
<TableCell>{sub.node}</TableCell>
146131
<TableCell>
147-
<Link href={`/submissions?id=${sub.id}`} >
148-
{sub.node}
149-
</Link>
132+
{format(new Date(sub.CreatedAt), "MM/dd HH:mm:ss")}
150133
</TableCell>
151-
<TableCell>
152-
<Link href={`/submissions?id=${sub.id}`} >
153-
{format(new Date(sub.CreatedAt), "MM/dd HH:mm:ss")}
154-
</Link>
155-
</TableCell>
156134
<TableCell className="text-right font-mono text-sm text-muted-foreground">
157135
<div className="flex items-center justify-end space-x-2">
158136
<span className="mx-2">{sub.id.substring(0, 8)}</span>
159137
<div
160-
onClick={(e) => e.stopPropagation()}
161-
onPointerDown={(e) => e.stopPropagation()}
138+
onClick={(e) => {
139+
e.stopPropagation();
140+
}}
162141
>
163142
<CopyButton content={sub.id} size="sm" />
164143
</div>
@@ -168,7 +147,7 @@ function MySubmissionsList() {
168147
))
169148
) : (
170149
<TableRow>
171-
<TableCell colSpan={5} className="text-center">
150+
<TableCell colSpan={6} className="text-center">
172151
{t('list.none')}
173152
</TableCell>
174153
</TableRow>

app/globals.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
@layer base {
66
:root {
7-
--background: 0 0% 100%;
7+
--background: 0 0% 99%;
88
--foreground: 222.2 84% 4.9%;
99

1010
--card: 0 0% 100%;

components/contests/announcements-card.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ export function AnnouncementsCard({ contestId }: { contestId: string }) {
4747
)}
4848
{error && <p className="text-sm text-destructive">{t('loadFail')}</p>}
4949
{!isLoading && announcements && announcements.length > 0 ? (
50-
<div className="space-y-6">
50+
<div className="space-y-3">
5151
{announcements.map((ann, index) => (
5252
<div key={ann.id}>
5353
<div className="space-y-1">
5454
<h3 className="font-semibold">{ann.title}</h3>
5555
<p className="text-xs text-muted-foreground" title={format(new Date(ann.created_at), 'Pp', { locale: locales[locale] || enUS })}>
5656
{formatDistanceToNow(new Date(ann.created_at), { addSuffix: true, locale: locales[locale] || enUS })}
5757
</p>
58-
<div className="pt-2">
58+
<div>
5959
<MarkdownViewer content={ann.description} />
6060
</div>
6161
</div>

0 commit comments

Comments
 (0)