import { notFound } from "next/navigation"; import { formatDate } from "~/lib/format.js"; import Link from "next/link"; import { createCaller } from "~/server/trpc.js"; import { auth } from "~/server/auth.js"; import { BudgetStatusCard } from "~/components/projects/BudgetStatusCard.js"; import { ProjectDetailActions } from "~/components/projects/ProjectDetailClient.js"; import { ProjectDemandsTable } from "~/components/projects/ProjectDemandsTable.js"; import { ProjectAssignmentsTable } from "~/components/projects/ProjectAssignmentsTable.js"; import { PROJECT_STATUS_BADGE as STATUS_COLORS, ORDER_TYPE_BADGE as ORDER_TYPE_COLORS } from "~/lib/status-styles.js"; import { InfoTooltip } from "~/components/ui/InfoTooltip.js"; import { CoverArtSection } from "~/components/projects/CoverArtSection.js"; import { ShoringIndicator } from "~/components/projects/ShoringIndicator.js"; const EDIT_ROLES = new Set(["ADMIN", "MANAGER"]); interface ProjectDetailPageProps { params: Promise<{ id: string }>; } export default async function ProjectDetailPage({ params }: ProjectDetailPageProps) { const { id } = await params; const [trpc, session] = await Promise.all([createCaller(), auth()]); const userRole = (session?.user as { role?: string } | undefined)?.role ?? "USER"; const canEditProject = EDIT_ROLES.has(userRole); let project: Awaited>; try { project = await trpc.project.getById({ id }); } catch { notFound(); } const activeAssignments = project.assignments.filter((assignment) => assignment.status !== "CANCELLED"); const activeDemands = project.demands.filter((demand) => demand.status !== "CANCELLED" && demand.status !== "COMPLETED"); const requestedSeats = activeDemands.reduce((sum, demand) => sum + demand.requestedHeadcount, 0); const unfilledSeats = activeDemands.reduce((sum, demand) => sum + demand.unfilledHeadcount, 0); return (
{/* Back link */} Back to Projects {/* Cover Art */} {/* Project header */}
{project.shortCode} {project.status} {project.orderType}

{project.name}

{formatDate(project.startDate)} {" — "} {formatDate(project.endDate)}
Win probability: {project.winProbability}%
What-If
Chargecode
{project.shortCode}
Order Type
{project.orderType}
Allocation Type
{project.allocationType}
Assignments
{activeAssignments.length} active
Open Demands
{activeDemands.length} items · {unfilledSeats}/{requestedSeats} seats unfilled
{project.responsiblePerson && (
Responsible Person
{project.responsiblePerson}
)}
{/* Budget status card (client component) */} {/* Nearshore ratio indicator (client component) */} {/* Assignments table (client component with delete action) */} {/* Open demands table (client component with fill action) */}
); }