Skip to content

Feature Overview

Project Work Portal (apps/project-work-portal)

Section titled “Project Work Portal (apps/project-work-portal)”
  • JWT login/logout with session persistence
  • Role-based access (admin, member, viewer)
  • Admin account creation (DevTools, gated by ALLOW_REGISTRATION env var)
  • Team member invite system — coming soon
  • Service-unavailable UX when auth server is unreachable
  • Time-of-day greeting with user’s first name (morning/afternoon/evening icons)
  • Date display and contextual summary line (callbacks, new leads, expiring estimates)
  • Stat cards with colored icon badges (leads, customers, projects, estimates)
  • “Needs Attention” widget (stale leads, upcoming callbacks)
  • Recent Activity feed
  • “Continue where you left off” section (last 4 viewed leads/customers/projects via localStorage)
  • Callback reminders with “Mark Complete” action
  • Leads table with sortable columns, pagination, search
  • Pipeline status column with plant emoji header, sortable
  • Health status dot merged into name column (green/amber/red)
  • Temperature dot indicator
  • Submission count badge (multi-submit detection)
  • Filter bar with typed syntax (lead_status:pending, temperature:hot, health:stale)
  • Pipeline summary bar with status counts and “Needs Attention” filter
  • Column visibility toggling (show/hide optional columns)
  • Lead detail page with sticky header, status dropdown, action buttons
  • Lead scoring with signal breakdown pills
  • Lead-to-customer conversion with loading toast and redirect to new customer profile
  • Reject/archive lead with confirmation modal
  • Customers table with sortable columns, pagination, search
  • Customer detail page with stat cards (active projects, total value, customer since)
  • Contact info and property info cards
  • Converted-from-lead link back to original lead record
  • Collapsible “Notes & Activity” card with NoteEditor and activity timeline
  • Rich NoteEditor with markdown support (powered by marked library)
  • Side-by-side markdown preview (“Preview Markdown” button splits editor in half)
  • Note templates (Site Visit Summary, Phone Call Follow-up, Estimate Discussion, Issue Report)
  • Image upload via button, paste, or drag-and-drop
  • Image resize before upload (client-side)
  • Auto-save drafts to localStorage with 500ms debounce
  • Draft persistence across SPA navigation (flush on unmount)
  • Draft restored indicator
  • beforeunload protection (warns on unsaved content)
  • Activity timeline with type icons and relative timestamps
  • Toast notifications on note save (success/error)
  • Global ToastProvider with useToast() hook
  • 5 variants: success, error, warning, info, loading
  • Auto-dismiss (configurable duration, default 5s) or persistent (manual dismiss)
  • In-place update (e.g. loading spinner transitions to success)
  • Bottom-right positioning with slide-in animation
  • Dismiss button on persistent and error toasts
  • “Open in Google Maps” link (uses address or lat/lng fallback)
  • More coming soon
  • Search dropdown from header (top 5 results per category)
  • Full search page at /search with grouped results (leads, customers, projects)
  • Server-side search across contact names, emails, phones, addresses
  • Full quote/estimate CRUD with inline line item editing
  • Catalog-based item selection (ItemSelector dropdown) or custom items
  • Display IDs: QT-{seq}-{rev} format (e.g. QT-1-A, QT-1-B for revisions)
  • Revision system: revising a sent/accepted quote creates a new revision, supersedes original
  • Clone: independent copy with new identity (not a revision)
  • Status lifecycle: draft → sent → viewed → accepted → expired → rejected → converted → superseded → archived
  • Tax rate with per-quote override (tenant default, inline “Override” button)
  • Expiration dates with no-past-date validation and TTL from tenant config
  • Auto-expire on list view (transitions viewed quotes past expiration to expired)
  • Expiration reminder scheduling (stubbed email, ready for provider integration)
  • Quote preview modal (Eye button) — renders HTML in sandboxed iframe, works for saved and unsaved quotes
  • Share link generation with configurable visible contact fields (name, email, phone, property)
  • Public unauthenticated quote view at /quote/:token with optional PDF download button
  • PDF generation (browser print API with HTML fallback)
  • Convert-to-project flow (creates project + tasks from line items)
  • Renew expired quotes with new expiration date
  • Archive/unarchive with Active/Archived pill toggle (glowing blue border on active pill)
  • Duplicate-to-builder from archived quotes (pre-fills title, tax settings)
  • File attachments per quote via R2 storage (drag-and-drop upload, file type/size hints)
  • Accepted file types: PDF, JPG, PNG, WEBP, DOCX, XLSX — Max 10MB
  • Public attachment download via share token
  • Undo/Redo (10-step history) with keyboard-style Undo2/Redo2 buttons
  • Clear confirmation modal (danger variant)
  • Save as Draft (keeps builder open) vs Save Quote (clears builder)
  • Edit collision check (warns if builder has unsaved work)
  • Locked banner for non-draft quotes with “Create Revision” shortcut
  • Activity logging on save (estimate_draft type)
  • Theme toggle (light/dark mode)
  • Sidebar hover-expand toggle (default: on, persisted to localStorage)
  • Company/office info editing (name, phone, email, address)
  • Office address geocoding on save
  • Quotes feature toggle (enables catalog management)
  • Contractor token TTL configuration (1-24 hours)
  • Default tax rate configuration
  • Force password change toggle (per-tenant)

DevTools Panel (Settings page, admin-only)

Section titled “DevTools Panel (Settings page, admin-only)”
  • Generate fake leads (10/50/100) with realistic scoring distribution
  • Generate fake customers (10/50/100) via intake pipeline (creates leads, then converts)
  • Soft-delete and hard-delete all leads/customers
  • System info display (DB size, record counts, environment, tenant ID)
  • Health checks (API, Portal, Intake Form) with lucide icons and polling
  • Create user account (gated by ALLOW_REGISTRATION deployment toggle)
  • Reset user password with optional force-password-change flag
  • User list with lock icon for forced password change users
  • BrowserStorageAdapter for dev/preview (IndexedDB, blob URLs, fully offline)
  • ApiStorageAdapter for production (proxied to /api/v1/uploads for R2/S3)
  • Auto-selects adapter by environment, overridable via VITE_STORAGE_ADAPTER
  • Toast notifications: uploading spinner, success, file-too-large, unsupported type, errors
  • File extension and size validation (10MB limit, images only)
  • Collapsible sidebar with hover-expand (smooth CSS transition)
  • Sidebar stays in document flow (no overlay), main content reflows
  • Breadcrumb navigation on detail pages
  • Recently viewed tracking (leads, customers, projects) with cross-tab sync
  • i18n system with English string table (packages/shared/src/i18n/en.ts)
  • Template interpolation ({{name}}, {{count}})
  • All user-facing strings use translation keys

  • Multi-step intake form (mobile-first PWA)
  • Project type selection (roofing, siding, gutters, flooring, etc.)
  • Dynamic question sets per project type
  • Contact info collection (name, email, phone, address)
  • Urgency selection
  • Material preferences
  • Form validation with Zod schemas
  • Email notification on submission
  • CORS-enabled health endpoint
  • Contractor auth via search (/contractor routes)
  • Multi-step inspection flow with question sets per project type
  • Multi-photo capture per question (MultiPhotoUpload component)
  • Configurable minimum photo count per question
  • Client-side image resize before upload
  • Offline support via Service Worker (sw.js)
  • IndexedDB persistence for offline data
  • SyncStatus badge showing online/offline/syncing state
  • Queue drain logic on reconnect (retry with exponential backoff)
  • inspectionId passthrough across all steps

  • Leads: list, get, create, update, delete, convert-to-customer
  • Customers: list, get, create, update
  • Projects: list, get, create, update
  • Estimates: list, get, create, update, duplicate (revise), clone, archive/unarchive, renew, convert-to-project
  • Estimate attachments: upload, list, delete, download (authenticated + public via share token)
  • Estimate share: generate share token with configurable visible fields, public HTML view
  • Estimate preview: render HTML from builder data without persisting
  • Estimate PDF: HTML rendering with optional browser print
  • Activities: list, recent, create
  • Catalog: items CRUD, unit types CRUD (feature-gated)
  • Register (creates admin + tenant)
  • Login (returns JWT), with force-password-change flow
  • Change password endpoint (purpose-scoped JWT)
  • Current user info (GET /api/v1/auth/me)
  • Contractor token generation (configurable TTL, purpose claim)
  • Invite team member — coming soon
  • Accept invite — coming soon
  • Password hashing via Web Crypto API (PBKDF2-SHA256, CF Workers compatible)
  • Stats aggregation (counts, pipeline breakdown)
  • “Needs Attention” query (stale leads, upcoming callbacks)
  • Computed health_status from last_contacted_at
  • Cross-entity search (leads, customers, projects)
  • POST /v1/geocode — on-demand Nominatim geocoding
  • Auto-geocode on lead/customer create and update
  • Distance calculation from office
  • Generate fake leads/customers with realistic scoring (customers via intake pipeline)
  • Soft-delete and hard-delete leads/customers
  • System info (DB size via PRAGMA, record counts)
  • Registration status check
  • Admin account creation (deployment-gated)
  • User management: list users, reset password, toggle force-password-change
  • Dev error dashboard at API root (HTML, last 50 errors, 60s auto-refresh off by default)
  • POST /api/v1/uploads — file upload with validation
  • Estimate attachments: upload (multipart/form-data), list, delete, download
  • Public attachment download via share token with expiry check
  • R2 storage backend (Cloudflare R2)
  • POST /api/v1/webhooks/intake — intake form submission webhook
  • GET /api/v1/health — health check endpoint
  • Centralized URI prefixes via SERVICE_PREFIXES and apiPath() builder in packages/shared
  • Tenant-scoped data isolation (all queries filtered by tenant_id)
  • Drizzle ORM with Cloudflare D1
  • PRAGMA try/catch for remote D1 compatibility
  • Tenant_id indexes on all tables for efficient count queries

  • Zod schemas for all API inputs (leads, customers, projects, estimates, auth, attachments)
  • TypeScript types and interfaces
  • Lead scoring engine (30+ weighted rules, temperature classification)
  • Phone number normalization (E.164 via normalizePhone())
  • Phone display formatting
  • Display value formatter (snake_case to Title Case)
  • i18n string table with interpolation
  • Query key constants for React Query
  • Drizzle ORM schema definitions
  • Constants/enums (lead statuses, temperatures, health statuses, user roles, estimate statuses)
  • Centralized URI prefix constants (SERVICE_PREFIXES) and path builders (apiPath(), portalPath(), intakePath())
  • Attachment constants (accepted types, max size, MIME type map)

  • ci-api-deploy.yml — API preview + production deploy via Wrangler
  • ci-portal-deploy.yml — Portal preview + production deploy via CF Pages
  • ci-intake-deploy.yml — Intake form deploy
  • deploy-stack.yml — Manual full-stack deploy
  • test-suite.yml — Manual test runner
  • Preview deploys on dev branch push
  • Post-deploy login smoke tests with CI service account
  • Auth smoke test (warning, not blocking)
  • Health endpoint verification
  • E2E regression suite (17 tests): login, all pages load, no broken images, no failed requests, sidebar reflow, markdown preview, toast notifications, draft persistence
  • E2E DevTools tests (generate/delete data)
  • E2E contractor inspection flow tests
  • Unit tests (resize-image, idb-storage)
  • API proxy via CF Pages functions (/api/v1/** catch-all)
  • Cloudflare D1 (SQLite)
  • 11 migrations (init, geocoding, indexes, inspection drafts, force password change, quote builder, estimates status, expiration reminders, attachments, archived status)
  • Dev seed script with test account (suadmin@dev.local, password via DEFAULT_SUADMIN_PASSWORD env var) and 1200+ deterministic short IDs
  • Geocode backfill script