Skip to main content

Legal Documents Management System

Overview

The Legal Documents Management System enables administrators to manage Terms of Service and Privacy Policy documents with version control, flexible enforcement options, and comprehensive audit trails. Users must affirmatively accept these documents on first joining and whenever updates are made.

Key Features

Version Management

  • Semantic Versioning: Documents use standard versioning (1.0.0, 1.1.0, 2.0.0)
  • One Active Version: Only one document version per type can be active at a time
  • Immutable History: Published documents cannot be edited (create new version instead)
  • Draft Support: Save as draft and publish later

Flexible Enforcement

  • Immediate Acceptance: Users blocked from platform until they accept
  • Grace Period: Users see dismissible banner; access blocked after X days
  • Admin Configurable: Choose enforcement per update

Audit & Compliance

  • Complete Audit Trail: Track IP address, user agent, and exact timestamp of every acceptance
  • User History: See when each user accepted which version
  • CSV Export: Download acceptance records for compliance
  • Analytics: View acceptance rates and statistics

User Experience

  • Modal Interface: Clean modal with tab interface (Terms | Privacy)
  • Grace Period Banner: Smart countdown timer with color escalation
  • Settings Integration: Users can review accepted documents anytime
  • Fallback Support: Works offline with localStorage backup

Architecture

Data Model

LegalDocument
├── id (CUID)
├── type (terms | privacy)
├── version (string - semantic)
├── title (string)
├── content (text - full HTML/markdown)
├── effectiveDate (datetime)
├── publishedAt (datetime) - null if draft
├── publishedBy (email)
├── isActive (boolean) - only one per type
├── requiresImmediate (boolean)
├── gracePeriodDays (number)
└── acceptances (relationship)

DocumentAcceptance
├── id (CUID)
├── userId (string)
├── documentId (string)
├── version (string - denormalized)
├── acceptedAt (datetime)
├── ipAddress (string - optional)
└── userAgent (string - optional)

API Endpoints

Public Endpoints (no auth required):

  • GET /legal/current/:type - Get current active document
  • POST /legal/accept - User accepts document
  • GET /legal/status - Check if user needs to accept

Admin Endpoints (AdminClerkAuthGuard):

  • GET /legal/admin/documents - List all versions
  • GET /legal/admin/documents/:id - Get single document
  • POST /legal/admin/documents - Create new version
  • PUT /legal/admin/documents/:id - Update draft
  • POST /legal/admin/documents/:id/publish - Publish & activate
  • DELETE /legal/admin/documents/:id - Delete unpublished
  • GET /legal/admin/acceptances - View logs with filtering
  • GET /legal/admin/documents/:id/analytics - Get statistics

User Flows

New User Registration

  1. User signs up via Clerk
  2. Redirected to platform
  3. AuthenticatedLayout checks /legal/status API
  4. No previous acceptance → Blocking TermsAcceptanceModal appears
  5. User reads terms in tabs
  6. User checks agreement checkbox
  7. User clicks "Accept Terms"
  8. API call to /legal/accept records acceptance
  9. Database persists: User ID, Document ID, Version, IP, Timestamp
  10. User granted platform access

Update to Terms (Immediate Enforcement)

  1. Admin creates new version (e.g., 2.0.0)
  2. Admin publishes document
  3. Previous v1.0.0 marked as inactive/archived
  4. On next user login:
    • API checks /legal/status
    • Detects userVersion (1.0.0) ≠ currentVersion (2.0.0)
    • Shows blocking modal with new terms
    • User must accept v2.0.0 before accessing platform
  5. Acceptance recorded in database

Update to Terms (Grace Period)

  1. Admin creates new version (e.g., 2.0.0)
  2. Admin publishes with:
    • requiresImmediate: false
    • gracePeriodDays: 7
  3. On next user login:
    • API checks /legal/status
    • Returns grace period info
    • Frontend shows GracePeriodBanner instead of blocking modal
    • Banner shows: "New Terms available. Review by [date]"
    • User can dismiss and continue using platform
  4. After 7 days:
    • Banner converts to blocking modal
    • User must accept to continue

Review Anytime in Settings

  1. User goes to Settings page
  2. Scrolls to "Legal Agreements" section
  3. Sees:
    • Terms: "Accepted • Version 2.0.0" with ✓ indicator
    • Privacy: "Accepted • Version 1.5.0" with ✓ indicator
    • Latest version info below each
  4. Clicks "Review" button on either
  5. Modal opens with current documents
  6. Can read and review anytime

Admin Panel

  • Features:
    • Tab switching between Terms & Privacy
    • Three sections: Active Version | Drafts | Archived
    • Status badges and acceptance counts
    • Dropdown actions: View, Edit, Duplicate, Delete
    • Empty states with helpful CTAs

Create/Edit Document (/legal/new or /legal/[id]/edit)

  • Form Fields:
    • Document type selector (disabled when editing)
    • Version number (semantic versioning)
    • Title for display
    • Content textarea (large, for full legal text)
    • Effective date picker
    • Enforcement Settings:
      • Toggle: "Require Immediate Acceptance"
      • Conditional: Grace Period Days input
  • Actions:
    • Save as Draft (for later editing)
    • Save & Publish (goes live immediately)
    • Preview modal (shows as users will see it)

Document Details Page (/legal/[id])

  • Display:
    • Full document content
    • Enforcement settings card
    • Version and publication info
  • Two Tabs:
    • Content: Display full text
    • Analytics:
      • Total acceptances (number)
      • Total users (number)
      • Acceptance rate (%)
      • Recent acceptances table (last 20 + link to all)
  • Actions:
    • Publish (for drafts only)
    • Edit (for drafts only)
    • Duplicate (for any version)

Acceptance Logs Page (/legal/acceptances)

  • Filters:
    • Document type (All/Terms/Privacy)
    • Email search
    • Real-time filtering
  • Statistics Cards:
    • Total acceptances
    • Showing count
    • Last 7 days
  • Table Display:
    • User email, name
    • Document type (badge)
    • Version accepted
    • Acceptance date & time
    • IP address (hidden on mobile)
  • Pagination: Previous/Next navigation
  • CSV Export: Download all filtered records with headers

Frontend Components

TermsAcceptanceModal

  • Props:
    • isOpen: boolean
    • onAccept: () => void
    • requiresImmediate?: boolean
  • Features:
    • Tab interface (Terms | Privacy)
    • Fetches current documents from API
    • Large content area (scrollable)
    • Agreement checkbox (required)
    • Loading states
    • Error handling with fallback
    • Toast notifications

GracePeriodBanner

  • Props:
    • type: "terms" | "privacy"
    • gracePeriodDays: number
    • publishedDate: string
    • onReviewClick: () => void
    • onDismiss: () => void
  • Features:
    • Sticky top banner
    • Countdown timer (updates every minute)
    • Color escalation: Amber (normal) → Red (3+ days left)
    • "Review Now" button
    • Dismiss button (X)
    • Auto-hides when grace period expires

AuthenticatedLayout Integration

  • On Mount:
    • Calls /legal/status API
    • Determines if acceptance needed
    • Shows appropriate UI (modal or banner)
  • Fallback: Uses localStorage if API fails
  • Grace Period Logic:
    • If immediate: show blocking modal
    • If grace period: show dismissible banner

Settings Page

  • New Section: Legal Agreements
    • Status cards for Terms & Privacy
    • Shows accepted version
    • Shows latest version
    • Green ✓ if current
    • "Review" button for each
    • Integrated with TermsAcceptanceModal

Database Schema

-- Legal Documents Table
CREATE TABLE legal_documents (
id TEXT PRIMARY KEY,
type TEXT NOT NULL, -- 'terms' | 'privacy'
version TEXT NOT NULL,
title TEXT NOT NULL,
content TEXT NOT NULL,
effective_date TIMESTAMP NOT NULL,
published_at TIMESTAMP,
published_by TEXT,
is_active BOOLEAN DEFAULT false,
requires_immediate BOOLEAN DEFAULT true,
grace_period_days INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP,

UNIQUE(type, version),
INDEX(type),
INDEX(is_active),
INDEX(published_at)
);

-- Document Acceptance Records (Audit Trail)
CREATE TABLE document_acceptances (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
document_id TEXT NOT NULL,
version TEXT NOT NULL,
accepted_at TIMESTAMP DEFAULT NOW(),
ip_address TEXT,
user_agent TEXT,

UNIQUE(user_id, document_id, version),
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY(document_id) REFERENCES legal_documents(id) ON DELETE CASCADE,
INDEX(user_id),
INDEX(document_id),
INDEX(accepted_at)
);

-- User Table (extended fields for legal documents)
ALTER TABLE users ADD COLUMN (
terms_accepted_at TIMESTAMP,
terms_version TEXT,
privacy_policy_accepted_at TIMESTAMP,
privacy_policy_version TEXT
);

Security & Compliance

Access Control

  • Admin Guard: All admin endpoints protected by AdminClerkAuthGuard
  • User Auth: User endpoints require ClerkAuthGuard
  • Email Whitelist: Only whitelisted emails can access admin panel

Data Protection

  • Immutable Records: Published documents cannot be edited
  • Version Tracking: Each acceptance tied to specific version
  • Audit Trail: IP + user agent logged with each acceptance
  • Atomic Transactions: Publishing deactivates old version atomically

Compliance Features

  • GDPR Ready: Tracks consent with timestamp and details
  • Audit Export: CSV download for legal records
  • User Rights: Users can review accepted documents anytime
  • Change History: Full version history maintained

Best Practices

For Admins

  1. Version Numbers: Use semantic versioning

    • 1.0.0 = Initial release
    • 1.1.0 = Minor updates (formatting, clarification)
    • 2.0.0 = Major changes (new rules, terms)
  2. Enforcement Strategy:

    • Immediate: Material changes that users must know about
    • Grace Period: Minor updates, improved UX (3-7 days typical)
  3. Publishing Workflow:

    • Create version as draft
    • Review content (use preview button)
    • Choose enforcement type
    • Publish when ready
    • Old version auto-archives
  4. Audit & Tracking:

    • Regularly check acceptance logs
    • Export records for compliance
    • Monitor acceptance rates
    • Follow up on non-compliant users

For Users

  1. First Login: Read and accept both terms and privacy policy
  2. Updates: Respond promptly to new versions
  3. Settings: Review accepted documents anytime
  4. Questions: Contact support if unclear

Example Workflows

Publishing New Terms of Service

Admin Panel → Legal Docs → Create Document
├─ Type: Terms of Service
├─ Version: 2.0.0
├─ Content: [paste new terms]
├─ Effective Date: [select date]
├─ Enforcement: Require Immediate ✓
└─ Click: Save & Publish

Result:
├─ Previous v1.0.0 → Archived
├─ New v2.0.0 → Active
└─ Users see blocking modal on next login

Grace Period Update

Admin Panel → Legal Docs → Create Document
├─ Type: Privacy Policy
├─ Version: 1.5.0
├─ Content: [paste updated policy]
├─ Enforcement: Grace Period
├─ Grace Days: 7
└─ Click: Save & Publish

Result:
├─ Users see dismissible banner
├─ Deadline shown: 7 days from now
├─ After 7 days → Modal becomes blocking
└─ Acceptance recorded with version

Viewing Analytics

Admin Panel → Legal Docs → [Click Document]
├─ View analytics tab
├─ See:
│ ├─ Total acceptances: 245/300 users
│ ├─ Acceptance rate: 81.7%
│ └─ Recent acceptances table
└─ Click "Acceptances" to view all records

Troubleshooting

Users not seeing modal

  • ✅ Check /legal/status API response
  • ✅ Verify user version ≠ active version
  • ✅ Check browser console for errors
  • ✅ Clear localStorage and refresh
  • ✅ Verify /legal/current/:type endpoint
  • ✅ Check network tab in DevTools
  • ✅ Verify API is running
  • ✅ Check CORS headers

Acceptance not recorded

  • ✅ Verify POST to /legal/accept succeeds
  • ✅ Check user is authenticated
  • ✅ Verify database connection
  • ✅ Check for validation errors

Can't edit document

  • ✅ Document must be in Draft status
  • ✅ Published docs cannot be edited
  • ✅ Create new version for changes
  • ✅ Click "Duplicate" to copy content

Additional Resources