diff --git a/app/(protected)/admin/cohort/[id]/page.tsx b/app/(protected)/admin/cohort/[id]/page.tsx index ce4fc31..589e202 100644 --- a/app/(protected)/admin/cohort/[id]/page.tsx +++ b/app/(protected)/admin/cohort/[id]/page.tsx @@ -5,7 +5,7 @@ import { requireAdmin } from "../../../../../lib/guards"; import { prisma } from "../../../../../lib/prisma"; import { editQuestion, endCohort, reviewApplication } from "../actions"; -const TABS = [ +const STATUS_TABS = [ { key: "pending", label: "Pending", status: ApplicationStatus.SUBMITTED }, { key: "approved", label: "Approved", status: ApplicationStatus.APPROVED }, { key: "rejected", label: "Not approved", status: ApplicationStatus.REJECTED }, @@ -14,7 +14,7 @@ const TABS = [ export default async function CohortDetailPage({ params, searchParams, -}: Readonly<{ params: { id: string }; searchParams?: { status?: string } }>) { +}: Readonly<{ params: { id: string }; searchParams?: { status?: string; view?: string } }>) { await requireAdmin(); const cohort = await prisma.cohort.findUnique({ where: { id: params.id } }); @@ -23,21 +23,27 @@ export default async function CohortDetailPage({ redirect("/admin/cohort"); } - const activeTab = TABS.find((tab) => tab.key === searchParams?.status) ?? TABS[0]; + const view = searchParams?.view === "edit" ? "edit" : "review"; + const activeStatus = + STATUS_TABS.find((tab) => tab.key === searchParams?.status) ?? STATUS_TABS[0]; - // Applications live on the cohort screen, filtered by status for this cohort's year. - const [applications, counts] = await Promise.all([ - prisma.application.findMany({ - where: { cohortId: cohort.id, status: activeTab.status }, - orderBy: { updatedAt: "asc" }, - include: { user: { include: { profile: true } } }, - }), - prisma.application.groupBy({ - by: ["status"], - where: { cohortId: cohort.id }, - _count: { _all: true }, - }), - ]); + // Only the Review tab renders applications, so skip both queries on Edit + // (the findMany joins user + profile — wasted work otherwise). + const [applications, counts] = + view === "review" + ? await Promise.all([ + prisma.application.findMany({ + where: { cohortId: cohort.id, status: activeStatus.status }, + orderBy: { updatedAt: "asc" }, + include: { user: { include: { profile: true } } }, + }), + prisma.application.groupBy({ + by: ["status"], + where: { cohortId: cohort.id }, + _count: { _all: true }, + }), + ]) + : [[], []]; const countByStatus = new Map(counts.map((row) => [row.status, row._count._all])); const questions = parseQuestions(cohort.questions); @@ -54,182 +60,205 @@ export default async function CohortDetailPage({ Cohort {cohort.year} {cohort.isActive ? "(Active)" : "(Ended)"} - {cohort.isActive ? ( -
- - -
- ) : ( -

- Recruitment for this cohort has ended. Open a new year from the Cohorts page to start - recruiting again. -

- )} + + + {view === "edit" ? ( + <> + {cohort.isActive ? ( +
+ + +
+ ) : ( +

+ Recruitment for this cohort has ended. Open a new year from the Cohorts page to + start recruiting again. +

+ )} + +
+

Application questions

+ {questions.length === 0 ? ( +

+ {cohort.isActive + ? "No questions yet. Add the first one below." + : "No questions set."} +

+ ) : cohort.isActive ? ( +
+ {questions.map((question, index) => ( +
+ + + + +
+ + + + +
+
+ ))} +
+ ) : ( + // Ended cohort: questions are read-only so historical answers stay labelled. +
+ {questions.map((question) => ( +

+ {question.label} + {question.required ? " (required)" : ""} +

+ ))} +
+ )} -
-

Application questions

- {questions.length === 0 ? ( -

- {cohort.isActive ? "No questions yet. Add the first one below." : "No questions set."} -

- ) : cohort.isActive ? ( -
- {questions.map((question, index) => ( -
+ {cohort.isActive ? ( + - - + +
- - - -
- ))} + ) : null}
- ) : ( - // Ended cohort: questions are read-only so historical answers stay labelled. -
- {questions.map((question) => ( -

- {question.label} - {question.required ? " (required)" : ""} -

+ + ) : ( +
+

Applications

+
- )} + - {cohort.isActive ? ( -
- - - - -
- -
-
- ) : null} -
- -
-

Applications

- - - {applications.length === 0 ? ( -

No applications in this view.

- ) : ( -
- {applications.map((application) => { - const answers = parseAnswers(application.answers); - return ( -
-
-

{application.user.name ?? application.user.email}

-

- {application.user.email} · Submitted{" "} - {dateFormatter.format(application.createdAt)} -

-

- Batch: {application.user.profile?.batch ?? "Not provided"}{" "} - · Branch:{" "} - {application.user.profile?.branch ?? "Not provided"} -

- {questions.map((question) => ( -

- {question.label}:{" "} - {answers[question.id] || "Not provided"} + {applications.length === 0 ? ( +

No applications in this view.

+ ) : ( +
+ {applications.map((application) => { + const answers = parseAnswers(application.answers); + return ( +
+
+

{application.user.name ?? application.user.email}

+

+ {application.user.email} · Submitted{" "} + {dateFormatter.format(application.createdAt)}

- ))} - {/* Cohort has no questions — fall back to the legacy free-text fields. */} - {questions.length === 0 ? ( - <> -

- Goals: {application.goals || "Not provided"} -

-

- Experience: {application.experience || "Not provided"} +

+ Batch:{" "} + {application.user.profile?.batch ?? "Not provided"} ·{" "} + Branch:{" "} + {application.user.profile?.branch ?? "Not provided"} +

+ {questions.map((question) => ( +

+ {question.label}:{" "} + {answers[question.id] || "Not provided"}

- {application.whyJoin ? ( + ))} + {/* Cohort has no questions — fall back to the legacy free-text fields. */} + {questions.length === 0 ? ( + <> +

+ Goals: {application.goals || "Not provided"} +

- Why join: {application.whyJoin} + Experience:{" "} + {application.experience || "Not provided"}

- ) : null} - + {application.whyJoin ? ( +

+ Why join: {application.whyJoin} +

+ ) : null} + + ) : null} +
+ {application.status === ApplicationStatus.SUBMITTED ? ( +
+ + + +
) : null} -
- {application.status === ApplicationStatus.SUBMITTED ? ( -
- - - -
- ) : null} -
- ); - })} -
- )} -
+ + ); + })} +
+ )} +
+ )} );