From e55d7a1de57d475e0c1a7fd5055fd93dc431c7d4 Mon Sep 17 00:00:00 2001 From: Chandler Copeland Date: Thu, 19 Mar 2026 16:46:26 -0600 Subject: [PATCH] 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 --- .../app/portal/(protected)/dashboard/page.tsx | 55 +++++++++++++++++++ .../portal/_components/DashboardFilters.tsx | 26 +++++++++ 2 files changed, 81 insertions(+) create mode 100644 teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx create mode 100644 teressa-copeland-homes/src/app/portal/_components/DashboardFilters.tsx diff --git a/teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx b/teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx new file mode 100644 index 0000000..dfc072b --- /dev/null +++ b/teressa-copeland-homes/src/app/portal/(protected)/dashboard/page.tsx @@ -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 ( +
+
+

+ Welcome back, {firstName} +

+
+
+ Filter by status: + +
+
+ +
+
+ ); +} diff --git a/teressa-copeland-homes/src/app/portal/_components/DashboardFilters.tsx b/teressa-copeland-homes/src/app/portal/_components/DashboardFilters.tsx new file mode 100644 index 0000000..55ce40d --- /dev/null +++ b/teressa-copeland-homes/src/app/portal/_components/DashboardFilters.tsx @@ -0,0 +1,26 @@ +"use client"; + +import { useRouter } from "next/navigation"; + +export function DashboardFilters({ currentStatus }: { currentStatus?: string }) { + const router = useRouter(); + return ( + + ); +}