Skip to content

Dashboard Architecture

The dashboard is a Next.js 14 App Router application that provides a browser-based interface for AI Legal UK's analysis skills. It supports demo mode (fixture data, zero API cost) and live mode (real supported provider API calls with the user's own key).

Technology Stack

LayerTechnologyPurpose
FrameworkNext.js 14 (App Router)Server-side rendering, API routes, file-system routing
UI Componentsshadcn/ui + Radix UI primitivesAccessible, composable component library
StylingTailwind CSS + CSS variablesUtility-first styling with theme support (light/dark)
LanguageTypeScriptType safety across client and server
API Clients@anthropic-ai/sdk plus fetch-based OpenAI Responses adapterProvider-backed live analysis
StatelocalStorage (client-side)Reviews, API key, mode preference

Page Structure (9 Pages)

All pages use "use client" directives for client-side interactivity.

PathComponentDescription
/app/page.tsxMain dashboard with skill cards, mode toggle, and quick-start actions
/reviewapp/review/page.tsxUpload and analyse a document -- file picker, skill selector, progress display
/review/[id]app/review/[id]/page.tsxCompleted review with score gauge, risk heatmap, clause cards, recommendations
/compareapp/compare/page.tsxSide-by-side contract comparison with diff highlighting
/legislationapp/legislation/page.tsxBrowse and search UK legislation via the MCP server
/skillsapp/skills/page.tsxFull skill catalog with descriptions and categories
/generateapp/generate/page.tsxDocument generator for NDAs, terms, agreements, board packs
/historyapp/history/page.tsxPast review history loaded from localStorage
/settingsapp/settings/page.tsxAPI key configuration, mode preferences, theme settings

API Routes (6 Endpoints)

EndpointMethodDescription
/api/reviewPOSTMain analysis endpoint. Returns SSE stream when Accept: text/event-stream, otherwise buffered JSON
/api/comparePOSTContract comparison. Supports SSE streaming and JSON fallback
/api/generatePOSTDocument generation (NDA, terms, agreements, board packs)
/api/anthropic/validatePOSTValidates an Anthropic key for the current settings UI; review execution also accepts OpenAI through the provider adapter
/api/document/extractPOSTServer-side text extraction from uploaded files (multipart/form-data)
/api/legislation/searchGETProxies search requests to legislation.gov.uk

Core Library Modules (dashboard/lib/)

api.ts -- Central API Integration

The largest and most critical module. Contains the provider-neutral prompt orchestration plus Anthropic and OpenAI review adapters.

SKILL_PROMPTS Map (26 Skills)

A Record<string, string> mapping skill IDs to their analysis prompts. Each prompt defines the persona, assessment criteria, and output format for a specific skill:

typescript
const SKILL_PROMPTS: Record<string, string> = {
  "legal-review": `You are an expert UK commercial contract lawyer...`,
  "legal-employment": `You are an expert UK employment lawyer...`,
  "legal-ir35": `You are an expert UK tax and employment status specialist...`,
  "legal-compliance": `You are an expert UK regulatory compliance auditor...`,
  "legal-risks": `...`,
  "legal-missing": `...`,
  "legal-plain": `...`,
  "legal-freelancer": `...`,
  "legal-property": `...`,
  "legal-corporate": `...`,
  "legal-negotiate": `...`,
  "legal-aml": `...`,
  "legal-consumer": `...`,
  "legal-esg": `...`,
  "legal-dispute": `...`,
  "legal-ai-compliance": `...`,
  "legal-benchmark": `...`,
  "legal-due-diligence": `...`,
  "legal-regulatory-calendar": `...`,
  "legal-legislation-tracker": `...`,
  "legal-gdpr": `...`,
  "legal-tenancy": `...`,
  "legal-ip": `...`,
  "legal-debt": `...`,
  "legal-immigration": `...`,
  "legal-wills": `...`,
};

REVIEW_OUTPUT_SCHEMA -- Shared Review Schema

A shared JSON schema named submit_review standardises structured review output across providers. The schema defines all possible fields across all review types:

SectionFields
Commontype (enum: contract/employment/ir35/compliance), documentName, score, grade, summary, clauses[], recommendations[], metadata
IR35-specificir35Score, status (inside/outside/borderline), confidence, factors[], riskIndicators[], contractAmendments[], financialExposure
Employment-specificera2025Dashboard[], equalityActMatrix[], obligations[], financialExposure
Compliance-specificframeworks[], checkItems[]

Required fields: type, documentName, summary, metadata.

SKILL_TO_TYPE Mapping

Maps every skill in the live-review allowlist (defined in dashboard/lib/api-route-utils.ts as VALID_LIVE_REVIEW_SKILLS) to one of four review types. The allowlist itself is the single authoritative source of "which skills can run live"; the table below is a snapshot for reference only:

Review TypeSkills
contractlegal-review, legal-risks, legal-missing, legal-plain, legal-freelancer, legal-property, legal-corporate, legal-negotiate, legal-dispute, legal-benchmark, legal-due-diligence, legal-tenancy, legal-ip, legal-debt, legal-wills
employmentlegal-employment
ir35legal-ir35
compliancelegal-compliance, legal-aml, legal-consumer, legal-esg, legal-ai-compliance, legal-regulatory-calendar, legal-legislation-tracker, legal-gdpr, legal-immigration

runSkillAnalysis(apiKey, skillId, documentText)

Non-streaming analysis function (kept for backward compatibility):

  1. Looks up the skill prompt from SKILL_PROMPTS
  2. Selects the requested provider adapter and passes the user API key
  3. Calls the provider adapter with the shared prompt, document text, and review schema
  4. Extracts the provider structured output
  5. Calls buildReview() to construct a typed Review object
  6. Falls back to a generic review wrapper when a provider returns unstructured text

streamSkillAnalysis(apiKey, skillId, documentText)

Returns a ReadableStream<Uint8Array> of SSE events for real-time progress:

  1. Uses Anthropic streaming where available; non-streaming providers fall back to buffered analysis
  2. Emits progress events as chunks arrive (mapped to stages: "Analysing clauses...", "Reviewing provisions...", "Scoring risks...", "Finalising recommendations...")
  3. Progress percent scales from 15% to 85% based on chunk count
  4. On completion, emits progress at 100% followed by result with the complete Review JSON
  5. On error, emits error event with the message
typescript
// SSE event format
function sseEvent(event: string, data: unknown): string {
  return `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
}

api-route-utils.ts -- Request Validation

Shared validation logic for all API route handlers.

ExportPurpose
VALID_LIVE_REVIEW_SKILLSReadonly array of skill IDs enabled for live analysis — the canonical allowlist consulted on every POST /api/review
MAX_LIVE_DOCUMENT_CHARS50_000 -- maximum character count for uploaded documents
ANTHROPIC_VALIDATE_URLhttps://api.anthropic.com/v1/messages
ANTHROPIC_VALIDATE_MODELDefault: claude-sonnet-4-20250514. Override with the ANALYSIS_MODEL env var.
validateReviewRouteBody()Validates API key (must start with sk-), skill (must be in allowlist), text (non-empty, within char limit)
normalizeApiKey()Trims whitespace from API key strings
mapReviewRouteError()Maps error messages to appropriate HTTP status codes (400, 401, 429, 500)
buildAnthropicValidationRequest()Constructs a minimal API request to validate a key

Validation Flow

Request body → normalizeApiKey() → validateReviewRouteBody()
                                         ├─ 401: Missing/invalid API key
                                         ├─ 400: Invalid skill or missing text
                                         ├─ 413: Document exceeds 50,000 chars
                                         └─ 200: Valid (returns apiKey, skill, text)

document-extraction.ts -- Text Extraction

Server-side text extraction supporting 4 file formats:

FormatPrimary MethodFallback
.txtRaw UTF-8 read--
.mdRaw UTF-8 read--
.docxtextutil -convert txt -stdout (macOS)python3 -c "from docx import Document..." (python-docx)
.pdfpdftotext -layout (poppler)python3 -c "from pypdf import PdfReader..." (pypdf)

The module auto-detects available backends at startup and caches the result. If PDF text extraction yields insufficient text (scanned document), it falls back to OCR.

document-ocr.ts -- OCR Fallback

OCR fallback for scanned PDFs. Triggered when pdftotext or pypdf returns minimal text.

ProviderStatusConfiguration
openaiImplementedOPENAI_API_KEY + OCR_OPENAI_MODEL env vars
googleStubRecognised but not implemented
awsStubRecognised but not implemented
azureStubRecognised but not implemented
tesseractStubRecognised but not implemented

Provider selection: OCR_PROVIDER or AI_LEGAL_UK_OCR_PROVIDER env var.

review-utils.ts -- Review Lifecycle

Helpers for creating, updating, and transforming review objects. Includes scoring helpers and review merge logic.

storage.ts -- Client-Side Persistence

Uses localStorage for all client-side state:

KeyContentType
ai-legal-uk-modeCurrent mode"demo" or "live"
ai-legal-uk-reviewsSaved review objectsReview[] (JSON-serialised)
ai-legal-uk-api-keyUser.s provider API keystring

Cross-tab synchronisation via a custom ai-legal-uk:storage-sync event and the native storage event.

FunctionPurpose
getMode() / setMode()Read/write demo or live mode
getApiKey() / setApiKey() / clearApiKey()Manage the API key
getReviews() / saveReview()Read all reviews / merge and save a new review
subscribeToStorageSync()Subscribe to storage change events across tabs

skill-registry.json / skills-data.ts -- Skill Catalog

dashboard/lib/skill-registry.json is the canonical registry for all 38 skills. skills-data.ts derives the dashboard Skill[] array from that registry so routes, input contracts, and live-review capability metadata stay in one place:

typescript
interface Skill {
  id: string;        // e.g., "legal-review"
  name: string;      // e.g., "Contract Review"
  command: string;    // e.g., "/legal review"
  category: SkillCategory;
  description: string;
  route: "/review" | "/compare" | "/generate" | "/history" | "/skills";
  inputContract: "text" | "comparison" | "generation" | "report" | "utility";
  liveReview: boolean;
}

Categories: Contract Analysis, Property Law, Document Generation, Compliance & Reporting, Employment & Corporate, Consumer & ESG, Platform Tools, Business Intelligence, Specialist.

types.ts -- Type Definitions

The Review union type and all supporting interfaces:

typescript
type Review = ContractReview | EmploymentReview | IR35Assessment | ComplianceAudit
InterfaceKey Fields
ContractReviewtype: 'contract', score, grade, clauses[], recommendations[], metadata
EmploymentReviewtype: 'employment', era2025Dashboard[], equalityActMatrix[], obligations[], financialExposure
IR35Assessmenttype: 'ir35', ir35Score, status (inside/outside/borderline), confidence, factors[]
ComplianceAudittype: 'compliance', frameworks[], checkItems[]

Supporting types: Clause, Recommendation, DocumentMetadata, ERA2025Check, EqualityActCheck, Obligation, FinancialExposure, IR35Factor, FrameworkScore, ComplianceCheckItem.

legislation.ts and linkify-statutes.tsx

  • legislation.ts -- Statute data and types for the legislation search UI (LegislationSearchResult, LegislationDetail)
  • linkify-statutes.tsx -- React component that automatically converts statute references (e.g., "Companies Act 2006") in analysis text into clickable links to legislation.gov.uk

UI Components (dashboard/components/)

Layout Components

ComponentDetails
Sidebar280px fixed width on desktop. Collapsible as a Radix Sheet on mobile. Contains skill categories with navigation links.
HeaderTop bar with mode toggle (demo/live) and theme toggle (light/dark). Shows current page title.

Analysis Components

ComponentDetails
FileUploadDrag-and-drop zone + file picker + paste support. Accepts PDF, DOCX, TXT, MD. Shows upload progress.
ScoreGaugeSVG circular gauge displaying score 0--100. Animated on mount. Colour-coded: green (75+), amber (50--74), red (below 50).
RiskHeatmapInteractive grid showing risk distribution across clauses. Cells colour-coded by severity. Clickable to jump to clause detail.
ClauseCardExpandable card for individual clause analysis. Shows clause reference, title, risk score badge, issues list, and recommendation. Copy-to-clipboard support.

Control Components

ComponentDetails
ModeToggleSwitches between demo (fixture data) and live (API) modes. When switching to live, validates the API key by calling /api/anthropic/validate. Shows error if key is invalid.
ThemeToggleLight/dark theme switcher using CSS variables and next-themes. Persists preference.

Theming

The dashboard uses CSS variables for theming, allowing light and dark modes:

  • Colour variables defined in globals.css using HSL values
  • Theme switching via next-themes with class strategy
  • shadcn/ui components automatically respect the active theme
  • All custom components use Tailwind CSS utilities that reference the CSS variables

API Key Security Model

Important Security Design

The dashboard server does not store or hold any ANTHROPIC_API_KEY. The user's API key is stored exclusively in their browser's localStorage and sent with each request body. Route handlers pass it directly to the Anthropic SDK without any server-side persistence.

Browser (localStorage) → Request body → Route handler → Provider adapter → Model API

This design means:

  • No server-side secrets management required
  • Each user brings their own API key and billing
  • The server cannot access analysis data after the request completes
  • API keys never touch disk on the server side
  • No user accounts or authentication needed

AI Legal UK · The Counsel — Established MMXXVI · Built for England & Wales · Not legal advice.