diff --git a/teressa-copeland-homes/src/app/portal/(protected)/clients/[id]/page.tsx b/teressa-copeland-homes/src/app/portal/(protected)/clients/[id]/page.tsx new file mode 100644 index 0000000..e459618 --- /dev/null +++ b/teressa-copeland-homes/src/app/portal/(protected)/clients/[id]/page.tsx @@ -0,0 +1,32 @@ +import { db } from "@/lib/db"; +import { clients, documents } from "@/lib/db/schema"; +import { eq, desc, sql } from "drizzle-orm"; +import { notFound } from "next/navigation"; +import { ClientProfileClient } from "../../../_components/ClientProfileClient"; + +export default async function ClientProfilePage({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { id } = await params; + + const [client] = await db.select().from(clients).where(eq(clients.id, id)); + if (!client) notFound(); + + const docs = await db + .select({ + id: documents.id, + name: documents.name, + status: documents.status, + sentAt: documents.sentAt, + clientId: documents.clientId, + clientName: sql`${clients.name}`, + }) + .from(documents) + .leftJoin(clients, eq(documents.clientId, clients.id)) + .where(eq(documents.clientId, id)) + .orderBy(desc(documents.createdAt)); + + return ; +} diff --git a/teressa-copeland-homes/src/app/portal/_components/ClientProfileClient.tsx b/teressa-copeland-homes/src/app/portal/_components/ClientProfileClient.tsx new file mode 100644 index 0000000..5f750ba --- /dev/null +++ b/teressa-copeland-homes/src/app/portal/_components/ClientProfileClient.tsx @@ -0,0 +1,109 @@ +"use client"; + +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import { ClientModal } from "./ClientModal"; +import { ConfirmDialog } from "./ConfirmDialog"; +import { DocumentsTable } from "./DocumentsTable"; +import { deleteClient } from "@/lib/actions/clients"; + +type DocumentRow = { + id: string; + name: string; + clientName: string | null; + status: "Draft" | "Sent" | "Viewed" | "Signed"; + sentAt: Date | null; + clientId: string; +}; + +type Props = { + client: { id: string; name: string; email: string }; + docs: DocumentRow[]; +}; + +export function ClientProfileClient({ client, docs }: Props) { + const router = useRouter(); + const [isEditOpen, setIsEditOpen] = useState(false); + const [isDeleteOpen, setIsDeleteOpen] = useState(false); + + async function handleDelete() { + await deleteClient(client.id); + router.push("/portal/clients"); + } + + return ( +
+ {/* Header card */} +
+
+ + Back to Clients + +
+
+
+

+ {client.name} +

+

{client.email}

+
+
+ + +
+
+
+ + {/* Documents card */} +
+

+ Documents +

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

No documents yet.

+ ) : ( + + )} +
+ + {/* Edit modal */} + setIsEditOpen(false)} + mode="edit" + clientId={client.id} + defaultName={client.name} + defaultEmail={client.email} + /> + + {/* Delete confirmation */} + setIsDeleteOpen(false)} + /> +
+ ); +} diff --git a/teressa-copeland-homes/src/app/portal/_components/ConfirmDialog.tsx b/teressa-copeland-homes/src/app/portal/_components/ConfirmDialog.tsx new file mode 100644 index 0000000..56b18e2 --- /dev/null +++ b/teressa-copeland-homes/src/app/portal/_components/ConfirmDialog.tsx @@ -0,0 +1,46 @@ +"use client"; + +type Props = { + isOpen: boolean; + title: string; + message: string; + onConfirm: () => void; + onCancel: () => void; + confirmLabel?: string; +}; + +export function ConfirmDialog({ + isOpen, + title, + message, + onConfirm, + onCancel, + confirmLabel = "Delete", +}: Props) { + if (!isOpen) return null; + + return ( +
+
+

{title}

+

{message}

+
+ + +
+
+
+ ); +}