Skip to content

Commit 80c7f2f

Browse files
authored
fix(hackathons): always open submissions in a new tab (#568)
* fix(hackathons): single-column teams tab and primary-colored pager Revert the teams tab grid to a single column and rework the shared Pagination component to match the icon-chevron layout used by the organizer submissions and participants pages, styled with the primary color. * feat(submissions): link submission card avatars to profile pages Wrap the individual avatar on SubmissionCard in a profile link and forward team-member usernames to GroupAvatar so each clustered avatar opens that user's profile in a new tab. * fix(hackathons): always open submissions in a new tab Across the hackathon, organizer, judge, and profile surfaces, clicking a submission now opens the project page in a new tab so reviewers and participants do not lose their list/queue context. Switched the remaining anchor tags to next/link Link components.
1 parent 1269fdf commit 80c7f2f

10 files changed

Lines changed: 45 additions & 21 deletions

File tree

app/(landing)/hackathons/[slug]/components/sidebar/MySubmissionPanel.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ export default function MySubmissionPanel() {
9292
status !== 'WITHDRAWN';
9393

9494
const handleView = () => {
95-
router.push(`/projects/${submission.id}?type=submission`);
95+
window.open(
96+
`/projects/${submission.id}?type=submission`,
97+
'_blank',
98+
'noopener,noreferrer'
99+
);
96100
};
97101

98102
const handleEdit = () => {

app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,15 @@ const SubmissionCard = ({ submission }: SubmissionCardProps) => {
194194
</>
195195
)}
196196

197-
<a href={projectUrl} target='_blank' rel='noopener noreferrer'>
197+
<Link href={projectUrl} target='_blank' rel='noopener noreferrer'>
198198
<BoundlessButton
199199
variant='outline'
200200
size='sm'
201201
className='hover:bg-primary h-9 rounded-xl border-white/5 bg-white/5 px-4 text-xs font-bold transition-all hover:text-black'
202202
>
203203
View Project
204204
</BoundlessButton>
205-
</a>
205+
</Link>
206206
</div>
207207
</div>
208208
</div>

app/(landing)/hackathons/[slug]/components/tabs/contents/winners/GeneralWinnerCard.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Link from 'next/link';
12
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
23
import { HackathonWinner } from '@/lib/api/hackathons';
34
import { SubmissionCardProps } from '@/types/hackathon';
@@ -14,7 +15,7 @@ export const GeneralWinnerCard = ({
1415
const projectUrl = `/projects/${winner.submissionId}?type=submission`;
1516

1617
return (
17-
<a
18+
<Link
1819
href={projectUrl}
1920
target='_blank'
2021
rel='noopener noreferrer'
@@ -35,6 +36,6 @@ export const GeneralWinnerCard = ({
3536
</div>
3637

3738
<div className='text-primary text-xs font-bold'>{winner.prize}</div>
38-
</a>
39+
</Link>
3940
);
4041
};

app/(landing)/hackathons/[slug]/components/tabs/contents/winners/PodiumWinnerCard.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Link from 'next/link';
12
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
23
import { HackathonWinner } from '@/lib/api/hackathons';
34
import { Trophy } from 'lucide-react';
@@ -53,15 +54,15 @@ export const PodiumWinnerCard = ({
5354
</div>
5455
</div>
5556

56-
<a href={projectUrl} target='_blank' rel='noopener noreferrer'>
57+
<Link href={projectUrl} target='_blank' rel='noopener noreferrer'>
5758
<BoundlessButton
5859
variant='outline'
5960
size='sm'
6061
className='hover:bg-primary h-8 rounded-lg border-white/5 bg-white/5 px-3 text-[10px] font-bold transition-all hover:text-black'
6162
>
6263
View Project
6364
</BoundlessButton>
64-
</a>
65+
</Link>
6566
</div>
6667
</div>
6768
);

app/(landing)/hackathons/[slug]/components/tabs/contents/winners/TopWinnerCard.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Link from 'next/link';
12
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
23
import { HackathonWinner } from '@/lib/api/hackathons';
34
import { Trophy } from 'lucide-react';
@@ -79,15 +80,15 @@ export const TopWinnerCard = ({ winner, submission }: TopWinnerCardProps) => {
7980
</div>
8081
</div>
8182

82-
<a href={projectUrl} target='_blank' rel='noopener noreferrer'>
83+
<Link href={projectUrl} target='_blank' rel='noopener noreferrer'>
8384
<BoundlessButton
8485
variant='outline'
8586
size='sm'
8687
className='hover:bg-primary h-9 rounded-xl border-white/5 bg-white/5 px-4 text-xs font-bold transition-all hover:text-black'
8788
>
8889
View Project
8990
</BoundlessButton>
90-
</a>
91+
</Link>
9192
</div>
9293
</div>
9394
</div>

app/judge/[hackathonId]/submissions/page.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

33
import Link from 'next/link';
4-
import { useParams, useRouter } from 'next/navigation';
4+
import { useParams } from 'next/navigation';
55
import { useEffect, useMemo, useState } from 'react';
66
import {
77
CheckCircle2,
@@ -29,7 +29,6 @@ export default function JudgeSubmissionsPage() {
2929

3030
function SubmissionsList() {
3131
const params = useParams<{ hackathonId: string }>();
32-
const router = useRouter();
3332
const hackathonId = params?.hackathonId ?? '';
3433

3534
const [filter, setFilter] = useState<Filter>('all');
@@ -88,13 +87,17 @@ function SubmissionsList() {
8887
} else if (e.key === 'Enter') {
8988
const s = visible[focusIdx];
9089
if (s) {
91-
router.push(`/judge/${hackathonId}/submissions/${s.submission.id}`);
90+
window.open(
91+
`/judge/${hackathonId}/submissions/${s.submission.id}`,
92+
'_blank',
93+
'noopener,noreferrer'
94+
);
9295
}
9396
}
9497
};
9598
window.addEventListener('keydown', onKey);
9699
return () => window.removeEventListener('keydown', onKey);
97-
}, [visible, focusIdx, router, hackathonId]);
100+
}, [visible, focusIdx, hackathonId]);
98101

99102
return (
100103
<div className='space-y-5'>
@@ -277,6 +280,8 @@ function Row({
277280
return (
278281
<Link
279282
href={`/judge/${hackathonId}/submissions/${submission.submission.id}`}
283+
target='_blank'
284+
rel='noopener noreferrer'
280285
onMouseEnter={onHover}
281286
className={cn(
282287
'group flex items-center gap-3 rounded-xl border bg-[#101010] px-4 py-3 transition-all',

app/me/hackathons/submissions/submission-components.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import { Separator } from '@/components/ui/separator';
1717
import { TableCell, TableRow as ShadcnTableRow } from '@/components/ui/table';
1818
import Image from 'next/image';
19+
import Link from 'next/link';
1920
import { format } from 'date-fns';
2021
import React, { useState } from 'react';
2122

@@ -206,7 +207,7 @@ export function SubmissionsSheetContent({
206207
</div>
207208
<div className='flex items-center gap-3'>
208209
<StatusBadge status={submission.status} />
209-
<a
210+
<Link
210211
href={viewUrl}
211212
target='_blank'
212213
rel='noopener noreferrer'
@@ -216,7 +217,7 @@ export function SubmissionsSheetContent({
216217
>
217218
<ExternalLink className='h-3.5 w-3.5' />
218219
View Page
219-
</a>
220+
</Link>
220221
</div>
221222
</div>
222223

@@ -463,7 +464,7 @@ export function TableRow({
463464
{submission.projectName}
464465
</span>
465466
{/* Always in DOM — fade via opacity so no layout shift */}
466-
<a
467+
<Link
467468
href={viewUrl}
468469
target='_blank'
469470
rel='noopener noreferrer'
@@ -477,7 +478,7 @@ export function TableRow({
477478
aria-label={`Open ${submission.projectName} in new tab`}
478479
>
479480
<ExternalLink className='h-3.5 w-3.5' />
480-
</a>
481+
</Link>
481482
</div>
482483
</div>
483484
</TableCell>

components/hackathons/winners/WinnersTab.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,12 @@ const TrackWinnerCard = ({
200200
</div>
201201

202202
{projectUrl ? (
203-
<Link href={projectUrl}>
203+
<Link
204+
href={projectUrl}
205+
target='_blank'
206+
rel='noopener noreferrer'
207+
className='block no-underline'
208+
>
204209
<div className='flex items-center justify-between rounded-lg border border-gray-900 bg-black/20 p-2 transition-colors hover:bg-black/40'>
205210
<p className='line-clamp-1 text-sm font-medium text-white'>
206211
{trackWinner.projectName}

components/organization/cards/JudgingParticipant.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ const JudgingParticipant = ({
258258
<span className='h-1 w-1 rounded-full bg-gray-700' />
259259
<Link
260260
href={`/projects/${submissionData.id}?type=submission`}
261+
target='_blank'
262+
rel='noopener noreferrer'
261263
className='flex items-center gap-1 transition-colors hover:text-white'
262264
>
263265
View Details <ExternalLink className='h-3 w-3' />
@@ -433,6 +435,8 @@ const JudgingParticipant = ({
433435
)}
434436
<Link
435437
href={`/projects/${submissionData.id}?type=submission`}
438+
target='_blank'
439+
rel='noopener noreferrer'
436440
className='flex h-7 w-7 items-center justify-center rounded-md border border-gray-800 text-gray-500 hover:bg-white/5 hover:text-white'
437441
>
438442
<ExternalLink className='h-3 w-3' />

components/organization/hackathons/submissions/SubmissionsList.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useState } from 'react';
2-
import { useRouter } from 'next/navigation';
32
import {
43
Users,
54
User,
@@ -56,7 +55,6 @@ export function SubmissionsList({
5655
onSelectionChange,
5756
hackathon,
5857
}: SubmissionsListProps) {
59-
const router = useRouter();
6058
const [reviewingId, setReviewingId] = useState<string | null>(null);
6159
const [disqualifyingId, setDisqualifyingId] = useState<string | null>(null);
6260
const [isDisqualifying, setIsDisqualifying] = useState(false);
@@ -155,7 +153,11 @@ export function SubmissionsList({
155153
};
156154

157155
const handleSubmissionClick = (submissionId: string) => {
158-
router.push(`/projects/${submissionId}?type=submission`);
156+
window.open(
157+
`/projects/${submissionId}?type=submission`,
158+
'_blank',
159+
'noopener,noreferrer'
160+
);
159161
};
160162

161163
if (submissions.length === 0 && !loading) {

0 commit comments

Comments
 (0)