From 4d475c1025efe15bc9c3ff4de630f24b67c75fba Mon Sep 17 00:00:00 2001 From: sacha <23283108+sacha-l@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:18:21 +0200 Subject: [PATCH] =?UTF-8?q?feat(program):=20published=20hackathon=20page?= =?UTF-8?q?=20=E2=80=94=20searchable=20projects=20on=20top,=20info=20colla?= =?UTF-8?q?psed=20below?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Once results are published, the public program page leads with the projects (searchable by title/author/brief) and collapses all the hackathon info (description, key info, content) into an '·ABOUT THIS HACKATHON' panel at the bottom. Submit panel hidden once published. Pre-publish layout is unchanged. --- client/src/pages/ProgramDetailPage.tsx | 112 +++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/client/src/pages/ProgramDetailPage.tsx b/client/src/pages/ProgramDetailPage.tsx index 017bda0..0a9b04d 100644 --- a/client/src/pages/ProgramDetailPage.tsx +++ b/client/src/pages/ProgramDetailPage.tsx @@ -60,6 +60,10 @@ const ProgramDetailPage = () => { const [modalOpen, setModalOpen] = useState(false); const [nonMemberOpen, setNonMemberOpen] = useState(false); const [submitOpen, setSubmitOpen] = useState(false); + // Public results: search box over published submissions, and the collapsible + // "about" panel (hackathon info) that sits below the projects once published. + const [resultsQuery, setResultsQuery] = useState(""); + const [aboutOpen, setAboutOpen] = useState(false); // Admins can apply on behalf of any project (server-side `requireTeamMemberOrAdminByBodyProject` // accepts admins). Without this branch, the page would dead-end at "You need to be a team member" @@ -240,6 +244,15 @@ const ProgramDetailPage = () => { .filter((s): s is Extract => s.type === "schedule") .flatMap((s) => s.rows); + // Once a hackathon's results are published, flip the page: searchable projects + // on top, hackathon info collapsed at the bottom. + const isPublished = isHackathon && !!results?.published && results.submissions.length > 0; + const filteredResults = (results?.submissions ?? []).filter((s) => { + const q = resultsQuery.trim().toLowerCase(); + if (!q) return true; + return [s.projectTitle, s.submitterName, s.projectBrief].some((v) => (v ?? "").toLowerCase().includes(q)); + }); + const RackButton = ({ onClick, disabled, children, }: { onClick?: () => void; disabled?: boolean; children: React.ReactNode }) => ( @@ -382,7 +395,43 @@ const ProgramDetailPage = () => { - {program.description && ( + {/* Published hackathon: searchable projects up top. */} + {isPublished && ( +
+
+
·PROJECTS ({filteredResults.length})
+ setResultsQuery(e.target.value)} + placeholder="Search projects…" + className="bg-panel-deep border border-hairline text-display placeholder:text-label-dim font-mono text-[12px] px-2 py-1.5 focus:outline-none focus:border-display w-full sm:w-56" + /> +
+ {filteredResults.length === 0 ? ( +

No projects match your search.

+ ) : ( +
+ {filteredResults.map((s, i) => ( + + ))} +
+ )} +
+ )} + + {!isPublished && program.description && (
·DESCRIPTION

@@ -393,7 +442,7 @@ const ProgramDetailPage = () => { {!isHackathon && } - {isHackathon && ( + {isHackathon && !isPublished && (

·KEY INFO
{program.coverImageUrl && !coverError && ( @@ -513,7 +562,7 @@ const ProgramDetailPage = () => {
)} - {results?.published && results.submissions.length > 0 && ( + {results?.published && results.submissions.length > 0 && !isPublished && (
·RESULTS
@@ -600,13 +649,66 @@ const ProgramDetailPage = () => { {/* Hackathon builder reference + submission requirements. Authored as ordered `content` sections so they render directly above the submit panel. Non-hackathon programs render their content higher up. */} - {isHackathon && ( + {isHackathon && !isPublished && ( s.type !== "schedule")} /> )} - {program.programType === "hackathon" && ( + {/* Published hackathon: all the event info collapsed below the projects. */} + {isPublished && ( +
+ + {aboutOpen && ( +
+ {program.description && ( +

+ {program.description} +

+ )} + {program.coverImageUrl && !coverError && ( + {`${program.name} setCoverError(true)} + className="w-full rounded-sm border border-hairline object-cover" + /> + )} +
+ {eventRange && ( +
+ DATE + {eventRange} +
+ )} + {program.location && ( +
+ LOCATION + {program.location} +
+ )} + {scheduleRows.map((r, i) => ( +
+ {r.time} + {r.label} +
+ ))} +
+ s.type !== "schedule")} /> +
+ )} +
+ )} + + {program.programType === "hackathon" && !isPublished && (
·SUBMIT YOUR PROJECT