diff --git a/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx b/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx
index 7465543..574e4e7 100644
--- a/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx
+++ b/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx
@@ -68,11 +68,12 @@ async function persistFields(docId: string, fields: SignatureFieldData[]) {
// Token color palette — each maps to a SignatureFieldType
const PALETTE_TOKENS: Array<{ id: SignatureFieldType; label: string; color: string }> = [
- { id: 'client-signature', label: 'Signature', color: '#2563eb' }, // blue
- { id: 'initials', label: 'Initials', color: '#7c3aed' }, // purple
- { id: 'checkbox', label: 'Checkbox', color: '#059669' }, // green
- { id: 'date', label: 'Date', color: '#d97706' }, // amber
- { id: 'text', label: 'Text', color: '#64748b' }, // slate
+ { id: 'client-signature', label: 'Signature', color: '#2563eb' }, // blue
+ { id: 'initials', label: 'Initials', color: '#7c3aed' }, // purple
+ { id: 'checkbox', label: 'Checkbox', color: '#059669' }, // green
+ { id: 'date', label: 'Date', color: '#d97706' }, // amber
+ { id: 'text', label: 'Text', color: '#64748b' }, // slate
+ { id: 'agent-signature', label: 'Agent Signature', color: '#dc2626' }, // red
];
// Draggable token in the palette
diff --git a/teressa-copeland-homes/src/app/portal/(protected)/profile/page.tsx b/teressa-copeland-homes/src/app/portal/(protected)/profile/page.tsx
new file mode 100644
index 0000000..ae319ea
--- /dev/null
+++ b/teressa-copeland-homes/src/app/portal/(protected)/profile/page.tsx
@@ -0,0 +1,31 @@
+import { auth } from '@/lib/auth';
+import { redirect } from 'next/navigation';
+import { db } from '@/lib/db';
+import { users } from '@/lib/db/schema';
+import { eq } from 'drizzle-orm';
+import { AgentSignaturePanel } from '../../_components/AgentSignaturePanel';
+
+export default async function ProfilePage() {
+ const session = await auth();
+ if (!session?.user?.id) redirect('/agent/login');
+
+ const user = await db.query.users.findFirst({
+ where: eq(users.id, session.user.id),
+ columns: { agentSignatureData: true },
+ });
+
+ return (
+
+
Profile
+
+
+
Agent Signature
+
+ Draw your signature once. It will be embedded in any "Agent Signature" fields when you prepare a document.
+
+
+
+
+
+ );
+}
diff --git a/teressa-copeland-homes/src/app/portal/_components/AgentSignaturePanel.tsx b/teressa-copeland-homes/src/app/portal/_components/AgentSignaturePanel.tsx
new file mode 100644
index 0000000..861822e
--- /dev/null
+++ b/teressa-copeland-homes/src/app/portal/_components/AgentSignaturePanel.tsx
@@ -0,0 +1,112 @@
+'use client';
+import { useEffect, useRef, useState } from 'react';
+import SignaturePad from 'signature_pad';
+
+interface AgentSignaturePanelProps {
+ initialData: string | null;
+}
+
+export function AgentSignaturePanel({ initialData }: AgentSignaturePanelProps) {
+ const [savedData, setSavedData] = useState(initialData);
+ const [isDrawing, setIsDrawing] = useState(!initialData);
+ const canvasRef = useRef(null);
+ const sigPadRef = useRef(null);
+ const [saving, setSaving] = useState(false);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ if (!isDrawing || !canvasRef.current) return;
+ const canvas = canvasRef.current;
+ const ratio = Math.max(window.devicePixelRatio || 1, 1);
+ canvas.width = canvas.offsetWidth * ratio;
+ canvas.height = canvas.offsetHeight * ratio;
+ canvas.getContext('2d')?.scale(ratio, ratio);
+ sigPadRef.current = new SignaturePad(canvas, {
+ backgroundColor: 'rgba(0,0,0,0)',
+ penColor: '#1B2B4B',
+ });
+ return () => sigPadRef.current?.off();
+ }, [isDrawing]);
+
+ async function handleSave() {
+ if (!sigPadRef.current || sigPadRef.current.isEmpty()) {
+ setError('Please draw your signature first');
+ return;
+ }
+ const dataURL = sigPadRef.current.toDataURL('image/png');
+ setSaving(true);
+ setError(null);
+ try {
+ const res = await fetch('/api/agent/signature', {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ dataURL }),
+ });
+ if (res.ok) {
+ setSavedData(dataURL);
+ setIsDrawing(false);
+ } else {
+ const data = await res.json().catch(() => ({ error: 'Save failed' }));
+ setError(data.error ?? 'Save failed');
+ }
+ } catch {
+ setError('Network error — please try again');
+ } finally {
+ setSaving(false);
+ }
+ }
+
+ if (!isDrawing && savedData) {
+ return (
+
+
Your saved signature:
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+

+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ {savedData && (
+
+ )}
+
+ {error &&
{error}
}
+
+ );
+}
diff --git a/teressa-copeland-homes/src/app/portal/_components/PortalNav.tsx b/teressa-copeland-homes/src/app/portal/_components/PortalNav.tsx
index 149f523..3889985 100644
--- a/teressa-copeland-homes/src/app/portal/_components/PortalNav.tsx
+++ b/teressa-copeland-homes/src/app/portal/_components/PortalNav.tsx
@@ -4,52 +4,58 @@ import Link from "next/link";
import { usePathname } from "next/navigation";
import { LogoutButton } from "@/components/ui/LogoutButton";
-interface PortalNavProps {
- userEmail: string;
-}
-
const navLinks = [
{ href: "/portal/dashboard", label: "Dashboard" },
{ href: "/portal/clients", label: "Clients" },
+ { href: "/portal/profile", label: "Profile" },
];
-export function PortalNav({ userEmail }: PortalNavProps) {
+export function PortalNav({ userEmail }: { userEmail: string }) {
const pathname = usePathname();
return (
-