Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
4f21395
Merge pull request #1 from OliTamrat/claude/merge-to-main-A3mjA
OliTamrat Mar 10, 2026
7d649f3
Update package-lock.json after npm install
OliTamrat Mar 11, 2026
8f47e85
Add Phase 1 production readiness: error boundary, health check, tests…
OliTamrat Mar 11, 2026
d711449
Add Phase 2 production readiness: logging, validation, CSP headers, d…
OliTamrat Mar 11, 2026
a29fb0b
Add Phase 3 backend: SQLite database, API routes, USGS ingestion, dat…
OliTamrat Mar 11, 2026
8d93553
Complete Phase 3 frontend migration + Phase 4 Docker, docs, architecture
OliTamrat Mar 11, 2026
4b59ca9
Add API integration tests and .env.example for deployment readiness
OliTamrat Mar 11, 2026
32e08d9
Harden security and add data provenance transparency
OliTamrat Mar 11, 2026
22eabf0
Fix CI test failures: ensure data directory exists and seed is idempo…
OliTamrat Mar 11, 2026
767afce
Fix Vercel production config: standalone output, read-only FS, health…
OliTamrat Mar 11, 2026
159750f
Merge pull request #2 from OliTamrat/claude/audit-production-readines…
OliTamrat Mar 11, 2026
ab737bb
Add Neon PostgreSQL support with SQLite fallback for local dev
OliTamrat Mar 11, 2026
98e149a
Add credential security rules and commit attribution policy to CLAUDE.md
OliTamrat Mar 11, 2026
d5ba15f
Fix USGS ingest: replace discontinued sites with active monitoring st…
OliTamrat Mar 11, 2026
46179ae
Merge pull request #3 from OliTamrat/claude/audit-production-readines…
OliTamrat Mar 11, 2026
a99581d
Add Vercel Cron job for automated USGS data ingestion every 6 hours
OliTamrat Mar 11, 2026
cdde449
Fix null crashes when USGS data has missing fields
OliTamrat Mar 11, 2026
366d63f
Merge pull request #4 from OliTamrat/claude/audit-production-readines…
OliTamrat Mar 11, 2026
99833a8
Fix CSP to allow Vercel Live and fix null eColiCount crash
OliTamrat Mar 11, 2026
7978d8d
Merge pull request #5 from OliTamrat/claude/audit-production-readines…
OliTamrat Mar 11, 2026
79a197c
Comprehensive null safety + CSP fix for Vercel deployment
OliTamrat Mar 11, 2026
748f5f0
Merge pull request #6 from OliTamrat/claude/audit-production-readines…
OliTamrat Mar 11, 2026
6be7546
Fix Vercel cron: use daily schedule for Hobby plan
OliTamrat Mar 11, 2026
cb96d9a
Merge pull request #7 from OliTamrat/claude/audit-production-readines…
OliTamrat Mar 11, 2026
c2f547e
Add EPA WQX integration, data validation, methodology page, provenanc…
OliTamrat Mar 11, 2026
90de262
Merge pull request #8 from OliTamrat/claude/audit-production-readines…
OliTamrat Mar 11, 2026
43d01de
Add drill-down modals, enrich education/research/dashboard content
OliTamrat Mar 11, 2026
bb5e2c8
Make navigation responsive for tablets and mobile
OliTamrat Mar 11, 2026
476ad74
Merge pull request #9 from OliTamrat/claude/audit-production-readines…
OliTamrat Mar 11, 2026
68c48e0
Fix header overflow and footer layout on mobile screens
OliTamrat Mar 11, 2026
b0126e5
Comprehensive responsive fixes across all 5 pages and components
OliTamrat Mar 11, 2026
5a15f23
Merge pull request #10 from OliTamrat/claude/audit-production-readine…
OliTamrat Mar 11, 2026
bd3e6d0
Improve text contrast and readability across both themes
OliTamrat Mar 11, 2026
307edfc
Merge pull request #11 from OliTamrat/claude/audit-production-readine…
OliTamrat Mar 11, 2026
f9d7870
Fix theme dropdown, UDC campus coordinates, and light mode map tiles
OliTamrat Mar 11, 2026
6ada589
Correct station coordinates against USGS/DC GIS verified data
OliTamrat Mar 11, 2026
b263f7c
Merge pull request #12 from OliTamrat/claude/audit-production-readine…
OliTamrat Mar 11, 2026
f733fa4
Redesign footer and fix search to include researcher names
OliTamrat Mar 11, 2026
ce41f57
Merge pull request #13 from OliTamrat/claude/audit-production-readine…
OliTamrat Mar 11, 2026
4785541
Add AI-powered Research Assistant with Claude integration
OliTamrat Mar 11, 2026
181bf56
Add faculty admin panel for data management and upload
OliTamrat Mar 11, 2026
e9a8f4f
Merge pull request #14 from OliTamrat/claude/audit-production-readine…
OliTamrat Mar 11, 2026
c352c5c
Fix AI chat API error on Vercel and improve mobile chat panel
OliTamrat Mar 11, 2026
1b62d5e
Merge pull request #15 from OliTamrat/claude/audit-production-readine…
OliTamrat Mar 11, 2026
3bf96fe
Fix AI chat: replace Zod v4 schemas with jsonSchema, improve error ha…
OliTamrat Mar 11, 2026
959ab2c
Merge pull request #16 from OliTamrat/claude/audit-production-readine…
OliTamrat Mar 11, 2026
e0722b9
Add ANTHROPIC_API_KEY to .env.example
OliTamrat Mar 12, 2026
88b4e27
Remove leaked ANTHROPIC_API_KEY from .env.example
OliTamrat Mar 12, 2026
76add23
Fix chat: switch to claude-haiku-4-5 and improve error details
OliTamrat Mar 12, 2026
c9c066b
Add chat API hardening, rate limiting, CSRF protection, and English/S…
OliTamrat Mar 12, 2026
c8e16d9
Fix DC boundary outline with accurate 119-point polygon
claude Mar 12, 2026
22ba9bd
Merge pull request #19 from OliTamrat/claude/audit-production-readine…
OliTamrat Mar 12, 2026
20d547d
Update CLAUDE.md to prohibit Claude attribution URLs
claude Mar 12, 2026
19f4741
Make map legend collapsible and mobile-friendly
claude Mar 12, 2026
97117ee
Merge pull request #20 from OliTamrat/claude/audit-production-readine…
OliTamrat Mar 12, 2026
4375edc
Fix AI chat panel z-index to stay above Leaflet map
claude Mar 12, 2026
b869136
Merge pull request #21 from OliTamrat/claude/audit-production-readine…
OliTamrat Mar 12, 2026
d07728d
Add functional Settings modal and secure admin auth
claude Mar 12, 2026
5d58ff8
Fix Turbopack root resolution for monorepo-like setups
claude Mar 12, 2026
629a611
Add error handling for better-sqlite3 native module failures
claude Mar 12, 2026
2958e8d
Merge pull request #22 from OliTamrat/claude/audit-production-readine…
OliTamrat Mar 12, 2026
1af98ca
Secure admin dashboard and fix stale station data display
claude Mar 12, 2026
5ef68bc
Merge main into feature branch, resolve admin page conflict
claude Mar 12, 2026
3d4b271
Force dynamic rendering on all data API routes
claude Mar 12, 2026
c1a9a4c
Add WB-001/HR-001 to USGS ingestion and fix stale date display
claude Mar 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules
.next
.git
.github
*.md
!README.md
.env*.local
data/*.db
data/*.db-wal
data/*.db-shm
24 changes: 24 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# UDC Water Resources Dashboard — Environment Variables
# Copy this file to .env.local and fill in your values

# ---------- Database ----------
# Option 1: SQLite (local development, default)
# DB_PATH=./data/udc-water.db

# Option 2: Neon PostgreSQL (production / Vercel)
# When DATABASE_URL is set, the app uses PostgreSQL instead of SQLite.
# DATABASE_URL=postgresql://user:password@ep-xxx.us-east-1.aws.neon.tech/neondb?sslmode=require

# ---------- API Security ----------
# API key for the /api/ingest endpoint (optional locally, required in production)
# Generate with: openssl rand -hex 32
# INGEST_API_KEY=your-secret-key-here
# ---------- AI Research Assistant ----------
# Required for the AI chat feature (optional — dashboard works without it)
# Get your key from: https://console.anthropic.com
# ANTHROPIC_API_KEY=sk-ant-xxx

# ---------- Admin Panel ----------
# Required for /admin access in production (optional locally — open access in dev)
# Generate with: openssl rand -hex 32
# ADMIN_API_KEY=your-admin-key-here
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Seed database
run: npm run db:seed

- name: Run tests
run: npm test

- name: Build
run: npm run build
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# database
/data/*.db
/data/*.db-wal
/data/*.db-shm

# env files
.env*.local

Expand Down
122 changes: 122 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# UDC Water Resources Data Dashboard - Project Memory

## Rules for Claude Code

### Security — NEVER Expose Credentials
- **NEVER** commit, log, or output credentials, API keys, database connection strings, passwords, or tokens
- **NEVER** hardcode secrets in source files — always use environment variables
- When referencing `DATABASE_URL`, `INGEST_API_KEY`, or similar, use placeholder values like `postgresql://user:password@host/db`
- If a user shares a credential in conversation, do NOT echo it back in code, commits, or file contents
- `.env.local` is gitignored — secrets belong there, never in tracked files

### Git Commit & Push Rules
- **NEVER** include Claude Code session attribution URLs in commit messages, PR descriptions, or code comments
- **NEVER** include any `https://claude.ai/code/...` links anywhere in the codebase or git history
- Never amend someone else's commit — always create new commits
- Write clear, descriptive commit messages summarizing the "why"

## Project Overview
Interactive water quality monitoring dashboard for UDC's Water Resources Research Institute (WRRI) and CAUSES.
Built with Next.js 16.1.6 (App Router), TypeScript, Tailwind CSS 4, Leaflet, Recharts, React 19.

## Current State (as of March 2026 audit)
- **17 TSX/TS component files**, 2 data files, 4 app pages, 12 monitoring stations
- **Entirely static/client-side** — no backend, no database, no real APIs
- **Geospatial data** derived from official DC GIS government sources (verified)
- **Theme system** working (dark/light/system) with localStorage persistence

## Production Readiness Audit — Issues to Address

### Phase 1: Critical (Error Handling & Testing) — DONE
- [x] **Error Boundary** — `src/components/ErrorBoundary.tsx` wraps app in layout.tsx
- [x] **Testing** — Vitest configured, 17 tests across 4 suites (data, error boundary, health, validation)
- [x] **CI/CD** — `.github/workflows/ci.yml` (test + build on push/PR)
- [x] **Health check** — `GET /api/health` returns status, timestamp, version, uptime
- [x] **Functional search** — Header search filters stations, research, pages; tooltips on placeholder buttons

### Phase 2: Important (Logging, Monitoring, Validation) — DONE
- [x] **Logger utility** — `src/lib/logger.ts` with buffered client-side logging (info/warn/error)
- [x] **Input validation** — `src/lib/validation.ts` with XSS sanitization, applied to Header search
- [x] **Deployment docs** — README updated with Docker, Vercel, health check, and testing instructions
- [x] **Security headers** — CSP, X-Content-Type-Options, X-Frame-Options, Referrer-Policy in next.config.ts

### Phase 3: Backend & Data — DONE (Local SQLite, Azure-ready)
- [x] **Database** — SQLite via better-sqlite3 (`data/udc-water.db`), schema in `src/lib/db.ts`
- [x] **Seed script** — `npm run db:seed` populates DB from static data (12 stations, 144 readings)
- [x] **API routes** — `GET /api/stations`, `GET /api/stations/:id/history`, `GET /api/export`
- [x] **Data export** — CSV and JSON export via `/api/export?format=csv&station=ANA-001`
- [x] **USGS ingestion** — `POST /api/ingest?source=usgs` fetches real USGS NWIS instantaneous values
- [x] **Ingestion logging** — `ingestion_log` table tracks all ingest runs with status and error messages
- [x] **Neon PostgreSQL** — `@neondatabase/serverless` + `ws`; `DATABASE_URL` env var switches from SQLite
- [ ] **Cron scheduling** — Set up Azure Functions Timer or Vercel Cron for automated ingestion
- [x] **Frontend migration** — StationTable, MetricCards, station detail page fetch from API with static fallback

### Phase 5: AI Research Assistant — DONE
- [x] **AI Chat API** — `POST /api/chat` with Claude via Vercel AI SDK v6
- [x] **Domain system prompt** — EPA thresholds, seasonal patterns, station metadata, WRRI research context
- [x] **Tool-augmented** — AI can query `/api/stations`, `/api/stations/:id/history` for live data
- [x] **Chat UI** — Floating panel (`ResearchAssistant.tsx`) with streaming, suggested questions, clear history
- [x] **Graceful degradation** — Shows config message when `ANTHROPIC_API_KEY` not set
- [ ] **RAG expansion** — Vector search over research papers and USGS reports (future)
- [ ] **Chart generation** — AI-generated plots from query results (future)

### Phase 4: Nice-to-Have — DONE (Docker, Docs)
- [x] Contributing guidelines — `CONTRIBUTING.md`
- [x] Architecture diagrams — ASCII diagram in README
- [x] Docker/Kubernetes configs — `Dockerfile`, `docker-compose.yml`, `.dockerignore`
- [ ] User authentication/authorization (currently API-key based)

### Phase 6: Faculty Admin Panel — DONE
- [x] **Admin page** — `/admin` with auth gate (ADMIN_API_KEY env var)
- [x] **CSV/JSON upload** — Drag-and-drop with auto column mapping + validation
- [x] **AI-assisted column mapping** — Claude Haiku maps non-standard column names to schema (falls back to heuristics)
- [x] **Station CRUD** — Add, edit, delete stations from `/api/admin/stations`
- [x] **Readings CRUD** — View, add, delete readings from `/api/admin/readings` with pagination
- [x] **Ingestion trigger** — Run USGS/EPA ingestion from admin UI
- [x] **Ingestion log viewer** — Full history of all data imports
- [x] **Sidebar link** — "Data Admin" nav item under ADMIN section

## Database Setup
- **Local dev**: SQLite via better-sqlite3 (default, no config needed)
- **Production**: Neon PostgreSQL — set `DATABASE_URL` env var on Vercel
- **Seed Neon**: `DATABASE_URL=postgresql://... npx tsx scripts/seed.ts`
- **Seed local**: `npx tsx scripts/seed.ts` (or `npm run db:seed`)
- **Schema**: Dual schemas in `src/lib/db.ts` (SQLite + PostgreSQL), auto-selected
- **Future**: Optionally migrate to Azure PostgreSQL (same `DATABASE_URL` swap)

## Key Files
- `src/app/page.tsx` — Main dashboard
- `src/app/layout.tsx` — Root layout with ThemeProvider + ErrorBoundary
- `src/app/station/[id]/page.tsx` — Station detail pages
- `src/app/api/stations/route.ts` — Stations list API
- `src/app/api/stations/[id]/history/route.ts` — Station history API
- `src/app/api/export/route.ts` — CSV/JSON data export
- `src/app/api/ingest/route.ts` — USGS data ingestion
- `src/app/api/chat/route.ts` — AI research assistant (Claude via Vercel AI SDK)
- `src/app/api/health/route.ts` — Health check endpoint
- `src/app/admin/page.tsx` — Faculty admin panel (upload, CRUD, logs)
- `src/app/api/admin/upload/route.ts` — CSV/JSON upload with auto column mapping
- `src/app/api/admin/stations/route.ts` — Station CRUD API
- `src/app/api/admin/readings/route.ts` — Readings CRUD API
- `src/app/api/admin/ai-map-columns/route.ts` — AI-powered column mapping
- `src/lib/db.ts` — Database abstraction (SQLite + Neon PostgreSQL)
- `src/lib/logger.ts` — Client-side logging utility
- `src/lib/validation.ts` — Input sanitization
- `src/components/ai/ResearchAssistant.tsx` — AI chat panel (floating widget)
- `src/components/map/DCMap.tsx` — Interactive Leaflet map (dynamic import, SSR disabled)
- `src/components/layout/Header.tsx` — Top bar with functional search
- `src/components/ErrorBoundary.tsx` — React error boundary
- `src/data/dc-waterways.ts` — 801 lines: stations, waterways, research, EJ data
- `src/data/dc-boundaries.ts` — Ward polygons, watershed, flood zones
- `scripts/seed.ts` — Database seed script

## Tech Notes
- Leaflet map uses `dynamic()` with `ssr: false` (required for client-only rendering)
- Tailwind v4 uses `@theme` directive with CSS variables for UDC brand colors (#FDB927 gold, #CE1141 red, #002B5C navy)
- Theme persisted in localStorage key: `udc-theme`
- Next.js standalone output configured in `next.config.ts`
- AI assistant requires `ANTHROPIC_API_KEY` env var (optional — dashboard works without it)
- Admin panel uses `ADMIN_API_KEY` env var for access control (no key = open access in dev)
- AI column mapping in admin uses Claude Haiku (fast/cheap) with heuristic fallback
- AI SDK v6: uses `tool()`, `stepCountIs()`, `DefaultChatTransport`, `sendMessage` (not v4/v5 API)
- All external resources use HTTPS (CartoDB tiles, Leaflet CDN, Google Fonts)
88 changes: 88 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Contributing to UDC Water Resources Dashboard

Welcome! This project is developed at UDC's CAUSES / WRRI for water quality research and environmental justice in DC.

## Getting Started

### Prerequisites
- **Node.js 20+** (LTS recommended)
- **npm** (comes with Node.js)
- **Git**

### Setup

```bash
git clone https://github.com/OliTamrat/UDC.git
cd UDC
npm install
npm run db:seed # Populate the local SQLite database
npm run dev # Start dev server at http://localhost:3000
```

### Useful Commands

| Command | What it does |
|---------|-------------|
| `npm run dev` | Start development server |
| `npm run build` | Production build |
| `npm test` | Run all tests |
| `npm run test:watch` | Run tests in watch mode |
| `npm run db:seed` | Seed SQLite database from static data |
| `npm run lint` | Run ESLint |

## Project Structure

```
src/
├── app/ # Next.js App Router pages and API routes
│ ├── api/ # Backend: stations, export, ingest, health
│ └── station/ # Dynamic station detail pages
├── components/ # React components (map, dashboard, charts, layout)
├── context/ # Theme provider
├── data/ # Static geospatial and reference data
├── lib/ # Utilities: database, logger, validation
└── __tests__/ # Vitest test suites
scripts/ # Database seed script
data/ # SQLite database (gitignored)
```

## Development Workflow

1. **Create a branch** from `main`: `git checkout -b feature/your-feature`
2. **Make changes** — follow the coding standards below
3. **Run tests**: `npm test`
4. **Verify build**: `npm run build`
5. **Open a PR** against `main` with a clear description

## Coding Standards

- **TypeScript strict mode** — all code must pass `tsc` with no errors
- **Functional React components** with hooks (no class components except ErrorBoundary)
- **Tailwind CSS** for styling — use the UDC theme variables (`udc-gold`, `udc-red`, `udc-navy`)
- **Dark/light theme** — all new components must support both themes using `useTheme()`
- **No `console.log`** in production code — use `src/lib/logger.ts` instead
- **Test new features** — add tests in `src/__tests__/` using Vitest

## API Routes

All API routes are in `src/app/api/`. The database connection is in `src/lib/db.ts`.

| Route | Method | Purpose |
|-------|--------|---------|
| `/api/stations` | GET | List all stations with latest readings |
| `/api/stations/:id/history` | GET | Time-series data for a station |
| `/api/export` | GET | CSV/JSON data export |
| `/api/ingest` | POST | Fetch data from USGS NWIS |
| `/api/health` | GET | Health check |

## Data Sources

- **USGS NWIS** — real-time stream gauge data via `waterservices.usgs.gov`
- **DC GIS** — ward boundaries, waterway polygons from DC Open Data
- **Static data** — `src/data/dc-waterways.ts` (reference/seed data)

## Need Help?

- Check `CLAUDE.md` for the project memory and architecture plan
- Open an issue on GitHub for bugs or feature requests
- Contact the WRRI lab for data-related questions
32 changes: 32 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
FROM node:20-alpine AS base

# Install dependencies
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

# Build the application
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run db:seed && npm run build

# Production runner
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/data ./data

USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]
Loading
Loading