From 3e377d498d36bf824b78da534dafe8af5496c22d Mon Sep 17 00:00:00 2001 From: Benjtalkshow Date: Sat, 16 May 2026 00:30:46 +0100 Subject: [PATCH 1/3] 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. --- .../components/tabs/contents/FindTeam.tsx | 2 +- components/ui/pagination.tsx | 100 ++++++++---------- 2 files changed, 48 insertions(+), 54 deletions(-) diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/FindTeam.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/FindTeam.tsx index 3b2569fd..a279e06e 100644 --- a/app/(landing)/hackathons/[slug]/components/tabs/contents/FindTeam.tsx +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/FindTeam.tsx @@ -267,7 +267,7 @@ const FindTeam = () => { ) : teams.length > 0 ? ( <> -
+
{teams.map(team => ( = ({ return null; } + const canPrev = currentPage > 1; + const canNext = currentPage < totalPages; + return ( -
-
- +
+
+
Page {currentPage} of {totalPages} - - -
- +
+ + + +
From d14009a7b44a795cdd1e5cc082826f273b597b1f Mon Sep 17 00:00:00 2001 From: Benjtalkshow Date: Sat, 16 May 2026 00:48:08 +0100 Subject: [PATCH 2/3] 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. --- .../contents/submissions/SubmissionCard.tsx | 20 +++++++++- components/avatars/GroupAvatar.tsx | 38 ++++++++++++++----- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx index 26d00747..c1f18f3e 100644 --- a/app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx @@ -1,6 +1,7 @@ 'use client'; import { useState } from 'react'; +import Link from 'next/link'; import { useParams, useRouter } from 'next/navigation'; import { useQueryClient } from '@tanstack/react-query'; import { LayoutGrid, Loader2, Pencil, Trash2 } from 'lucide-react'; @@ -142,7 +143,24 @@ const SubmissionCard = ({ submission }: SubmissionCardProps) => {
{isTeam ? ( - m.avatar ?? '')} /> + m.avatar ?? '')} + usernames={teamMembers.map(m => m.username)} + /> + ) : participant?.username ? ( + + + ) : ( { +const GroupAvatar = ({ members, usernames }: GroupAvatarProps) => { const showCount = members.length > 3; const maxVisible = showCount ? 3 : members.length; const visibleMembers = members.slice(0, maxVisible); @@ -18,14 +20,32 @@ const GroupAvatar = ({ members }: GroupAvatarProps) => { return ( - {visibleMembers.map((member, index) => ( - - - - {member.slice(0, 2).toUpperCase()} - - - ))} + {visibleMembers.map((member, index) => { + const username = usernames?.[index]; + const avatar = ( + + + + {member.slice(0, 2).toUpperCase()} + + + ); + if (username) { + return ( + + {avatar} + + ); + } + return
{avatar}
; + })} {remainingCount > 0 && ( +{remainingCount} From 217b544df532384741e07ea0ed117608cbf62da1 Mon Sep 17 00:00:00 2001 From: Benjtalkshow Date: Mon, 18 May 2026 09:40:07 +0100 Subject: [PATCH 3/3] 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. --- .../[slug]/components/sidebar/MySubmissionPanel.tsx | 6 +++++- .../tabs/contents/submissions/SubmissionCard.tsx | 4 ++-- .../tabs/contents/winners/GeneralWinnerCard.tsx | 5 +++-- .../tabs/contents/winners/PodiumWinnerCard.tsx | 5 +++-- .../tabs/contents/winners/TopWinnerCard.tsx | 5 +++-- app/judge/[hackathonId]/submissions/page.tsx | 13 +++++++++---- .../submissions/submission-components.tsx | 9 +++++---- components/hackathons/winners/WinnersTab.tsx | 7 ++++++- .../organization/cards/JudgingParticipant.tsx | 4 ++++ .../hackathons/submissions/SubmissionsList.tsx | 8 +++++--- 10 files changed, 45 insertions(+), 21 deletions(-) diff --git a/app/(landing)/hackathons/[slug]/components/sidebar/MySubmissionPanel.tsx b/app/(landing)/hackathons/[slug]/components/sidebar/MySubmissionPanel.tsx index 0757914d..d1610701 100644 --- a/app/(landing)/hackathons/[slug]/components/sidebar/MySubmissionPanel.tsx +++ b/app/(landing)/hackathons/[slug]/components/sidebar/MySubmissionPanel.tsx @@ -92,7 +92,11 @@ export default function MySubmissionPanel() { status !== 'WITHDRAWN'; const handleView = () => { - router.push(`/projects/${submission.id}?type=submission`); + window.open( + `/projects/${submission.id}?type=submission`, + '_blank', + 'noopener,noreferrer' + ); }; const handleEdit = () => { diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx index c1f18f3e..5d0d74bc 100644 --- a/app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx @@ -194,7 +194,7 @@ const SubmissionCard = ({ submission }: SubmissionCardProps) => { )} - + { > View Project - +
diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/GeneralWinnerCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/GeneralWinnerCard.tsx index 37e74020..04da02d5 100644 --- a/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/GeneralWinnerCard.tsx +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/GeneralWinnerCard.tsx @@ -1,3 +1,4 @@ +import Link from 'next/link'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { HackathonWinner } from '@/lib/api/hackathons'; import { SubmissionCardProps } from '@/types/hackathon'; @@ -14,7 +15,7 @@ export const GeneralWinnerCard = ({ const projectUrl = `/projects/${winner.submissionId}?type=submission`; return ( -
{winner.prize}
-
+ ); }; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/PodiumWinnerCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/PodiumWinnerCard.tsx index 8f67b508..ce5ab26d 100644 --- a/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/PodiumWinnerCard.tsx +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/PodiumWinnerCard.tsx @@ -1,3 +1,4 @@ +import Link from 'next/link'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { HackathonWinner } from '@/lib/api/hackathons'; import { Trophy } from 'lucide-react'; @@ -53,7 +54,7 @@ export const PodiumWinnerCard = ({
- + View Project - +
); diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/TopWinnerCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/TopWinnerCard.tsx index 4d051904..c5a8bb4c 100644 --- a/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/TopWinnerCard.tsx +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/TopWinnerCard.tsx @@ -1,3 +1,4 @@ +import Link from 'next/link'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { HackathonWinner } from '@/lib/api/hackathons'; import { Trophy } from 'lucide-react'; @@ -79,7 +80,7 @@ export const TopWinnerCard = ({ winner, submission }: TopWinnerCardProps) => {
- + { > View Project - + diff --git a/app/judge/[hackathonId]/submissions/page.tsx b/app/judge/[hackathonId]/submissions/page.tsx index dcc3ebfb..af4afaab 100644 --- a/app/judge/[hackathonId]/submissions/page.tsx +++ b/app/judge/[hackathonId]/submissions/page.tsx @@ -1,7 +1,7 @@ 'use client'; import Link from 'next/link'; -import { useParams, useRouter } from 'next/navigation'; +import { useParams } from 'next/navigation'; import { useEffect, useMemo, useState } from 'react'; import { CheckCircle2, @@ -29,7 +29,6 @@ export default function JudgeSubmissionsPage() { function SubmissionsList() { const params = useParams<{ hackathonId: string }>(); - const router = useRouter(); const hackathonId = params?.hackathonId ?? ''; const [filter, setFilter] = useState('all'); @@ -88,13 +87,17 @@ function SubmissionsList() { } else if (e.key === 'Enter') { const s = visible[focusIdx]; if (s) { - router.push(`/judge/${hackathonId}/submissions/${s.submission.id}`); + window.open( + `/judge/${hackathonId}/submissions/${s.submission.id}`, + '_blank', + 'noopener,noreferrer' + ); } } }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); - }, [visible, focusIdx, router, hackathonId]); + }, [visible, focusIdx, hackathonId]); return (
@@ -277,6 +280,8 @@ function Row({ return (
- View Page - +
@@ -463,7 +464,7 @@ export function TableRow({ {submission.projectName} {/* Always in DOM — fade via opacity so no layout shift */} - - + diff --git a/components/hackathons/winners/WinnersTab.tsx b/components/hackathons/winners/WinnersTab.tsx index 506270fe..9502c5e2 100644 --- a/components/hackathons/winners/WinnersTab.tsx +++ b/components/hackathons/winners/WinnersTab.tsx @@ -200,7 +200,12 @@ const TrackWinnerCard = ({ {projectUrl ? ( - +

{trackWinner.projectName} diff --git a/components/organization/cards/JudgingParticipant.tsx b/components/organization/cards/JudgingParticipant.tsx index 2e709e53..4008a5d6 100644 --- a/components/organization/cards/JudgingParticipant.tsx +++ b/components/organization/cards/JudgingParticipant.tsx @@ -258,6 +258,8 @@ const JudgingParticipant = ({ View Details @@ -433,6 +435,8 @@ const JudgingParticipant = ({ )} diff --git a/components/organization/hackathons/submissions/SubmissionsList.tsx b/components/organization/hackathons/submissions/SubmissionsList.tsx index 30029d9a..927e3805 100644 --- a/components/organization/hackathons/submissions/SubmissionsList.tsx +++ b/components/organization/hackathons/submissions/SubmissionsList.tsx @@ -1,5 +1,4 @@ import { useState } from 'react'; -import { useRouter } from 'next/navigation'; import { Users, User, @@ -56,7 +55,6 @@ export function SubmissionsList({ onSelectionChange, hackathon, }: SubmissionsListProps) { - const router = useRouter(); const [reviewingId, setReviewingId] = useState(null); const [disqualifyingId, setDisqualifyingId] = useState(null); const [isDisqualifying, setIsDisqualifying] = useState(false); @@ -155,7 +153,11 @@ export function SubmissionsList({ }; const handleSubmissionClick = (submissionId: string) => { - router.push(`/projects/${submissionId}?type=submission`); + window.open( + `/projects/${submissionId}?type=submission`, + '_blank', + 'noopener,noreferrer' + ); }; if (submissions.length === 0 && !loading) {