fix(signers): show invalid email error, persist signers immediately via PATCH, add PATCH /api/documents/[id]

This commit is contained in:
Chandler Copeland
2026-04-03 17:29:27 -06:00
parent 07cfaf0511
commit 4f25a8c124
2 changed files with 52 additions and 3 deletions

View File

@@ -0,0 +1,36 @@
import { NextResponse } from 'next/server';
import { auth } from '@/lib/auth';
import { db } from '@/lib/db';
import { documents } from '@/lib/db/schema';
import type { DocumentSigner } from '@/lib/db/schema';
import { eq } from 'drizzle-orm';
// PATCH /api/documents/[id] — partial update (signers, name, etc.)
export async function PATCH(
req: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const session = await auth();
if (!session) return new Response('Unauthorized', { status: 401 });
const { id } = await params;
const body = await req.json() as { signers?: DocumentSigner[] };
const doc = await db.query.documents.findFirst({ where: eq(documents.id, id) });
if (!doc) return NextResponse.json({ error: 'Not found' }, { status: 404 });
const updates: Partial<typeof documents.$inferInsert> = {};
if (body.signers !== undefined) updates.signers = body.signers;
if (Object.keys(updates).length === 0) {
return NextResponse.json({ ok: true });
}
const [updated] = await db
.update(documents)
.set(updates)
.where(eq(documents.id, id))
.returning();
return NextResponse.json(updated);
}

View File

@@ -67,6 +67,16 @@ export function PreparePanel({
const [signerInput, setSignerInput] = useState('');
const [signerInputError, setSignerInputError] = useState<string | null>(null);
async function saveSigners(updated: DocumentSigner[]) {
onSignersChange?.(updated);
// Persist immediately so signers survive page refresh
await fetch(`/api/documents/${docId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ signers: updated }),
});
}
function handleAddSigner() {
const email = signerInput.trim().toLowerCase();
if (!email || !isValidEmail(email)) {
@@ -78,14 +88,14 @@ export function PreparePanel({
return;
}
const color = SIGNER_COLORS[signers.length % SIGNER_COLORS.length];
onSignersChange?.([...signers, { email, color }]);
saveSigners([...signers, { email, color }]);
setSignerInput('');
setSignerInputError(null);
}
function handleRemoveSigner(email: string) {
onSignersChange?.(signers.filter(s => s.email !== email));
// Clear validation state since signer count changed
const updated = signers.filter(s => s.email !== email);
saveSigners(updated);
onUnassignedFieldIdsChange?.(new Set());
}
@@ -329,6 +339,9 @@ export function PreparePanel({
</button>
</div>
<p className="text-xs text-gray-400 mt-1">Each signer receives their own signing link.</p>
{signerInputError === 'invalid' && (
<p className="text-sm text-red-600 mt-1">Please enter a valid email address.</p>
)}
{signerInputError === 'duplicate' && (
<p className="text-sm text-red-600 mt-1">That email is already in the signer list.</p>
)}