Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the local dev/demo experience for the dashboard by changing Docker-exposed ports, improving the dashboard’s patient detail and cohort analytics UX (including “add trial” + patient/alert subviews), and adding environment-driven proxy configuration.
Changes:
- Remap Docker host ports (Postgres to 5434, backend to 8001) and align the dashboard dev proxy defaults via Vite/env example.
- Expand dashboard UI flows: richer Patient Detail layout and Cohort Analytics “Patients/Stats” subviews plus an “Add Trial” form.
- Add “fallback/mock data” behavior in several React Query hooks when backend requests fail.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| docker-compose.yml | Changes host port mappings for Postgres and backend services. |
| apps/dashboard/vite.config.ts | Loads env via loadEnv and sets proxy target from VITE_BACKEND_URL. |
| apps/dashboard/src/pages/PatientDetailPage.tsx | Adds unified clinical picture panel + updated timeline/symptom rendering. |
| apps/dashboard/src/hooks/usePatients.ts | Wraps API calls in try/catch and returns fallback patient-related data. |
| apps/dashboard/src/hooks/useAlerts.ts | Wraps alerts/overview/AE calls in try/catch and returns fallback data; adds queryEnabled. |
| apps/dashboard/src/components/CohortAnalytics.tsx | Adds Add-Trial flow and patient/alerts subview navigation inside analytics. |
| apps/dashboard/src/components/CohortAnalytics.css | Adds styles for Add-Trial form + “no analytics yet” empty state. |
| apps/dashboard/src/App.tsx | Moves patients/alerts access under analytics, adds dynamic trial list with add-trial support. |
| apps/dashboard/src/App.css | Adds calendar tab wrapper styling; removes message avatar styling and adjusts layout. |
| apps/dashboard/package-lock.json | Updates lockfile entries for axios/react-query. |
| apps/dashboard/.env.example | Documents VITE_BACKEND_URL for local Vite proxying. |
| README.md | Expands repository README with product/architecture narrative and dev context. |
Files not reviewed (1)
- apps/dashboard/package-lock.json: Language not supported
Comments suppressed due to low confidence (2)
apps/dashboard/src/hooks/usePatients.ts:76
- These queries swallow all request failures and return mock data, which prevents TanStack Query from ever entering an error state and can mask real backend/proxy issues. If fallback data is only intended for demos, gate it behind an explicit dev/demo flag (e.g.,
import.meta.env.DEVorVITE_USE_MOCKS) and otherwise rethrow so callers can handle errors correctly.
export interface PatientDetail {
id: string;
subject_id: string;
apps/dashboard/src/pages/PatientDetailPage.tsx:76
UNIFIED_ALERT_TEXTis hardcoded and the UI always renders an "Active alert" banner, even when the selected patient has no current alerts / elevated risk. This can show incorrect clinical information. Consider deriving this content from the patient’s actual alerts/risk data (and conditionally rendering the banner only when an active alert exists).
<p style={{ color: 'var(--secondary-color)', fontStyle: 'italic' }}>No transcript messages recorded.</p>
) : (
session.messages.map((msg) => (
<div key={msg.id} style={{ marginBottom: 10 }}>
<div style={{ fontSize: '0.75em', color: 'var(--secondary-color)', marginBottom: 2 }}>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| }); | ||
| return response as unknown as PatientListResponse; | ||
| } catch (error) { |
There was a problem hiding this comment.
The backend /dashboard/patients/{patient_id} endpoint returns a nested payload ({ patient: {...}, risk_score: ..., recent_alerts: ... }), but this hook casts the response directly to PatientDetail (which expects subject_id, treatment_arm, etc. at the top level). This will produce undefined fields when the backend succeeds (and the new fallback path makes this harder to notice). Normalize the API response to the PatientDetail shape (or update the types/UI to match the real contract).
| console.warn('Backend failed, using fallback data for useAlerts'); | ||
| return [ | ||
| { | ||
| id: 'mock-alert-1', | ||
| patient_id: 'mock-p1', | ||
| alert_type: 'wearable_anomaly', | ||
| severity: 'high', | ||
| title: 'Elevated Resting Heart Rate', | ||
| description: 'Patient average resting heart rate has increased by 15 bpm over the last 3 days.', | ||
| status: 'open', | ||
| created_at: new Date().toISOString() | ||
| }, | ||
| { | ||
| id: 'mock-alert-2', | ||
| patient_id: 'mock-p2', | ||
| alert_type: 'missed_checkin', | ||
| severity: 'medium', | ||
| title: 'Missed 2 Consecutive Check-ins', | ||
| description: 'Patient has not completed their daily check-in for 48 hours.', | ||
| status: 'open', | ||
| created_at: new Date().toISOString() | ||
| } | ||
| ]; |
There was a problem hiding this comment.
These queries swallow all request failures and return mock data, which prevents TanStack Query from ever entering an error state and can mask real backend/proxy issues. If fallback data is only intended for demos, gate it behind an explicit dev/demo flag (e.g., import.meta.env.DEV or VITE_USE_MOCKS) and otherwise rethrow so callers can handle errors correctly.
| console.warn('Backend failed, using fallback data for useAlerts'); | |
| return [ | |
| { | |
| id: 'mock-alert-1', | |
| patient_id: 'mock-p1', | |
| alert_type: 'wearable_anomaly', | |
| severity: 'high', | |
| title: 'Elevated Resting Heart Rate', | |
| description: 'Patient average resting heart rate has increased by 15 bpm over the last 3 days.', | |
| status: 'open', | |
| created_at: new Date().toISOString() | |
| }, | |
| { | |
| id: 'mock-alert-2', | |
| patient_id: 'mock-p2', | |
| alert_type: 'missed_checkin', | |
| severity: 'medium', | |
| title: 'Missed 2 Consecutive Check-ins', | |
| description: 'Patient has not completed their daily check-in for 48 hours.', | |
| status: 'open', | |
| created_at: new Date().toISOString() | |
| } | |
| ]; | |
| if (import.meta.env.DEV || import.meta.env.VITE_USE_MOCKS) { | |
| console.warn('Backend failed, using fallback data for useAlerts'); | |
| return [ | |
| { | |
| id: 'mock-alert-1', | |
| patient_id: 'mock-p1', | |
| alert_type: 'wearable_anomaly', | |
| severity: 'high', | |
| title: 'Elevated Resting Heart Rate', | |
| description: 'Patient average resting heart rate has increased by 15 bpm over the last 3 days.', | |
| status: 'open', | |
| created_at: new Date().toISOString() | |
| }, | |
| { | |
| id: 'mock-alert-2', | |
| patient_id: 'mock-p2', | |
| alert_type: 'missed_checkin', | |
| severity: 'medium', | |
| title: 'Missed 2 Consecutive Check-ins', | |
| description: 'Patient has not completed their daily check-in for 48 hours.', | |
| status: 'open', | |
| created_at: new Date().toISOString() | |
| } | |
| ]; | |
| } | |
| throw error; |
| --light-font: rgba(255, 255, 255, 0.7); | ||
| } | ||
|
|
||
| /* Visit Calendar tab: calendar first, then patient list, then alert queue */ |
There was a problem hiding this comment.
This comment says the tab stacks "calendar first, then patient list, then alert queue", but the Calendar tab currently renders only the calendar stack wrapper. Please update/remove the comment so it reflects the actual layout to avoid misleading future edits.
| /* Visit Calendar tab: calendar first, then patient list, then alert queue */ | |
| /* Calendar tab stack container */ |
| "dependencies": { | ||
| "@tanstack/react-query": "^5.95.2", | ||
| "axios": "^1.14.0", | ||
| "react": "^19.2.4", | ||
| "react-dom": "^19.2.4", |
There was a problem hiding this comment.
The repo’s dashboard tooling uses pnpm (Makefile and apps/dashboard/Dockerfile), but this PR updates package-lock.json without updating pnpm-lock.yaml. This can cause dependency drift between local/devcontainer installs. Consider either updating pnpm-lock.yaml (preferred) or removing package-lock.json if pnpm is the intended single source of truth.
| @@ -0,0 +1,3 @@ | |||
| # Where Vite proxies /api/* during `npm run dev`. | |||
| # Match docker-compose host port for `backend` (repo uses 8001:8000 when 8000 is taken). | |||
There was a problem hiding this comment.
The note implies the backend host port is only 8001 "when 8000 is taken", but docker-compose currently maps the backend to 8001 unconditionally. Consider clarifying the comment (and/or documenting how to switch ports) so the example doesn’t contradict the actual dev stack.
| # Match docker-compose host port for `backend` (repo uses 8001:8000 when 8000 is taken). | |
| # Match docker-compose host port for `backend` (default 8001:8000; update if you change the mapping). |
| - ./db/seed.sql:/docker-entrypoint-initdb.d/02-seed.sql | ||
| ports: | ||
| - "5432:5432" | ||
| - "5434:5432" |
There was a problem hiding this comment.
Postgres host port changed from 5432 to 5434. This will break local tooling/docs that expect localhost:5432 (e.g., psql/GUI connections). Consider either keeping 5432 as default and documenting an override, or update the repo docs/instructions to reflect 5434.
| - "5434:5432" | |
| - "5432:5432" |
| ENVIRONMENT: development | ||
| ports: | ||
| - "8000:8000" | ||
| - "8001:8000" |
There was a problem hiding this comment.
Backend is now exposed on host port 8001 instead of 8000; other parts of the repo still hardcode localhost:8000 (e.g., the mobile app base URL and several docs). Consider keeping 8000 as the default mapping, or update those references (and/or make the host port configurable via an env var) to avoid confusing connection failures.
| - "8001:8000" | |
| - "8000:8000" |
| // loadEnv reads apps/dashboard/.env* so VITE_BACKEND_URL is available here | ||
| // (process.env alone does not load .env for this file). | ||
| const env = loadEnv(mode, process.cwd(), '') | ||
| const backendUrl = | ||
| env.VITE_BACKEND_URL || 'http://127.0.0.1:8001' |
There was a problem hiding this comment.
loadEnv(mode, process.cwd(), '') loads all env vars (not just VITE_), which can accidentally pull in unrelated secrets and makes intent less clear. Prefer using the VITE_ prefix filter (third arg) and resolve the env dir explicitly (e.g., the dashboard folder) so the config behaves the same when invoked from different working directories.
Made-with: Cursor