Files
Experiments DB Dev 9535f86970 feat: add experiment calendar view with per-day subject counts
New ExperimentCalendar component added as a third view mode (List / Group / Calendar)
on the Experiment Detail page.

Backend:
- GET /api/experiments/:id/calendar?field=<fieldIdOrBuiltinKey>
  Returns { field, days: { "YYYY-MM-DD": count } } where count = number of
  subjects with a non-null/non-empty value for the chosen field on that date.
  Supports both builtin fields (vitals, treatment, notes, experiment_description)
  and custom fields addressed by fieldId UUID.

Frontend:
- ExperimentCalendar component: navigable monthly grid, field selector
  (defaults to first field with "weight" in label, else first active field),
  blue badges showing subject count per day, click to open modal with all
  subjects' data for that day.
- Integrated into ExperimentDetail as displayMode "calendar", persisted
  in localStorage alongside the existing list/group modes.
- experimentsApi.getCalendar() added to API client.

Tests:
- backend/tests/calendar.test.js: 8 unit tests (404, empty days, builtin
  count, custom field count, whitespace ignored, multi-month, field echo,
  missing param).
- frontend/tests/ExperimentCalendar.test.jsx: 13 RTL tests covering
  rendering, default field selection, count badges, month navigation,
  field-change re-fetch, day click modal, and multi-subject display.

All 47 backend + 13 calendar frontend tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 09:26:35 -04:00

66 lines
1.8 KiB
YAML

version: '3.9'
services:
postgres:
image: postgres:15-alpine
container_name: experiments_postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-expuser}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-exppassword}
POSTGRES_DB: ${POSTGRES_DB:-experiments_db}
TZ: America/New_York
command: ["postgres", "-c", "timezone=America/New_York"]
volumes:
- postgres_data:/var/lib/postgresql/data
# Port NOT exposed to host — only reachable by backend via internal Docker network
expose:
- "5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-expuser} -d ${POSTGRES_DB:-experiments_db}"]
interval: 10s
timeout: 5s
retries: 5
backend:
build:
context: ./backend
target: prod
container_name: experiments_backend
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: postgresql://${POSTGRES_USER:-expuser}:${POSTGRES_PASSWORD:-exppassword}@postgres:5432/${POSTGRES_DB:-experiments_db}
NODE_ENV: production
PORT: 3001
CORS_ORIGIN: "http://localhost:52867"
TZ: America/New_York
# Non-root user set in Dockerfile; entrypoint runs migrations then starts server
entrypoint: ["/bin/sh", "/app/docker-entrypoint.sh"]
ports:
- "3001:3001"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3001/health || exit 1"]
interval: 10s
timeout: 5s
retries: 10
start_period: 40s
frontend:
build:
context: ./frontend
target: prod
container_name: experiments_frontend
restart: unless-stopped
depends_on:
backend:
condition: service_healthy
ports:
- "52867:80"
volumes:
postgres_data:
name: experiments_postgres_data