Architecture diagram:
docs/architecture-diagram.png
Dataprint is a multi-tenant SaaS platform for firms that pursue public sector contracts. This includes — but is not limited to — government contracting, education, municipal services, transportation, and other public-sector verticals. The platform is currently focused on firms operating in Texas, with plans for broader expansion.
It centralises the full pursuit lifecycle — from opportunity discovery through proposal development to export — within a single workspace. Each firm (organization) gets a fully isolated environment. Teams collaborate on bids, use AI to analyze RFQs/RFPs, and produce compliant proposal documents.
┌─────────────────────────────────────────────────────────────────┐
│ FRONTEND (Next.js 16 / Vercel) │
│ Dashboard │ Pursuit Workspace │ Admin / Settings │
└────────────────────────┬────────────────────────────────────────┘
│ API Routes (Next.js Route Handlers)
┌────────────────────────▼────────────────────────────────────────┐
│ BACKEND SERVICES │
│ Clerk Auth │ Supabase (DB + RLS) │ RFQ Analyzer (AI) │
│ Export Engine (Word/PDF/InDesign) │
└────────────────────────┬────────────────────────────────────────┘
│
┌────────────────────────▼────────────────────────────────────────┐
│ EXTERNAL INTEGRATIONS │
│ MS Graph API (SharePoint + Teams) │ Claude API │
│ Adobe InDesign │ Public Procurement Sites │
└─────────────────────────────────────────────────────────────────┘
The frontend is a Next.js 16 App Router application deployed on Vercel. All authenticated pages live under the /(dashboard) route group and are wrapped in a sidebar layout.
| Route | Page | Status |
|---|---|---|
/ |
Main dashboard — KPI cards, pursuit trend chart, pursuit table | Live |
/dashboard |
Secondary dashboard view (static demo data) | Live |
/pursuits |
Pursuit list & management | Empty state (in progress) |
/opportunities |
Opportunity discovery from public procurement sites | Empty state |
/assets |
Digital assets (logos, boilerplate, past performance) | Empty state |
/database |
Org reference data (personnel, projects, clients) | Empty state |
/inbox |
Teams Chat integration / messaging | Empty state |
/templates |
Proposal section templates | Empty state |
/connections |
Partner firm connections | Empty state |
/settings |
Org admin settings (admin-only) | In progress |
RootLayout (app/layout.tsx)
└── ClerkProvider (auth context + shadcn theme)
└── ThemeProvider (light/dark mode)
└── TooltipProvider
├── DashboardLayout (app/(dashboard)/layout.tsx)
│ ├── AppSidebar (left navigation)
│ ├── SiteHeader (top bar)
│ └── ScrollArea (page body — only this section scrolls)
└── Toaster (global toast notifications)
- Inbox — pinned at top with primary action styling
- Workspace section: Dashboard, Pursuits, Opportunities, Assets, Database, Templates, Connections
- Settings — admin-only, conditionally rendered via
membership?.role === "org:admin" - NavUser — bottom user menu (profile, logout via Clerk
openUserProfile()/signOut())
| Package | Purpose |
|---|---|
next v16 |
Framework + App Router + Turbopack |
@clerk/nextjs |
Authentication UI + session management |
@tanstack/react-table |
Sortable/filterable data tables |
recharts |
Pursuit trend charts |
@dnd-kit/* |
Drag-and-drop (pursuit sections, kanban) |
@xyflow/react |
Node/graph visualization |
motion |
UI animations |
sonner |
Toast notifications |
tailwindcss v4 |
Utility-first styling |
shadcn / radix-ui |
Accessible component primitives |
@vercel/analytics |
Usage analytics |
Clerk handles all identity: sign-in, sign-up, SSO, and multi-org membership.
- Provider:
ClerkProviderin root layout wraps the entire app - Appearance:
shadcntheme from@clerk/themesapplied for visual consistency - Auth pattern: Every protected API route calls
auth()from@clerk/nextjs/serverto getuserIdandorgId - Org roles:
org:adminvsorg:member— admins see Settings, control org configuration - Webhook sync: All Clerk user/org events (created, updated, deleted) are synced to Supabase via
/api/webhooks/clerk— see Data Layer section
User signs in via Clerk
→ Clerk issues session JWT
→ orgId + userId available in all server components / API routes
→ Supabase queries filtered by organization_id
Supabase is the primary data store. All data is namespaced by organization_id for multi-tenant isolation.
Key Tables:
| Table | Contents |
|---|---|
organizations |
Firm-level records synced from Clerk |
users |
Member records synced from Clerk, linked to orgs |
pursuits |
Active bid/proposal records |
projects |
Past performance project records |
personnel |
Staff records (bios, certs, roles) |
assets |
Digital assets (images, files, logos) |
Client Types:
lib/supabase/client.ts— Browser-safe client using anon key + RLSlib/supabase/admin.ts— Server-only service role client (bypasses RLS, for trusted API routes)lib/supabase/clerk-client.ts— Supabase client initialized with Clerk JWT for RLS-aware server requests
The RFQ Analyzer reads uploaded solicitation documents and extracts structured data to pre-fill pursuit records and suggest relevant personnel, projects, and proposal sections.
- Model: Claude (via
@ai-sdk/anthropic+ Vercel AI SDK) - API Route:
/api/chat— streaming edge function - Runtime: Edge (low-latency streaming responses)
- Features:
- Streams responses in real-time to the frontend
- System prompt context injection
- Attachment/document support
- Reasoning/chain-of-thought display in the chat UI
User uploads RFQ/RFP document
→ /api/chat receives document + prompt
→ Anthropic Claude analyzes content
→ Streams structured analysis back to UI
→ Suggestions populate pursuit workspace fields
The Export Engine converts a completed pursuit workspace into formatted proposal documents.
Target output formats:
- Word (.docx) — standard proposal format
- PDF — for final submission
- InDesign (.indd) — for print-ready designed proposals via Adobe InDesign integration
Complexity factors:
- Must maintain proposal section order, formatting, and compliance structure
- InDesign export requires server-side scripting or Adobe APIs
- Multi-section document assembly from disparate data sources (sections, personnel, projects, assets)
- Page layout, headers/footers, table of contents generation
The Pursuit Workspace is the core product feature — a dedicated per-pursuit environment where proposal teams collaboratively build out a bid response. It is a child route of /pursuits/[id]/.
| Module | Function |
|---|---|
| RFQ/RFP Analysis | Upload solicitation, AI extracts requirements, sets up workspace |
| Proposal Sections | Section-by-section proposal writing with AI write-up suggestions |
| Personnel | Assign team members; AI suggests best-fit staff from org database |
| Projects | Attach relevant past performance; AI suggests matches from project database |
| Documents | Store and manage supporting files for the pursuit |
| Export | Generate final proposal in Word, PDF, or InDesign format |
RFQ/RFP uploaded
→ Claude analyzes → extracts requirements, NAICS, due date, scope
→ Pursuit record pre-populated in Supabase
→ AI suggests: Personnel (from personnel table)
+ Projects (from projects table)
+ Section structure (from templates)
→ Team fills/refines each section
→ Export Engine compiles into final document
Data security is enforced at two levels:
All tables have RLS policies that restrict reads/writes to rows where organization_id matches the authenticated user's org. No org can ever see another org's data — enforced at the database level.
All API routes additionally filter by organization_id derived from Clerk's orgId. This is a defense-in-depth measure on top of RLS.
/api/webhooks/clerk keeps Supabase in sync with Clerk as the source of truth for identity:
Clerk event fires (user.created / org.created / etc.)
→ Webhook validated via Svix signature
→ Supabase upsert to organizations / users tables
→ org metadata, member roles, avatars all synced
- SharePoint Sync — sync assets and documents to/from SharePoint document libraries
- Teams Chat — the Inbox surface; send/receive messages, @mention pursuit collaborators
- Powers the AI Assistant chat interface
- Powers the RFQ/RFP Analyzer
- Accessed via Vercel AI SDK (
@ai-sdk/anthropic) for streaming support - API key configured via
ANTHROPIC_API_KEYenv variable
- Target export format for print-ready designed proposals
- Integration path: InDesign Server API or scripted file generation (.indd / IDML)
- Highest complexity integration — requires layout engine and style mapping
- Sources for opportunity discovery (SAM.gov, USASpending, state portals)
- Data flows into the Opportunities page
- Likely via scraping or official APIs (SAM.gov API)
| Route | Method | Auth | Purpose |
|---|---|---|---|
/api/chat |
POST | Clerk | Streaming AI chat (Claude) |
/api/pursuits |
GET | Clerk + Org | Fetch org's pursuit records |
/api/projects |
GET | Clerk + Org | Fetch org's project records |
/api/personnel |
GET | Clerk + Org | Fetch org's personnel records |
/api/webhooks/clerk |
POST | Svix sig | Sync Clerk events to Supabase |
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY |
Clerk frontend key |
CLERK_SECRET_KEY |
Clerk backend key |
CLERK_WEBHOOK_SECRET |
Svix webhook signature validation |
NEXT_PUBLIC_SUPABASE_URL |
Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Supabase browser client key |
SUPABASE_SERVICE_ROLE_KEY |
Supabase admin key (server only) |
ANTHROPIC_API_KEY |
Claude AI API key |
AZURE_CLIENT_ID / AZURE_TENANT_ID |
Microsoft Graph OAuth |
Based on the architecture, the logical build order is:
- Pursuits list page — real data from Supabase, create/edit pursuit records
- Pursuit Workspace — individual pursuit route with the module workflow
- Personnel & Projects database — org reference data management
- RFQ/RFP Analyzer — Claude integration within pursuit workspace
- Opportunities — procurement site data ingestion
- Assets — file/asset management with Supabase Storage
- Teams Chat / Inbox — MS Graph integration
- SharePoint Sync — MS Graph document sync
- Export Engine — Word/PDF first, InDesign last (highest complexity)
Last updated: 2026-02-25