feat(03-03): add Dashboard page with filterable documents table

- Async server component queries all documents with LEFT JOIN to clients
- Filter state lives in URL (?status=Draft|Sent|Viewed|Signed) via DashboardFilters client component
- Rows filtered in JavaScript after fetch (tiny dataset in Phase 3)
- DashboardFilters extracted to _components/DashboardFilters.tsx (use client + useRouter)
- Displays agent first name extracted from session email
This commit is contained in:
Chandler Copeland
2026-03-19 16:46:26 -06:00
parent 59acc62606
commit e55d7a1de5
2 changed files with 81 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
import { auth } from "@/lib/auth";
import { db } from "@/lib/db";
import { documents, clients } from "@/lib/db/schema";
import { eq, desc } from "drizzle-orm";
import { DocumentsTable } from "../../_components/DocumentsTable";
import { DashboardFilters } from "../../_components/DashboardFilters";
const VALID_STATUSES = ["Draft", "Sent", "Viewed", "Signed"] as const;
type ValidStatus = (typeof VALID_STATUSES)[number];
export default async function DashboardPage({
searchParams,
}: {
searchParams: Promise<{ status?: string }>;
}) {
const session = await auth();
const { status } = await searchParams;
const allRows = await db
.select({
id: documents.id,
name: documents.name,
status: documents.status,
sentAt: documents.sentAt,
clientName: clients.name,
clientId: documents.clientId,
})
.from(documents)
.leftJoin(clients, eq(documents.clientId, clients.id))
.orderBy(desc(documents.createdAt));
const filteredRows =
status && (VALID_STATUSES as readonly string[]).includes(status)
? allRows.filter((r) => r.status === (status as ValidStatus))
: allRows;
const firstName = session?.user?.email?.split("@")[0] ?? "Agent";
return (
<div>
<div className="flex items-center justify-between mb-6">
<h1 className="text-[var(--navy)] text-2xl font-semibold">
Welcome back, {firstName}
</h1>
</div>
<div className="flex items-center gap-4 mb-6">
<span className="text-sm text-gray-600">Filter by status:</span>
<DashboardFilters currentStatus={status} />
</div>
<div className="bg-white rounded-xl shadow-sm overflow-hidden">
<DocumentsTable rows={filteredRows} showClientColumn={true} />
</div>
</div>
);
}

View File

@@ -0,0 +1,26 @@
"use client";
import { useRouter } from "next/navigation";
export function DashboardFilters({ currentStatus }: { currentStatus?: string }) {
const router = useRouter();
return (
<select
value={currentStatus ?? ""}
onChange={(e) =>
router.push(
e.target.value
? `?status=${e.target.value}`
: "/portal/dashboard"
)
}
className="border border-gray-200 rounded-lg px-3 py-1.5 text-sm"
>
<option value="">All statuses</option>
<option value="Draft">Draft</option>
<option value="Sent">Sent</option>
<option value="Viewed">Viewed</option>
<option value="Signed">Signed</option>
</select>
);
}