Online Contract Creation & Digital Signature Skill
This skill documents how to build an online contract signing flow using React, Supabase, and react-to-print. It is based on the implementation in the Wise Wolf project.
1. Architecture Overview
The system consists of three main parts:
- •Data Collection (Form): Collects user data (name, CPF, address, plan details) before the contract is shown.
- •Contract Rendering (Visual): A component that renders the contract text with dynamic data inserted. This component is what gets "printed" or converted to PDF.
- •Signature Modal (Interaction): A modal that overlays the screen, shows the contract, and captures the user's intent to sign (checkbox + typing name).
2. Dependencies
Ensure you have the following installed:
- •
react-to-print: For handling the printing/PDF generation logic. - •
lucide-react: For icons (ShieldCheck, Lock, etc.). - •Google Fonts (optional): 'Dancing Script' for a realistic signature look.
npm install react-to-print lucide-react
3. Implementation Steps
Step 1: Create the Contract Document Component
Create a component (e.g., ContractDocument.tsx) that acts as the "paper" contract. It should accept all contract data as props.
Key Features:
- •use
refto allowreact-to-printto grab the content. - •Use explicit inline styles or Tailwind classes for A4 dimensions (
w-[210mm] min-h-[297mm]). - •Include standard contract clauses.
- •Dynamic Signature Area: Render the signature based on whether it's signed (
acceptedAtprop is present).
// Example Snippet from ContractDocument.tsx
export function ContractDocument({ studentName, acceptedAt, userIp, ...props }) {
const componentRef = useRef(null);
// React-to-print hook (optional here if controlled by parent)
return (
<div ref={componentRef} className="w-[210mm] bg-white p-[25mm] ...">
{/* Contract Text */}
<h1>CONTRACT OF SERVICES</h1>
<p>I, {studentName}, ...</p>
{/* Signature Section */}
<div className="signature-area">
{acceptedAt ? (
<div className="digital-signature">
<span className="cursive-font">{studentName}</span>
<div className="metadata">
IP: {userIp} | Date: {acceptedAt}
</div>
</div>
) : (
<span>Waiting for signature...</span>
)}
</div>
</div>
);
}
Step 2: Create the Signature Modal
Create a modal (e.g., ContractModal.tsx) that forces the user to review and sign.
Key Logic:
- •Validation: Force the user to type their name exactly as it appears in the contract. This acts as a conscious act of signing.
- •Consent: A checkbox "I have read and agree...".
- •Visual Feedback: Show a live preview of the "cursive" signature using a font like
Dancing Script. - •Submission: When confirmed, pass a
signatureDataobject back to the parent.
// Example Snippet from ContractModal.tsx
const handleConfirm = () => {
if (accepted && typedName.trim().toLowerCase() === studentName.trim().toLowerCase()) {
onConfirm({
type: 'DIGITAL',
typedName: typedName.trim(),
timestamp: new Date().toISOString()
});
}
};
Step 3: Main Flow Integration (Registration Page)
In your main form (e.g., PublicRegistration.tsx):
- •
State Management:
- •
step: 'FORM' -> 'CONTRACT' -> 'SUCCESS'. - •
signatureData: Store IP, timestamp, and subscription ID after signing.
- •
- •
Process:
- •User fills form -> Clicks "Next".
- •
stepbecomes 'CONTRACT'. Modal opens. - •User signs ->
handleRegisteris called. - •Database Save: Create the user profile in Supabase with
contract_accepted: true,accepted_at, andsignature_ip. - •Backend Validation: Ensure your backend/Edge Function validates that these fields are present.
- •
PDF Generation (Post-Signing):
- •After success, render the
ContractDocumentagain (hidden or in a modal) with theacceptedAtandsignatureDataprops populated. - •Use
react-to-printto allow the user to download the signed copy.
- •After success, render the
4. Database Schema (Supabase)
Ensure your profiles table (or contracts table) has columns for audit trails:
alter table profiles add column contract_accepted boolean default false, add column accepted_at timestamptz, add column signature_ip text, add column signed_document_url text; -- Optional if storing the PDF file itself
5. Security & Legal Validity (Brazil - MP 2.200-2/2001)
To ensure legal validity (binding non-repudiation), you must capture:
- •Identity: Authentication (login) or strict personal data validation (CPF, Name match).
- •Intent: The mechanics of checking "I Agree" and typing the name acting as the signature.
- •Integrity: Generate a hash or store the exact data state at the time of signing (we use the
subscriptionIdor a generated UUID as a token). - •Traceability: Capture IP address and accurate Timestamp.
6. Edge Cases
- •User edits name: If the user changes their name in the form, the contract must re-render with the new name.
- •Mobile responsiveness: The
ContractDocumentis fixed A4 size210mm. On mobile, scale it down using CSStransform: scale(0.6)in the preview modal so it fits the screen.
Example Usage:
See components/PublicRegistration.tsx for the orchestration and components/ContractDocument.tsx for the template.