Skip to content

Staff App Routes & Behaviors

Reference documentation for all routes, components, and behaviors.


Route Summary

Path Component Auth Description
/login LoginPage No Email/password or magic link login
/auth/verify VerifyPage No Magic link verification
/splash SplashPage No Animated branding splash for recordings
/ DashboardPage Yes Overview stats, recent clients
/clients ClientsPage Yes Client list with search, aggregated status
/clients/new CreateClientPage Yes New client form
/pending-uploads PendingUploadsPage Yes Upload docs first, then create client
/clients/:clientId ClientDetailPage Yes Client info, returns list, actions
/clients/:clientId/returns/:returnId/upload DocumentUploadPage Yes Drag-drop document upload
/returns/:returnId/analysis AnalysisDashboardPage Yes 3-panel workspace (main review interface)
/queue QueuePage Yes Review workflow queue
/review/:returnId Redirects Yes Legacy route - redirects to /returns/:id/analysis

Public Routes

/login -> LoginPage

File: frontend/apps/staff-app/src/pages/LoginPage.tsx

Layout: Centered card with logo

Behavior: - Two login modes: Magic link (default) or password - Toggle between modes via footer button - Magic link: Enter email -> sends link -> shows confirmation - Dev mode: Shows clickable magic link for testing - Password: Enter email + password -> redirects to / - Preserves ?demo= param through login

State: - email, password - form inputs - usePassword - toggle between login modes - magicLinkSent - shows confirmation screen - devMagicLink - dev mode quick link

API calls: - useAuth().requestMagicLink(email) - useAuth().login(email, password)


/auth/verify -> VerifyPage

File: frontend/apps/staff-app/src/pages/VerifyPage.tsx

Layout: Centered card with status icon

Behavior: - Reads ?token= from URL - Calls verifyMagicLink(token) on mount - Shows loading -> success -> redirects to / - Or shows error with "Back to login" button

State: - status: "loading" | "success" | "error" - error: error message if failed

API calls: - useAuth().verifyMagicLink(token)


/splash -> SplashPage

File: frontend/apps/staff-app/src/pages/SplashPage.tsx

Layout: Full-screen dark gradient with centered branding

Behavior: - Staggered fade-in animation (logo -> title -> subtitle -> footer) - Auto-advances to / after 5 seconds (configurable via ?delay=) - Click anywhere or press any key to skip - ?delay=0 disables auto-advance

State: - showLogo, showTitle, showSubtitle, showFooter - animation states

URL params: - ?delay=3000 - custom auto-advance delay in ms - ?delay=0 - no auto-advance


Protected Routes (require authentication)

/ -> DashboardPage

File: frontend/apps/staff-app/src/pages/DashboardPage.tsx

Layout: Header + 3 stat cards + recent clients list

Behavior: - Shows total clients, returns in progress (placeholder), pending actions - Lists 5 most recent clients with status badge - Click client -> navigates to /clients/:id - "New Client" button -> /clients/new - "View all" link -> /clients

API calls: - api.getClients() - fetches all clients for stats

Data displayed: - Total clients count - Active/pending breakdown - Recent client list with avatar, name, email, status


/clients -> ClientsPage

File: frontend/apps/staff-app/src/pages/ClientsPage.tsx

Layout: Header + search bar + clients table

Behavior: - Search filters by name, email, account number (client-side) - Table shows: Client name, Account #, Email, Phone, Status, Created date - Status shows aggregated return status (not client.status) - Also shows return count and latest year under status - Click "View" -> navigates to /clients/:id - "New Client" button -> /clients/new

State: - searchQuery - search input value

API calls: - api.getClients() - fetches all clients

Status display logic: - in_review, ready_for_review, etc -> warning (yellow) - intake, documents_pending -> secondary (gray) - filed, accepted, complete -> success (green) - rejected -> destructive (red) - no_returns -> default


/clients/new -> CreateClientPage

File: frontend/apps/staff-app/src/pages/CreateClientPage.tsx

Layout: Centered form card

Behavior: - Form fields: First name, Last name, Email*, Phone (optional) - Checkbox: Send welcome email (default: checked) - Validates required fields on submit - Creates client -> redirects to /clients/:newId - Cancel -> back to /clients

State: - formData - form field values - error - validation/API error message

API calls: - api.createClient(formData) - creates new client


/pending-uploads -> PendingUploadsPage

File: frontend/apps/staff-app/src/pages/PendingUploadsPage.tsx

Layout: Header + upload zone + current upload batch + empty state

Behavior: - Upload documents without assigning to a client first - Large drag-drop zone for files - "Browse Files" button for file picker - "Import Folder" button for folder upload (webkitdirectory) - Shows upload progress with spinner - Documents grouped by batch (current session only) - Current upload shows: file icon, filename, type/classifying status, size, tax year badge - "Create Client" button opens CreateClientFromDocumentsDialog: - Pre-populates client info from AI extraction (if available) - Creates client, return, and assigns documents in one operation - Navigates to /clients/:newId on success - Empty state shown when no uploads present

State: - currentBatchId - unique batch ID for current session - uploadedDocs - documents uploaded in current session - uploading - upload in progress - isDragOver - drag hover state - dialogOpen - create client dialog visibility - selectedBatch - batch passed to create client dialog

API calls: - api.uploadPendingDocument(file, batchId) - uploads to pending storage - api.getPendingBatches() - fetches existing batches (disabled, future use)

Related Component: - CreateClientFromDocumentsDialog - dialog for creating client from batch - Calls POST /v1/batch/:batchId/create-client - Creates client + return + moves documents in one transaction


/clients/:clientId -> ClientDetailPage

File: frontend/apps/staff-app/src/pages/ClientDetailPage.tsx

Layout: Client header + 2-column info cards + returns table + actions card

Behavior: - Shows client info: name, account number, email, phone, dates - Tax Returns table with: Year, Type, Status, Documents, Updated, Actions - "New Return" button opens dialog: - Select tax year (2024, 2023, 2022) - Select return type (1040, 1120, 1120S, 1065, 990) - Creates return -> navigates to /clients/:id/returns/:newReturnId/upload - Return row actions: - Upload icon -> /clients/:id/returns/:rid/upload - Chart icon -> /returns/:rid/analysis - "Review" link -> /review/:rid - Actions card (disabled): Edit Client, Send Portal Invite, Create Engagement Letter - ChatDrawer floating in bottom-right

State: - isNewReturnOpen - dialog visibility - newReturnYear, newReturnType - dialog form values

API calls: - api.getClient(clientId) - api.getClientReturns(clientId) - api.createReturn(clientId, { tax_year, return_type })


/clients/:clientId/returns/:returnId/upload -> DocumentUploadPage

File: frontend/apps/staff-app/src/pages/DocumentUploadPage.tsx

Layout: Header + upload zone + document list

Behavior: - Large drag-drop zone for files - "Browse Files" button for file picker - "Import Folder" button for folder upload (webkitdirectory) - Shows upload progress with spinner - Document list shows: icon, filename, type, size, status badge - Status: "Processed" (green check) or "Processing" (spinner) - Duplicate detection (DUP-001): On upload, if file hash matches existing: - Same client: Shows "Duplicate Document" dialog with "View Original" / "Cancel" - Different client: Shows "Document Belongs to Another Client" dialog with "View in [Client]" / "Cancel" - "Continue to Analysis" -> /returns/:rid/analysis - "Save & Return to Client" -> /clients/:cid

State: - documents - newly uploaded docs (local state) - uploading - upload in progress - isDragOver - drag hover state - duplicateInfo - duplicate detection response from API (DuplicateInfo | null) - duplicateFilename - filename that triggered duplicate detection

API calls: - api.getClient(clientId) - api.getReturn(returnId) - api.getReturnDocuments(returnId) - existing docs - api.uploadDocument(returnId, file) - per file - Returns 201 on success - Returns 409 on duplicate with { error, is_same_client, existing_document }


/returns/:returnId/analysis -> AnalysisDashboardPage

File: frontend/apps/staff-app/src/pages/AnalysisDashboardPage.tsx

Layout: 3-panel workspace

┌──────────────────┬──────────────────────┬──────────────────────┐
│   PDF Viewer     │   Documents & Info   │   AI Chat            │
│   (40%)          │   (35%)              │   (25%)              │
└──────────────────┴──────────────────────┴──────────────────────┘

Behavior: - Header: Back button, client name, return info, panel toggle buttons - Left panel: PDF viewer (iframe) - Shows selected document - Auto-selects first document on load - Center panel: Document list with actions - AI analysis banner (blue) with "Run AI Analysis" button - AI insights summary when analysis complete (questions, missing docs, anomalies, recommendations counts) - Drag-drop zone: Entire document list area accepts file drops - Visual feedback: Blue ring and background tint on drag-over - Drop hint appears when dragging or when list is empty - Document list with: icon, filename, type, size, status, action buttons - Click document -> loads in PDF viewer - "Upload" button opens file picker (no longer navigates away) - Action buttons per document: - Tag icon: Reclassify (dropdown menu with document types) - Arrow icon: Move to another client (dialog with client/return selectors) - Trash icon: Delete (confirmation dialog) - Duplicate detection (DUP-001): On upload, if file hash matches existing: - Same client: Shows "Duplicate Document" dialog with "View Original" / "Cancel" - Different client: Shows "Document Belongs to Another Client" dialog with "View in [Client]" / "Cancel" - Right panel: AI Chat sidebar - Uses useChat hook - ChatHistory + ChatInput components - Panel toggle buttons in header to show/hide left/right panels

State: - leftPanelOpen, rightPanelOpen - panel visibility - selectedDocId - document shown in PDF viewer - analysisRequested - triggers AI analysis fetch - showReclassifyMenu, showMoveDialog, showDeleteConfirm - action dialogs - moveTargetClientId, moveTargetReturnId - move dialog selections - isDragOver, uploading - drag-drop upload state - duplicateInfo, duplicateFilename - duplicate detection dialog state

API calls: - api.getReturn(returnId) - api.getReturnDocuments(returnId) - api.getClient(clientId) - api.getAIAnalysis(returnId) - lazy loaded on button click - api.deleteDocument(docId) - api.reclassifyDocument(docId, type) - Chat: useChat({ clientId, taxReturnId })


/queue -> QueuePage

File: frontend/apps/staff-app/src/pages/QueuePage.tsx

Layout: Header + 4 stat cards + filter tabs + returns list

Behavior: - Stats: Total in queue, In Preparation, Ready for Review, AI Analysis Ready - Filter tabs: All Returns, Preparation, Review - Returns list shows: Client name, status badge, AI Ready badge, return type/year, doc count, updated date - Click return -> navigates to /review/:returnId

State: - returns - fetched queue data - loading - loading state - filter - "all" | "prep" | "review"

API calls: - api.getReviewQueue() - fetches returns in queue


/review/:returnId -> Redirect (DEPRECATED)

Status: DEPRECATED - Redirects to /returns/:returnId/analysis

This legacy route now redirects to the new 3-panel workspace. The ReviewPage component is no longer used but kept for reference.

All navigation has been updated: - ClientDetailPage "Open" button -> /returns/:id/analysis - QueuePage row click -> /returns/:id/analysis - Direct URL /review/:id -> redirects to /returns/:id/analysis


User Flows

New Client Flow

/clients -> /clients/new -> (create) -> /clients/:id

New Return Flow

/clients/:id -> "New Return" dialog -> (create) -> /clients/:id/returns/:rid/upload

Document Upload Flow

/clients/:id/returns/:rid/upload -> (upload docs) -> /returns/:rid/analysis

Document Review Flow

/clients/:id -> click return -> /returns/:rid/analysis
                              -> view PDFs, ask AI questions, manage documents

Queue Review Flow

/queue -> click return -> /review/:rid -> (review) -> Approve -> /queue

Upload-First Flow (Documents Before Client)

/pending-uploads -> (upload docs) -> "Create Client" -> (dialog) -> /clients/:newId

Component Dependencies

Shared Components

  • MainLayout - wraps all protected routes, provides navigation sidebar
  • ChatDrawer - floating chat button (ClientDetailPage)
  • ChatHistory + ChatInput - embedded chat (AnalysisDashboardPage, ReviewPage)

Shared Hooks

  • useAuth - authentication state and methods
  • useChat - chat session management

UI Library

  • All routes use components from @tax-practice/ui:
  • Card, Button, Badge, Input, Label, Table, Dialog, Select
  • api client for backend calls

API Endpoints Used

Endpoint Method Used By
/v1/clients GET DashboardPage, ClientsPage
/v1/clients POST CreateClientPage
/v1/clients/:id GET ClientDetailPage, DocumentUploadPage, AnalysisDashboardPage
/v1/clients/:id/returns GET ClientDetailPage
/v1/clients/:id/returns POST ClientDetailPage
/v1/returns/:id GET DocumentUploadPage, AnalysisDashboardPage, ReviewPage
/v1/returns/:id/documents GET DocumentUploadPage, AnalysisDashboardPage, ReviewPage
/v1/returns/:id/documents POST DocumentUploadPage, AnalysisDashboardPage, ReviewPage
/v1/returns/:id/ai/analysis GET AnalysisDashboardPage, ReviewPage
/v1/returns/:id/ai/chat GET DEPRECATED - use /chat/sessions/*
/v1/returns/:id/status PATCH ReviewPage
/v1/documents/:id DELETE AnalysisDashboardPage
/v1/documents/:id/content GET AnalysisDashboardPage (PDF viewer)
/v1/documents/:id/move POST AnalysisDashboardPage
/v1/documents/:id/reclassify POST AnalysisDashboardPage
/v1/chat/sessions POST useChat hook
/v1/chat/sessions/:id GET useChat hook
/v1/chat/sessions/:id/messages POST useChat hook
/v1/queue GET QueuePage
/v1/pending-documents POST PendingUploadsPage (upload)
/v1/pending-documents/batches GET PendingUploadsPage (future)
/v1/batch/:batchId/create-client POST CreateClientFromDocumentsDialog

Last updated: 2025-12-28