Skip to content

Commit 0a7c3dd

Browse files
committed
i18n: add translation for submissions
1 parent e7151f5 commit 0a7c3dd

File tree

1 file changed

+45
-37
lines changed

1 file changed

+45
-37
lines changed

app/(main)/submissions/page.tsx

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,22 @@ import { Progress } from "@/components/ui/progress";
1616
import { Button } from '@/components/ui/button';
1717
import { useToast } from '@/hooks/use-toast';
1818
import { Separator } from '@/components/ui/separator';
19+
import { useTranslations } from 'next-intl';
1920

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

2223
// Component for the list of submissions
2324
function MySubmissionsList() {
25+
const t = useTranslations('submissions');
2426
const { data: submissions, error, isLoading } = useSWR<Submission[]>('/submissions', fetcher, {
2527
refreshInterval: 5000
2628
});
2729

2830
if (isLoading) return (
2931
<Card>
3032
<CardHeader>
31-
<CardTitle>My Submissions</CardTitle>
32-
<CardDescription>A list of all your submissions.</CardDescription>
33+
<CardTitle>{t('list.title')}</CardTitle>
34+
<CardDescription>{t('list.description')}</CardDescription>
3335
</CardHeader>
3436
<CardContent>
3537
<div className="space-y-2">
@@ -38,23 +40,23 @@ function MySubmissionsList() {
3840
</CardContent>
3941
</Card>
4042
);
41-
if (error) return <div>Failed to load submissions.</div>;
43+
if (error) return <div>{t('list.loadFail')}</div>;
4244

4345
return (
4446
<Card>
4547
<CardHeader>
46-
<CardTitle>My Submissions</CardTitle>
47-
<CardDescription>A list of all your submissions.</CardDescription>
48+
<CardTitle>{t('list.title')}</CardTitle>
49+
<CardDescription>{t('list.description')}</CardDescription>
4850
</CardHeader>
4951
<CardContent>
5052
<Table>
5153
<TableHeader>
5254
<TableRow>
53-
<TableHead>ID</TableHead>
54-
<TableHead>Problem ID</TableHead>
55-
<TableHead>Status</TableHead>
56-
<TableHead>Score</TableHead>
57-
<TableHead>Submitted At</TableHead>
55+
<TableHead>{t('list.table.id')}</TableHead>
56+
<TableHead>{t('list.table.problemId')}</TableHead>
57+
<TableHead>{t('list.table.status')}</TableHead>
58+
<TableHead>{t('list.table.score')}</TableHead>
59+
<TableHead>{t('list.table.submittedAt')}</TableHead>
5860
</TableRow>
5961
</TableHeader>
6062
<TableBody>
@@ -78,7 +80,7 @@ function MySubmissionsList() {
7880
))
7981
) : (
8082
<TableRow>
81-
<TableCell colSpan={5} className="text-center">No submissions yet.</TableCell>
83+
<TableCell colSpan={5} className="text-center">{t('list.none')}</TableCell>
8284
</TableRow>
8385
)}
8486
</TableBody>
@@ -89,43 +91,45 @@ function MySubmissionsList() {
8991
}
9092

9193
function QueuePosition({ submissionId, cluster }: { submissionId: string, cluster: string }) {
94+
const t = useTranslations('submissions');
9295
const { data } = useSWR<{ position: number }>(`/submissions/${submissionId}/queue_position`, fetcher, { refreshInterval: 3000 });
9396

9497
if (data === undefined) return null;
9598

9699
return (
97100
<div className="flex items-center justify-between text-sm text-blue-500">
98-
<span className="text-muted-foreground flex items-center gap-2"><Loader2 className="h-4 w-4 animate-spin" />Queue Position</span>
99-
<span>#{data.position + 1} in {cluster} queue</span>
101+
<span className="text-muted-foreground flex items-center gap-2"><Loader2 className="h-4 w-4 animate-spin" />{t('details.queue.position')}</span>
102+
<span>{t('details.queue.info', { position: data.position + 1, cluster })}</span>
100103
</div>
101104
);
102105
}
103106

104107

105-
// --- [修改] Component for submission details ---
108+
// Component for submission details
106109
function SubmissionDetails({ submissionId }: { submissionId: string }) {
110+
const t = useTranslations('submissions');
107111
const { toast } = useToast();
108112
const { data: submission, error, isLoading, mutate } = useSWR<Submission>(`/submissions/${submissionId}`, fetcher, {
109113
refreshInterval: (data) => (data?.status === 'Queued' || data?.status === 'Running' ? 2000 : 0),
110114
});
111115
const { data: problem } = useSWR<Problem>(submission ? `/problems/${submission.problem_id}` : null, fetcher);
112116

113117
if (isLoading) return <SubmissionDetailsSkeleton />;
114-
if (error) return <div>Failed to load submission.</div>;
115-
if (!submission) return <div>Submission not found.</div>;
118+
if (error) return <div>{t('details.loadFail')}</div>;
119+
if (!submission) return <div>{t('details.notFound')}</div>;
116120

117121
const totalSteps = problem?.workflow.length ?? 0;
118122
const progress = totalSteps > 0 ? ((submission.current_step + 1) / totalSteps) * 100 : 0;
119123
const canBeInterrupted = submission.status === 'Queued' || submission.status === 'Running';
120124

121125
const handleInterrupt = async () => {
122-
if (!confirm('Are you sure you want to interrupt this submission? This action cannot be undone.')) return;
126+
if (!confirm(t('details.interrupt.confirm'))) return;
123127
try {
124128
await api.post(`/submissions/${submissionId}/interrupt`);
125-
toast({ title: 'Success', description: 'Submission interruption request sent.' });
129+
toast({ title: t('details.interrupt.successTitle'), description: t('details.interrupt.successDescription') });
126130
mutate();
127131
} catch (err: any) {
128-
toast({ variant: 'destructive', title: 'Error', description: err.response?.data?.message || 'Failed to interrupt submission.' });
132+
toast({ variant: 'destructive', title: t('details.interrupt.failTitle'), description: err.response?.data?.message || t('details.interrupt.failDefault') });
129133
}
130134
}
131135

@@ -134,78 +138,81 @@ function SubmissionDetails({ submissionId }: { submissionId: string }) {
134138
<div className="lg:col-span-2">
135139
<Card>
136140
<CardHeader>
137-
<CardTitle>Live Log</CardTitle>
138-
<CardDescription>Real-time output from the judge. Select a step to view its log.</CardDescription>
141+
<CardTitle>{t('details.log.title')}</CardTitle>
142+
<CardDescription>{t('details.log.description')}</CardDescription>
139143
</CardHeader>
140144
<CardContent>
141145
{problem && submission ? <SubmissionLogViewer submission={submission} problem={problem} onStatusUpdate={mutate} /> : <Skeleton className="h-96 w-full" />}
142146
</CardContent>
143147
</Card>
144148
</div>
145149

146-
{/* --- [修改] 右侧合并为一个卡片 --- */}
147150
<div className="space-y-6">
148151
<Card>
149152
<CardHeader>
150153
<div className="flex items-center justify-between">
151-
<CardTitle>Submission Info</CardTitle>
154+
<CardTitle>{t('details.info.title')}</CardTitle>
152155
{canBeInterrupted && (
153156
<Button variant="destructive" size="sm" onClick={handleInterrupt}>
154-
<XCircle /> Interrupt
157+
<XCircle className="h-4 w-4 mr-1" /> {t('details.interrupt.button')}
155158
</Button>
156159
)}
157160
</div>
158161
</CardHeader>
159162
<CardContent className="space-y-4 text-sm">
160163
{/* --- Submission Details Section --- */}
161164
<div className="flex items-center justify-between">
162-
<span className="text-muted-foreground flex items-center gap-2"><Hash className="h-4 w-4"/>Status</span>
165+
<span className="text-muted-foreground flex items-center gap-2"><Hash className="h-4 w-4"/>{t('details.info.status')}</span>
163166
<SubmissionStatusBadge status={submission.status} />
164167
</div>
165168
{submission.status === 'Queued' && <QueuePosition submissionId={submission.id} cluster={submission.cluster} />}
166169
{(submission.status === 'Running') && totalSteps > 0 && (
167170
<div>
168171
<Progress value={progress} className="w-full" />
169-
<p className="text-xs text-muted-foreground mt-1">Step {submission.current_step + 1} of {totalSteps}: {problem?.workflow[submission.current_step]?.name}</p>
172+
<p className="text-xs text-muted-foreground mt-1">{t('details.info.stepProgress', {
173+
current: submission.current_step + 1,
174+
total: totalSteps,
175+
name: problem?.workflow[submission.current_step]?.name ?? ''
176+
})}</p>
170177
</div>
171178
)}
172179
<div className="flex items-center justify-between">
173-
<span className="text-muted-foreground flex items-center gap-2"><Tag className="h-4 w-4"/>Score</span>
180+
<span className="text-muted-foreground flex items-center gap-2"><Tag className="h-4 w-4"/>{t('details.info.score')}</span>
174181
<span className="font-mono text-lg">{submission.score}</span>
175182
</div>
176183
<div className="flex items-center justify-between">
177-
<span className="text-muted-foreground flex items-center gap-2"><Clock className="h-4 w-4"/>Submitted</span>
184+
<span className="text-muted-foreground flex items-center gap-2"><Clock className="h-4 w-4"/>{t('details.info.submitted')}</span>
178185
<span>{formatDistanceToNow(new Date(submission.CreatedAt), { addSuffix: true })}</span>
179186
</div>
180187
<div className="flex items-center justify-between">
181-
<span className="text-muted-foreground flex items-center gap-2"><Code className="h-4 w-4"/>Problem</span>
188+
<span className="text-muted-foreground flex items-center gap-2"><Code className="h-4 w-4"/>{t('details.info.problem')}</span>
182189
<Link href={`/problems?id=${submission.problem_id}`} className="text-primary hover:underline">
183190
{submission.problem_id}
184191
</Link>
185192
</div>
186193
<div className="flex items-center justify-between">
187-
<span className="text-muted-foreground flex items-center gap-2"><User className="h-4 w-4"/>User</span>
194+
<span className="text-muted-foreground flex items-center gap-2"><User className="h-4 w-4"/>{t('details.info.user')}</span>
188195
<span>{submission.user.nickname}</span>
189196
</div>
190197
<div className="flex items-center justify-between">
191-
<span className="text-muted-foreground flex items-center gap-2"><Layers className="h-4 w-4"/>Cluster</span>
198+
<span className="text-muted-foreground flex items-center gap-2"><Layers className="h-4 w-4"/>{t('details.info.cluster')}</span>
192199
<span>{submission.cluster}</span>
193200
</div>
194201
<div className="flex items-center justify-between">
195-
<span className="text-muted-foreground flex items-center gap-2"><Server className="h-4 w-4"/>Node</span>
202+
<span className="text-muted-foreground flex items-center gap-2"><Server className="h-4 w-4"/>{t('details.info.node')}</span>
196203
<span>{submission.node || 'N/A'}</span>
197204
</div>
198205

199-
{/* --- [新增] Judge Info Section (conditionally rendered) --- */}
206+
{/* --- Judge Info Section (conditionally rendered) --- */}
200207
{submission.info && Object.keys(submission.info).length > 0 && (
201208
<>
202209
<Separator className="my-4" />
203210
<div className="space-y-2">
204-
<h3 className="font-semibold tracking-tight">Judge Info</h3>
211+
<h3 className="font-semibold tracking-tight">{t('details.judgeInfo.title')}</h3>
205212
<pre className="p-4 bg-muted rounded-md text-xs overflow-auto">
206213
{JSON.stringify(submission.info, null, 2)}
207214
</pre>
208-
<p className="text-xs text-muted-foreground">This is the raw JSON output from the final step of the judging process.</p>
215+
<p className="text-xs text-muted-foreground">{t('details.judgeInfo.description')}</p>
209216
</div>
210217
</>
211218
)}
@@ -218,6 +225,7 @@ function SubmissionDetails({ submissionId }: { submissionId: string }) {
218225

219226

220227
function SubmissionDetailsSkeleton() {
228+
const t = useTranslations('submissions');
221229
return (
222230
<div className="grid gap-6 lg:grid-cols-3">
223231
<div className="lg:col-span-2 space-y-6">
@@ -236,7 +244,7 @@ function SubmissionDetailsSkeleton() {
236244
</div>
237245
<div className="space-y-6">
238246
<Card>
239-
<CardHeader><Skeleton className="h-6 w-3/4" /></CardHeader>
247+
<CardHeader><CardTitle>{t('details.info.title')}</CardTitle></CardHeader>
240248
<CardContent className="space-y-4">
241249
{[...Array(6)].map((_, i) => (
242250
<div key={i} className="flex justify-between">
@@ -269,4 +277,4 @@ export default function MySubmissionsPage() {
269277
<SubmissionsPageContent />
270278
</Suspense>
271279
);
272-
}
280+
}

0 commit comments

Comments
 (0)