From f33a4075c9a8418715e2a954769739f6ad089005 Mon Sep 17 00:00:00 2001 From: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Date: Sat, 16 May 2026 00:50:36 +0100 Subject: [PATCH 01/11] fix(hackathons): single-column teams tab and primary-colored pager (#562) * 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. --- .../components/tabs/contents/FindTeam.tsx | 2 +- .../contents/submissions/SubmissionCard.tsx | 20 +++- components/avatars/GroupAvatar.tsx | 38 +++++-- components/ui/pagination.tsx | 100 ++++++++---------- 4 files changed, 96 insertions(+), 64 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 ? ( <> -
- {tier.name ?? - `${i + 1}${['st', 'nd', 'rd'][i] ?? 'th'} Place`} -
-- {Number(tier.prizeAmount ?? 0).toLocaleString()}{' '} - - {tier.currency ?? currency} - -
-+ {isTrack && ( + + Track · + + )} + {label} +
++ {Number(tier.prizeAmount ?? 0).toLocaleString()}{' '} + + {tier.currency ?? currency} + +
++ {tier.description} +
+ )} +- {tier.description} + {trackTiers.length > 0 && ( +
+ Category prizes alongside the overall placements. Pick the + tracks you want your submission considered for when you + submit.
- )} -+ {track?.description || tier.description} +
+ )} + {!track && tier.trackId && ( ++ Track details loading… +
+ )} ++ “{tagline}” +
+ ) : null; + })()} + + {/* Screenshots gallery (up to 5). Renders as a horizontal + scroll on mobile, a row of thumbnails on desktop. */} + {(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const screenshots = ((submission as any).screenshots ?? + []) as string[]; + return screenshots.length > 0 ? ( +{submission.description}
++ {submission.description} +
+ {e.trackName} +
+ {e.trackAnswers?.promptAnswer && ( ++ {e.trackAnswers.promptAnswer} +
+ )} + {Object.entries(e.trackAnswers?.customAnswers ?? {}) + .filter(([, v]) => v && v.trim().length > 0) + .map(([qid, value]) => ( +{qid}
++ {value} +
+{aid}
+ + {url} + ++ {value.length} of {trackCap} selected +
+ )} ++ {track.name} +
+ {track.prompt && ( +{form.watch('description')}
+ Tracks +
++ None — your submission will be considered for + overall placements only. +
++ Tracks +
+{trackWinner.teamName}
+ )} ++ {trackWinner.projectName} +
++ {trackWinner.projectName} +
++ Prize structure +
++ Overall only is the legacy default. Switch to Overall + + Tracks for things like Best UI/UX alongside 1st/2nd/3rd. +
+
+
+ {availableTracks.filter(t => !t.isArchived).length} active + track + {availableTracks.filter(t => !t.isArchived).length === 1 + ? '' + : 's'}{' '} + available. Mark a tier's kind as “Track” + below to bind it. +
+ )} ++ Tracks created but no prize is bound to them +
++ You have{' '} + + {availableTracks.filter(t => !t.isArchived).length} track + {availableTracks.filter(t => !t.isArchived).length === 1 + ? '' + : 's'} + {' '} + set up, but none of your prize tiers below is marked as a + Track. The public hackathon page will show only your overall + placements. To award a track prize: +
++ Tracks still waiting for a tier:{' '} + {unboundActiveTracks.map(t => t.name).join(', ')} +
++ Categorical prizes alongside overall placements (e.g. Best UI/UX, + Best Technical). Submitters opt into tracks at submission time; + winners are picked from each track's opted-in pool. +
++ No tracks yet. Create one to unlock track-based prizes in the + Rewards tab. +
+
{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
+ This is exactly what will be stamped on publish. EXCLUSIVE stacking + applied — one award per submission. Refreshes when scores change. +
+| Rank | +Project | +Score | +Prize | +Source | +
|---|---|---|---|---|
| #{o.rank} | +{o.projectName} | ++ {o.averageScore.toFixed(2)} + | ++ {formatPrize(o.prizeAmount, o.currency) ?? '—'} + | ++ {o.isOverride ? ( + Override + ) : ( + Computed + )} + | +
+ This track will not pay out — fix before publish. +
+ )} + {t.runnersUp.length > 0 && ( +| + Submission + | + {judges.map(j => ( +
+
+
+ {initialOf(j.name)}
+
+
+ {j.scoredCount}/{summary.totalSubmissions}
+
+
+ |
+ ))}
+ + Status + | +
|---|---|---|
| + {sub.projectName} + | + {judges.map(j => { + const scored = sub.scoredSet.has(j.userId); + return ( ++ + | + ); + })} +
+ {sub.isCovered ? (
+ |
+
+ Each section shows submissions opted into that track, sorted by their + average score across all judges. The highlighted row is the current + leader — at publish time, EXCLUSIVE stacking may promote a runner-up if + the leader wins an overall placement or another track. +
+ +- {winner.projectName} -
-{winner.projectName}
-+ {winner.projectName} +
+{winner.projectName}
+