Atlas Plan
Plans007 2026 02 20 Dashboard Service

Set Up @services/dashboard

Overview

Build the @services/dashboard TanStack Start web application. Server functions route aggregated mart queries to DuckDB and individual record lookups to LibSQL (via @packages/db Drizzle). All 8 routes from the architecture spec are implemented with shadcn/ui + Tailwind. A period selector allows filtering dashboard views to any loaded month.

Result: bun run dev --filter @services/dashboard serves a live dashboard at localhost:15002 reading real mart data from atlas.db and operational records from atlas-ops.db.

Goals

  • Scaffold @services/dashboard as a TanStack Start workspace package
  • Implement root layout with sidebar navigation across all 8 routes
  • Implement all 5 aggregated views backed by DuckDB mart queries (overview, revenue, orders, programs, schools, marketing)
  • Implement 2 individual record views backed by LibSQL Drizzle queries (student profile, organization detail)
  • Register dashboard at port 15002 in NETWORK.yml
  • Verify: bun run dev --filter @services/dashboard serves all routes with real data

Non-Goals

  • Deploying to Cloudflare Pages or any remote host (local dev only in this plan)
  • Authentication or access control
  • Component reuse with @services/present — deferred (see architecture.md Open Questions)
  • Dark/light mode theming (can be added later)
  • Mobile responsiveness (desktop-first)
  • Real-time data updates / websockets
  • Data entry or editing (read-only dashboard)

Phases

  • Phase 1: Package scaffold (package.json, tsconfig, vite, app.config, components.json, NETWORK.yml)
  • Phase 2: Root layout + data client helpers
  • Phase 3: Aggregated views — DuckDB routes (overview, revenue, orders, programs, schools, marketing)
  • Phase 4: Individual record views — LibSQL routes (student profile, organization detail)
  • Phase 5: Integration — bun install, NETWORK.yml, type-check, dev verification

Success

  • @services/dashboard scaffolded with all config files
  • NETWORK.yml updated with dashboard at port 15002
  • bun run test:type --filter @services/dashboard passes
  • bun run dev --filter @services/dashboard starts without errors
  • All 8 routes accessible at localhost:15002 with real data
  • Revenue route shows mart_revenue data with a period selector
  • Student profile route shows a real person record from atlas-ops.db

Requirements

  • Plan 003 completed: all mart tables in atlas.db
  • Plan 005 completed: atlas-ops.db schema in place (may be unpopulated — student/org views can show empty state)
  • @packages/db available as workspace dependency
  • shadcn/ui components available (add via bunx shadcn@latest add or catalog)
  • TanStack Start, TanStack Router, TanStack Query in workspace catalogs
  • Vite + @cloudflare/vite-plugin (for future deploy compatibility, but local dev only in this plan)
  • @repo/typescript/tanstack tsconfig preset
  • @repo/lint/base ESLint config

Context

Why This Approach

  • TanStack Start for consistency with @services/plan and the planned architecture
  • Server functions (not a separate API) — each route fetches its own data via createServerFn; no REST layer needed
  • DuckDB for aggregated views: mart queries are fast analytical reads; no caching needed for local use
  • LibSQL + Drizzle for individual records: typed queries, FK joins, row-level access
  • shadcn/ui + Tailwind: matches the architecture spec; headless, composable components
  • Period selector as a shared UI element: all DuckDB routes accept a period param that defaults to the latest available period in the mart

Key Constraints

  • DuckDB duckdb-async node client is used in server functions only (not importable in client code)
  • LibSQL client from @packages/db used in server functions only
  • All server function data fetching must be use server annotated for TanStack Start SSR compatibility
  • Route _app.tsx is the shared layout wrapper; sidebar nav links to all 8 routes
  • routeTree.gen.ts must be seeded manually for initial type-check; regenerated on vite dev
  • Dashboard port: 15002 (ions uses 15xxx prefix; plan service is 15001)
  • School progress route renders the year × organization matrix — same pivot as the Format layer's SchoolProgressSection type

Edge Cases

  • Empty mart (no data loaded): all aggregated views show empty state (no crash)
  • Empty atlas-ops.db (schema only, no data): individual record views show "No records found"
  • Student or organization ID not found: 404 handled gracefully
  • DuckDB file atlas.db not present: server function throws with a clear error

Tradeoffs

  • No real-time updates: dashboard reads from static files; acceptable for the monthly reporting use case
  • No pagination on school progress or channel marketing tables: marts are small enough for full-page loads
  • shadcn/ui pre-built components: faster to implement than custom; some design constraints

Skills

  • plan — plan file format and conventions

Boundaries

  • Always: Update Progress.md after each task completion
  • Always: Server functions for all data fetching (no client-side DB access)
  • Always: Use catalog: references in package.json for all deps
  • Always: Use workspace:* for @repo/* and @packages/* references
  • Ask first: Any route not in the architecture spec
  • Ask first: Any deviation from TanStack Start file-based routing conventions
  • Never: Write to DuckDB or LibSQL from the dashboard (read-only)
  • Never: Expose raw SQL to the client

Questions

  • Port? → 15002
  • Data sources? → DuckDB for aggregated, LibSQL for individual records
  • UI library? → shadcn/ui + Tailwind
  • Should the period selector default to the latest available month in the mart, or require explicit selection?
  • Should the overview route show a per-unit summary card or a combined entity total?
  • Should /orders and /programs be separate routes or combined (both read mart_program_progress)?

On this page