fix(signers): show invalid email error, persist signers immediately via PATCH, add PATCH /api/documents/[id]
This commit is contained in:
36
teressa-copeland-homes/src/app/api/documents/[id]/route.ts
Normal file
36
teressa-copeland-homes/src/app/api/documents/[id]/route.ts
Normal 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);
|
||||||
|
}
|
||||||
@@ -67,6 +67,16 @@ export function PreparePanel({
|
|||||||
const [signerInput, setSignerInput] = useState('');
|
const [signerInput, setSignerInput] = useState('');
|
||||||
const [signerInputError, setSignerInputError] = useState<string | null>(null);
|
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() {
|
function handleAddSigner() {
|
||||||
const email = signerInput.trim().toLowerCase();
|
const email = signerInput.trim().toLowerCase();
|
||||||
if (!email || !isValidEmail(email)) {
|
if (!email || !isValidEmail(email)) {
|
||||||
@@ -78,14 +88,14 @@ export function PreparePanel({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const color = SIGNER_COLORS[signers.length % SIGNER_COLORS.length];
|
const color = SIGNER_COLORS[signers.length % SIGNER_COLORS.length];
|
||||||
onSignersChange?.([...signers, { email, color }]);
|
saveSigners([...signers, { email, color }]);
|
||||||
setSignerInput('');
|
setSignerInput('');
|
||||||
setSignerInputError(null);
|
setSignerInputError(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRemoveSigner(email: string) {
|
function handleRemoveSigner(email: string) {
|
||||||
onSignersChange?.(signers.filter(s => s.email !== email));
|
const updated = signers.filter(s => s.email !== email);
|
||||||
// Clear validation state since signer count changed
|
saveSigners(updated);
|
||||||
onUnassignedFieldIdsChange?.(new Set());
|
onUnassignedFieldIdsChange?.(new Set());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,6 +339,9 @@ export function PreparePanel({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-400 mt-1">Each signer receives their own signing link.</p>
|
<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' && (
|
{signerInputError === 'duplicate' && (
|
||||||
<p className="text-sm text-red-600 mt-1">That email is already in the signer list.</p>
|
<p className="text-sm text-red-600 mt-1">That email is already in the signer list.</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user