feat(14-01): add multi-signer types and columns to schema.ts
- Add signerEmail?: string to SignatureFieldData interface
- Add getSignerEmail() helper function with fallback pattern
- Add DocumentSigner interface { email, color }
- Add documents.signers JSONB column typed as DocumentSigner[]
- Add documents.completionTriggeredAt nullable TIMESTAMP column
- Add signingTokens.signerEmail nullable TEXT column
This commit is contained in:
@@ -18,6 +18,7 @@ export interface SignatureFieldData {
|
||||
width: number; // PDF points (default: 144 — 2 inches)
|
||||
height: number; // PDF points (default: 36 — 0.5 inches)
|
||||
type?: SignatureFieldType; // Optional — v1.0 documents have no type; fallback = 'client-signature'
|
||||
signerEmail?: string; // Optional — absent = legacy single-signer or agent-owned field
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,6 +40,16 @@ export function isClientVisibleField(field: SignatureFieldData): boolean {
|
||||
return t !== 'agent-signature' && t !== 'agent-initials';
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe signer email reader — returns the field's signerEmail or a fallback.
|
||||
* Legacy single-signer documents have no signerEmail on their fields;
|
||||
* this coalesces to the fallback (typically the document's single recipient email).
|
||||
* ALWAYS use this instead of reading field.signerEmail directly.
|
||||
*/
|
||||
export function getSignerEmail(field: SignatureFieldData, fallbackEmail: string): string {
|
||||
return field.signerEmail ?? fallbackEmail;
|
||||
}
|
||||
|
||||
export const users = pgTable("users", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
email: text("email").notNull().unique(),
|
||||
@@ -72,6 +83,12 @@ export const formTemplates = pgTable("form_templates", {
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
/** Shape of each entry in documents.signers JSONB array. */
|
||||
export interface DocumentSigner {
|
||||
email: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export const documents = pgTable("documents", {
|
||||
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
name: text("name").notNull(),
|
||||
@@ -92,6 +109,10 @@ export const documents = pgTable("documents", {
|
||||
signedFilePath: text("signed_file_path"),
|
||||
pdfHash: text("pdf_hash"),
|
||||
signedAt: timestamp("signed_at"),
|
||||
/** Per-signer list with assigned colors. NULL = legacy single-signer document. */
|
||||
signers: jsonb("signers").$type<DocumentSigner[]>(),
|
||||
/** Atomic completion guard — set once by the last signer's handler. NULL = not yet completed. */
|
||||
completionTriggeredAt: timestamp("completion_triggered_at"),
|
||||
});
|
||||
|
||||
export const documentsRelations = relations(documents, ({ one }) => ({
|
||||
@@ -115,6 +136,8 @@ export const signingTokens = pgTable('signing_tokens', {
|
||||
jti: text('jti').primaryKey(),
|
||||
documentId: text('document_id').notNull()
|
||||
.references(() => documents.id, { onDelete: 'cascade' }),
|
||||
/** Signer this token belongs to. NULL = legacy single-signer token. */
|
||||
signerEmail: text('signer_email'),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
expiresAt: timestamp('expires_at').notNull(),
|
||||
usedAt: timestamp('used_at'),
|
||||
|
||||
Reference in New Issue
Block a user