@@ -16,20 +16,22 @@ import { Progress } from "@/components/ui/progress";
1616import { Button } from '@/components/ui/button' ;
1717import { useToast } from '@/hooks/use-toast' ;
1818import { Separator } from '@/components/ui/separator' ;
19+ import { useTranslations } from 'next-intl' ;
1920
2021const fetcher = ( url : string ) => api . get ( url ) . then ( res => res . data . data ) ;
2122
2223// Component for the list of submissions
2324function 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
9193function 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
106109function 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
220227function 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