@@ -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,42 +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
108+ // Component for submission details
105109function SubmissionDetails ( { submissionId } : { submissionId : string } ) {
110+ const t = useTranslations ( 'submissions' ) ;
106111 const { toast } = useToast ( ) ;
107112 const { data : submission , error, isLoading, mutate } = useSWR < Submission > ( `/submissions/${ submissionId } ` , fetcher , {
108113 refreshInterval : ( data ) => ( data ?. status === 'Queued' || data ?. status === 'Running' ? 2000 : 0 ) ,
109114 } ) ;
110115 const { data : problem } = useSWR < Problem > ( submission ? `/problems/${ submission . problem_id } ` : null , fetcher ) ;
111116
112117 if ( isLoading ) return < SubmissionDetailsSkeleton /> ;
113- if ( error ) return < div > Failed to load submission. </ div > ;
114- 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 > ;
115120
116121 const totalSteps = problem ?. workflow . length ?? 0 ;
117122 const progress = totalSteps > 0 ? ( ( submission . current_step + 1 ) / totalSteps ) * 100 : 0 ;
118123 const canBeInterrupted = submission . status === 'Queued' || submission . status === 'Running' ;
119124
120125 const handleInterrupt = async ( ) => {
121- 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 ;
122127 try {
123128 await api . post ( `/submissions/${ submissionId } /interrupt` ) ;
124- toast ( { title : 'Success' , description : 'Submission interruption request sent.' } ) ;
129+ toast ( { title : t ( 'details.interrupt.successTitle' ) , description : t ( 'details.interrupt.successDescription' ) } ) ;
125130 mutate ( ) ;
126131 } catch ( err : any ) {
127- 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' ) } ) ;
128133 }
129134 }
130135
@@ -133,8 +138,8 @@ function SubmissionDetails({ submissionId }: { submissionId: string }) {
133138 < div className = "lg:col-span-2" >
134139 < Card >
135140 < CardHeader >
136- < CardTitle > Live Log </ CardTitle >
137- < 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 >
138143 </ CardHeader >
139144 < CardContent >
140145 { problem && submission ? < SubmissionLogViewer submission = { submission } problem = { problem } onStatusUpdate = { mutate } /> : < Skeleton className = "h-[60vh] w-full" /> }
@@ -146,63 +151,68 @@ function SubmissionDetails({ submissionId }: { submissionId: string }) {
146151 < Card >
147152 < CardHeader >
148153 < div className = "flex items-center justify-between" >
149- < CardTitle > Submission Info </ CardTitle >
154+ < CardTitle > { t ( 'details.info.title' ) } </ CardTitle >
150155 { canBeInterrupted && (
151156 < Button variant = "destructive" size = "sm" onClick = { handleInterrupt } >
152- < XCircle /> Interrupt
157+ < XCircle className = "h-4 w-4 mr-1" /> { t ( 'details.interrupt.button' ) }
153158 </ Button >
154159 ) }
155160 </ div >
156161 </ CardHeader >
157162 < CardContent className = "space-y-4 text-sm" >
158163 { /* --- Submission Details Section --- */ }
159164 < div className = "flex items-center justify-between" >
160- < 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 >
161166 < SubmissionStatusBadge status = { submission . status } />
162167 </ div >
163168 { submission . status === 'Queued' && < QueuePosition submissionId = { submission . id } cluster = { submission . cluster } /> }
164169 { ( submission . status === 'Running' ) && totalSteps > 0 && (
165170 < div >
166171 < Progress value = { progress } className = "w-full" />
167- < 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 >
168177 </ div >
169178 ) }
170179 < div className = "flex items-center justify-between" >
171- < 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 >
172181 < span className = "font-mono text-lg" > { submission . score } </ span >
173182 </ div >
174183 < div className = "flex items-center justify-between" >
175- < 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 >
176185 < span > { formatDistanceToNow ( new Date ( submission . CreatedAt ) , { addSuffix : true } ) } </ span >
177186 </ div >
178187 < div className = "flex items-center justify-between" >
179- < 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 >
180189 < Link href = { `/problems?id=${ submission . problem_id } ` } className = "text-primary hover:underline" >
181190 { submission . problem_id }
182191 </ Link >
183192 </ div >
184193 < div className = "flex items-center justify-between" >
185- < 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 >
186195 < span > { submission . user . nickname } </ span >
187196 </ div >
188197 < div className = "flex items-center justify-between" >
189- < 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 >
190199 < span > { submission . cluster } </ span >
191200 </ div >
192201 < div className = "flex items-center justify-between" >
193- < 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 >
194203 < span > { submission . node || 'N/A' } </ span >
195204 </ div >
196205
206+ { /* --- Judge Info Section (conditionally rendered) --- */ }
197207 { submission . info && Object . keys ( submission . info ) . length > 0 && (
198208 < >
199209 < Separator className = "my-4" />
200210 < div className = "space-y-2" >
201- < h3 className = "font-semibold tracking-tight" > Judge Info </ h3 >
211+ < h3 className = "font-semibold tracking-tight" > { t ( 'details.judgeInfo.title' ) } </ h3 >
202212 < pre className = "p-4 bg-muted rounded-md text-xs overflow-auto" >
203213 { JSON . stringify ( submission . info , null , 2 ) }
204214 </ pre >
205- < 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 >
206216 </ div >
207217 </ >
208218 ) }
@@ -215,6 +225,7 @@ function SubmissionDetails({ submissionId }: { submissionId: string }) {
215225
216226
217227function SubmissionDetailsSkeleton ( ) {
228+ const t = useTranslations ( 'submissions' ) ;
218229 return (
219230 < div className = "grid gap-6 lg:grid-cols-3" >
220231 < div className = "lg:col-span-2 space-y-6" >
@@ -233,7 +244,7 @@ function SubmissionDetailsSkeleton() {
233244 </ div >
234245 < div className = "space-y-6" >
235246 < Card >
236- < CardHeader > < Skeleton className = "h-6 w-3/4" / ></ CardHeader >
247+ < CardHeader > < CardTitle > { t ( 'details.info.title' ) } </ CardTitle > </ CardHeader >
237248 < CardContent className = "space-y-4" >
238249 { [ ...Array ( 6 ) ] . map ( ( _ , i ) => (
239250 < div key = { i } className = "flex justify-between" >
@@ -266,4 +277,4 @@ export default function MySubmissionsPage() {
266277 < SubmissionsPageContent />
267278 </ Suspense >
268279 ) ;
269- }
280+ }
0 commit comments