# Project export: OrganTrail

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2026
- Tagline: Smart organ donation transport
- Devpost: https://devpost.com/software/organ-trail-5qwz7n
- GitHub: https://github.com/charava/organ-transport/
- Demo: http://organtrail.vercel.app/
- Video: https://www.youtube.com/embed/c-aJGZrIwlI?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Charlotte Rosario (5 commits)

## Devpost submission (written by the team)

### Inspiration

Charlotte’s mom has lived with end-stage kidney disease since she was 17 and has undergone two kidney transplants, most recently after spending three years on dialysis and being told the wait could stretch to a decade. Her last transplant required coordinating a deceased-donor kidney from out of state, underscoring how rare these organs are—and how stressful and life-limiting it is to wait without knowing when a kidney will come. Between 2014 and 2019, nearly 170 donated organs were never transplanted and hundreds more faced dangerous delays due to transportation failures. Today, over 113,000 people in the U.S. are waiting for a transplant, yet viable organs are still declined because they don’t arrive on time. Government reports confirm that the most fragile point is the final stretch between donor hospital and transplant center, where visibility drops and delays occur. OrganTrail tackles this gap by detecting transport risk in real time and leveraging routing APIs to recommend the fastest, most reliable delivery path. By strengthening the last mile, we aim to prevent avoidable organ waste and give more patients a second chance at life.

### What it does

OrganTrail is a multi-component organ transport monitoring system that integrates a hardware sensor module, live dashboard, clinical data layer, and AI-enabled rerouting agent: Hardware Module Built with temperature, humidity, and shock sensors clipped inside existing organ transport boxes. LED display system signals when shock events or extreme temperatures exceed organ-specific clinical thresholds. Custom PCB design ensures portability and direct integration into existing transport boxes, sealed by a Hydrophobic ePTFE vent patch + a recessed vent cavity for the sensors. Low-cost, compact, and fully self-contained for practical deployment. Live Dashboard Displays live sensor metrics: temperature, humidity, and shock readings in real time. Includes GPS-based mapping to visualize the current location and route of the organ. Provides an at-a-glance overview of transport conditions for all monitored organs. Clinical Data Integration Incorporates organ-specific preservation guidelines and ideal storage conditions. Uses historical and clinical data to contextualize sensor readings, highlighting deviations from safe thresholds for each organ type. Error Response & Rerouting Agent Detects abnormal sensor readings and triggers immediate alerts to the transport team. Uses organ donation databases and Google Maps APIs to identify and rank the next five optimal recipient destinations. Computes the most suitable patient for rerouting based on proximity, organ compatibility, and preservation urgency. Supports proactive decision-making to minimize organ spoilage due to transport delays or inefficiencies.

### How we built it

Hardware Module Arduino Uno, KY-002 shock sensor, DHT11 temperature & humidity sensor, and RGB LED continuously read sensor data. Compact, portable design clips inside existing organ transport boxes. Custom PCB and enclosure reduce bulky wiring and enable direct integration. RGB LED provides immediate visual alerts when readings exceed organ-specific safe thresholds. Live Dashboard Node.js bridge server ingests sensor readings over serial or via API, broadcasting updates via WebSocket. React dashboard visualizes real-time metrics: temperature, humidity, and shock events. Leaflet/OpenStreetMap maps display live organ location, path history, and planned route snapshots. Configurable safe ranges trigger warnings and critical alerts when exceeded. Redirect triage flow suggests nearest hospitals based on critical sensor events; operators can confirm or reject rerouting. Synthetic Data & Clinical Integration Generated realistic transport streams using global hospital locations. Researched existing clinical and research data to determine optimal preservation conditions for each organ type. Alerts and rerouting decisions are informed by organ-specific temperature, ischemic time limits, and risk thresholds. Sensor Event Logging & Monitoring Shock events logged with timestamps to capture mishandling risks. Temperature and humidity deviations tracked to ensure preservation compliance. Continuous monitoring enables real-time assessment, structured data trails, and predictive analytics for future optimization.

### Challenges we ran into

Sensor Limitations: Mechanical, low-cost sensors (temperature, humidity, shock) aren’t perfectly precise or robust, making it difficult to fully validate measurements under real organ-transport conditions. Mechanical & Mounting Constraints: Without access to real organ transport boxes, we couldn’t directly test mounting, condensation behavior, or thermal gradients, which are critical for accurate monitoring. Limited Clinical Data: Clinical transport and outcome datasets are restricted, so we relied on synthetic data streams to demonstrate end-to-end behavior, workflow, and alert logic rather than real-world validation.

### Accomplishments we're proud of

Compact, manufacturable PCB design for low-cost sensors that integrate directly into existing organ transport boxes. AI-informed rerouting algorithm leveraging Google Maps APIs to optimize organ turnaround and reduce spoilage risk. Real-time hardware and software dashboard displaying temperature, humidity, shock, GPS location, and alert status for live monitoring.

### What we learned

Workflow matters as much as sensors – clear alerts and intuitive UX are critical; raw data alone isn’t enough. Data scarcity is real – standardized organ transport datasets are limited, making even synthetic data streams valuable. Prioritizing signals is essential – working under hardware and cost constraints taught us to focus on the most meaningful metrics for decision-making. Integration is non-trivial – combining hardware, live dashboards, clinical thresholds, and rerouting logic requires careful design to be reliable in real-time.

### What's next

Environment-Optimized Hardware Explore materials and enclosure designs suited for cold, wet, and condensation-prone organ transport conditions. Custom PCB Manufacturing – Produce a single compact PCB integrating current sensors and adding accelerometer/gyroscope for richer handling data. Wireless Connectivity – Transition from serial-only communication to Bluetooth and Wi-Fi for more flexible, real-time data streaming. Real-World Testing – Iterate on sensor placement and durability inside actual transport boxes. Clinical & Operational Validation – Collaborate with hospitals, clinicians, and the UNOS to validate the workflow and leverage collected data to build predictive models for safer, optimized organ transport.

## README (from the GitHub repository)

# OrganTrail

On-ground organ transport logistics — multi-factor error detection (temperature, shock, humidity) and GPS tracking for road-based transplant transport.

This system is designed for ground vehicles (ambulances, transport vans) moving organs between facilities. 

Treehacks 2026 - Ananya, Ashley, Charlotte, Leah

---

## Features

- **Temperature monitoring** — Safe range 2–6°C for cold storage
- **Shock sensing** — Impact/vibration detection (g-force)
- **Humidity monitoring** — Relative humidity %
- **GPS location & path** — Live position and route tracking on map
- **Destination & route** — Each device shows destination; click "View route" to see path on map
- **Redirect triaging** — On critical errors, suggests redirect to nearby hospital; operator can confirm or reject
- **Alerts** — Critical/warning when readings go out of range

*Redirect suggestions use `hospitals.csv` — run `npm run process-hospitals` to generate `public/hospitals.json` from the CSV.*

---

## Quick start

### 1. Dashboard (React)

```bash
npm install
npm start
```

Opens at http://localhost:3000

### 2. Bridge server (serial → WebSocket)

```bash
cd server
npm install
npm start
```

The bridge runs on port 4000. Without a serial port, it accepts POST requests for testing.

### 3. Connect hardware (COM port / serial)

Set your serial port in `server/.env` or as an env var:

**Windows:**
```bash
set SERIAL_PORT=COM3
npm start
```

**macOS:**
```bash
SERIAL_PORT=/dev/cu.usbserial-10 npm start
```

**Linux:**
```bash
SERIAL_PORT=/dev/ttyUSB0 npm start
```

Find your port: `ls /dev/cu.*` (Mac) or Device Manager → Ports (Windows).

### 4. Microcontroller output format

**Text format** (one line per reading):
```
Shock: 0 | Temp: 21.50C | Humidity: 45.40% | Lat: 37.7749 | Lng: -122.4194
```

**JSON format**:
```json
{"temp":4.2,"shock":0,"humidity":45.4,"lat":37.7749,"lng":-122.4194,"deviceId":"DEV-001"}
```

- `temp` — temperature in °C
- `shock` — g-force (0 if no event)
- `humidity` — relative humidity %
- `lat`, `lng` — GPS coordinates (optional)
- `deviceId` — optional

### 5. Google Maps API (for route directions)

Add your Google Maps API key to `server/.env`:

```
GOOGLE_MAPS_API_KEY=your_api_key_here
```

Enable the **Directions API** in [Google Cloud Console](https://console.cloud.google.com/apis/library/directions-backend.googleapis.com). The View route modal will then show driving routes from Google Maps.

### 6. Hospitals data (for redirect suggestions)

Process `hospitals.csv` into JSON for the dashboard:

```bash
npm run process-hospitals
```

This creates `public/hospitals.json`. The redirect triage uses it to suggest nearest hospitals by location.

### 7. Test without hardware

With the bridge running:

```bash
cd server
npm run mock
```

Or use curl (with GPS):
```bash
curl -X POST http://localhost:4000/api/readings \
  -H "Content-Type: application/json" \
  -d '{"temp":4.5,"shock":0,"humidity":48,"lat":37.7749,"lng":-122.4194}'
```


## Detected evidence (automated analysis)

Indexed codebase: 21 recognized source files, 76 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- React (technology) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- Vercel (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (32 of 32)

```
.gitignore
Hospitals.csv
package.json
PROJECT_OVERVIEW.md
public/hospitals.json
public/index.html
public/manifest.json
public/robots.txt
public/synthetic-organ-transports.json
README.md
scripts/processHospitals.js
server/.env.example
server/index.js
server/mock-sender.js
server/package.json
src/App.css
src/App.js
src/App.test.js
src/components/MapPanel.js
src/components/RedirectTriage.js
src/components/RouteModal.js
src/data/mockTransportData.js
src/data/transportConfig.js
src/hooks/useLiveReadings.js
src/index.css
src/index.js
src/reportWebVitals.js
src/setupTests.js
src/utils/directions.js
src/utils/hospitals.js
synthetic_organ_shipments_1000.csv
vercel.json
```

### Dependencies

- package.json: @testing-library/dom@^10.4.1, @testing-library/jest-dom@^6.9.1, @testing-library/react@^16.3.2, @testing-library/user-event@^13.5.0, leaflet@^1.9.4, react@^19.2.4, react-dom@^19.2.4, react-leaflet@^5.0.0, react-scripts@5.0.1, web-vitals@^2.1.4
- server/package.json: @serialport/parser-readline@^13.0.0, dotenv@^16.3.1, express@^4.18.2, serialport@^12.0.0, ws@^8.14.2

### Recent commits (newest first)

- fix vercel
- new app'
- new
- updated maps and glitching
- Stop tracking .env, add .env.example
- new logo etc
- new updates for gps
- connected sensors - updated dashboard
- com hooks and processing
- overwrote readme
- overwrote readme
- overwrote readme
- overwrote readme
- Initialize project using Create React App

## Key source files (fetched from GitHub, selected and truncated for size)

### PROJECT_OVERVIEW.md

```markdown
# OrganTrail — Project Overview

**Treehacks 2026** — Ananya, Ashley, Charlotte, Leah

---

## What It Does

OrganTrail is an **on-ground organ transport monitoring dashboard** for road-based transplant logistics. It tracks temperature, shock, humidity, and GPS for transport vehicles (ambulances, transport vans) moving organs between donor and recipient facilities.

The system provides:
- **Real-time monitoring** of sensor data from a microcontroller via serial port
- **Multi-factor error detection** when readings go outside safe ranges
- **GPS tracking** with live position and path history on a map
- **Route visualization** with driving directions from Google Maps
- **Redirect triaging** — when a critical error occurs, suggests nearest hospitals for emergency diversion

---

## How We Built It

### Architecture

**Two-part system:**

1. **Bridge server** (Node.js, port 4000) — reads data from hardware, broadcasts to dashboard
2. **Dashboard** (React, port 3000) — displays data, maps, alerts, and controls

### Data Flow

```
Microcontroller → Serial (COM port) → Bridge Server → WebSocket → Dashboard
                     OR
POST /api/readings (testing) → Bridge Server → WebSocket → Dashboard
```

### Tech Stack

| Layer | Technology |
|-------|------------|
| Frontend | React 19, Create React App |
| Maps | Leaflet, react-leaflet |

| Backend | Node.js, Express |
| WebSocket | ws |
| Serial | serialport, @serialport/parser-readline |
| Directions | Google Directions API (proxied via server) |

---

## Features

### 1. Temperature Monitoring
- **Organ-specific safe ranges** (in `mockTransportData.js`):
  - **Heart**: 4–8°C (avoid too-cold exposure)
  - **Kidney/Liver/Lung/Pancreas**: 0–4°C (SCS / static cold storage)
- Live gauge display with safe range indicator
- Critical alerts when temperature goes outside range
- Device ID and last reading timestamp

### 2. Humidity Monitoring
- Relative humidity % display
- Bar visualization
- Updates when readings arrive

### 3. Shock Sensing
- **Thresholds**: ≥1.5g = warning, ≥2.5g = critical
- Recent shock events list with g-force and timestamp
- Alerts generated for warning/critical events

### 4. GPS Location & Map
- **MapPanel**: OpenStreetMap tiles via Leaflet
- Live position marker when connected (single device)
- Path taken (blue polyline) from GPS history
- Synthetic location when no GPS from device (SF Bay Area drift)
- Map center follows live device

### 5. Active Transports Table
- 10 mock transports (Heart, Liver, Kidney, Lung, Pancreas)
- Columns: Transport ID, Organ, Device, Destination, Temperature, Humidity, Shock, Location, Status, Last reading
- **Live** vs **Synthetic** status badge
- DEV-001 is the live device when connected

### 6. View Route Modal
- **Route snapshot**: Captures location, path, destination when opened — no live updates while modal is open (prevents flicker from sensor)
- **Path taken**: Blue polyline from GPS history
- **Route to destination**: Orange polyline from Goog
[truncated — 3287 more characters]
```

### package.json

```
{
  "name": "organ-trail",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/dom": "^10.4.1",
    "@testing-library/jest-dom": "^6.9.1",
    "@testing-library/react": "^16.3.2",
    "@testing-library/user-event": "^13.5.0",
    "leaflet": "^1.9.4",
    "react": "^19.2.4",
    "react-dom": "^19.2.4",
    "react-leaflet": "^5.0.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "proxy": "http://localhost:4000",
  "scripts": {
    "process-hospitals": "node scripts/processHospitals.js",
    "start": "react-scripts start",
    "build": "CI=false react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

```

### server/package.json

```
{
  "name": "organ-bridge",
  "version": "1.0.0",
  "description": "OrganTrail bridge server: serial port + WebSocket for organ transport monitoring",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "mock": "node mock-sender.js"
  },
  "dependencies": {
    "@serialport/parser-readline": "^13.0.0",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "serialport": "^12.0.0",
    "ws": "^8.14.2"
  }
}

```

### src/index.js

```javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

```

### server/index.js

```javascript
/**
 * OrganTrail Bridge Server
 *
 * - Reads temperature, shock, humidity, GPS from serial port (COM port)
 * - Broadcasts to all connected dashboard clients via WebSocket
 * - Also accepts POST /api/readings for testing without hardware
 *
 * Usage:
 *   Set SERIAL_PORT in server/.env (e.g. /dev/cu.usbserial-10 on macOS)
 *   Or: SERIAL_PORT=COM3 node index.js (Windows)
 */

require('dotenv').config();

const express = require('express');
const { WebSocketServer } = require('ws');
const http = require('http');
const { SerialPort } = require('serialport');
const { ReadlineParser } = require('@serialport/parser-readline');

const PORT = process.env.PORT || 4000;
const SERIAL_PATH = process.env.SERIAL_PORT;
const SERIAL_BAUD = parseInt(process.env.SERIAL_BAUD || '9600', 10);

const app = express();
app.use(express.json());

const server = http.createServer(app);
const wss = new WebSocketServer({ server });

function broadcast(data) {
  const payload = {
    ...data,
    at: data.at || new Date().toISOString(),
    receivedAt: new Date().toISOString(),
  };
  const msg = JSON.stringify(payload);
  wss.clients.forEach((client) => {
    if (client.readyState === 1) client.send(msg);
  });
  const ts = payload.receivedAt ? new Date(payload.receivedAt).toLocaleTimeString() : '';
  const parts = [];
  if (payload.temp != null) parts.push(`${payload.temp}°C`);
  if (payload.humidity != null) parts.push(`${payload.humidity}%`);
  parts.push(`shock=${payload.shock}g`);
  if (payload.lat != null && payload.lng != null) parts.push(`GPS ${payload.lat.toFixed(4)}, ${payload.lng.toFixed(4)}`);
  console.log('[broadcast]', parts.join(', ') + (ts ? ` @ ${ts}` : ''));
}

// POST /api/readings — for testing without hardware (curl, mock script)
app.post('/api/readings', (req, res) => {
  const reading = req.body;
  if (!reading || typeof reading.temp === 'undefined') {
    return res.status(400).json({ error: 'Expected { temp, shock?, deviceId?, lat?, lng? }' });
  }
  const payload = {
    temp: Number(reading.temp),
    shock: Number(reading.shock ?? 0),
    humidity: reading.humidity != null ? Number(reading.humidity) : undefined,
    deviceId: reading.deviceId || 'DEV-001',
  };
  if (reading.lat != null && reading.lng != null) {
    payload.lat = Number(reading.lat);
    payload.lng = Number(reading.lng);
  }
  broadcast(payload);
  res.json({ ok: true });
});

// GET /api/directions — proxy to Google Directions API (avoids CORS)
const GOOGLE_API_KEY = process.env.GOOGLE_MAPS_API_KEY;
app.get('/api/directions', async (req, res) => {
  if (!GOOGLE_API_KEY) {
    return res.status(503).json({ error: 'GOOGLE_MAPS_API_KEY not set in server/.env' });
  }
  const { origin, destination } = req.query;
  if (!origin || !destination) {
    return res.status(400).json({ error: 'Expected origin and destination query params' });
  }
  try {
    const url = `https://maps.googleapis.com/maps/api/directions/json?origin=${encodeURIComponent(origin)}&destination=${encodeURIComponent(destination)}&mode=driving&key=${GOOGLE_API_KEY}`;
    const resp = await fetch(url);
    const data = await resp.json();
    if (data.status !== 'OK') {
      return res.status(400).json({ error: data.error_message || data.status });
    }
    const route = data.routes?.[0];
    if (!route) {
      return res.status(404).json({ error: 'No route found' });
    }
    const leg = route.legs?.[0];
    res.json({
      polyline: route.overview_polyline?.points,
      distance: leg?.distance?.text,
      duration: leg?.duration?.text,
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// GET /api/ports — list available serial ports
app.get('/api/ports', async (req, res) => {
  try {
    const ports = await SerialPort.list();
    res.json(ports.map((p) => ({ path: p.path, manufacturer: p.manufacturer })));
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Serial port connection
let serialPort = null; 


if (SERIAL_PATH) {
  try {
    serialPort = new SerialPort({
      path: SERIAL_PATH,
      baudRate: SERIAL_BAUD,
    });

    const parser = serialPort.pipe(new ReadlineParser({ delimiter: '\n' }));

    function parseLine(trimmed) {
      // Try JSON first
      try {
        const r = JSON.parse(trimmed);
        const out = {
          temp: Number(r.temp ?? r.temperature ?? 0),
          shock: Number(r.shock ?? r.gForce ?? 0),
          humidity: r.humidity != null ? Number(r.humidity) : undefined,
          deviceId: r.deviceId || 'DEV-001',
        };
        if (r.lat != null && r.lng != null) {
          out.lat = Number(r.lat);
          out.lng = Number(r.lng);
        }
        return out;
      } catch (_) {}

      // Parse text format: "Shock: 0 | Temp: 21.50C | Humidity: 45.40%" or with GPS "| Lat: 37.77 | Lng: -122.42"
      const match = trimmed.match(/Shock:\s*([\d.-]+)\s*\|?\s*Temp:\s*([\d.]+)C?\s*\|?\s*Humidity:\s*([\d.]+)%?(?:\s*\|?\s*Lat:\s*([\d.-]+)\s*\|?\s*Lng:\s*([\d.-]+))?/i);
      if (match) {
        const out = {
          temp: Number(match[2]),
          shock: Number(match[1]),
          humidity: Number(match[3]),
          deviceId: 'DEV-001',
        };
        if (match[4] != null && match[5] != null) {
          out.lat = Number(match[4]);
          out.lng = Number(match[5]);
        }
        return out;
      }
      return null;
    }

    parser.on('data', (line) => {
      const trimmed = line.trim();
      if (!trimmed) return;
      const reading = parseLine(trimmed);
      if (reading) {
        broadcast(reading);
      } else {
        console.warn('[serial] Unrecognized format:', trimmed);
      }
    });

    serialPort.on('error', (err) => console.error('[serial] Error:', err.message));
    serialPort.on('open', () => console.log('[serial] Opened', SERIAL_PATH));
  } catch (err) {
    console.error('[serial] Failed to open:', err.message);
  }
} else {
  console.log('[serial] SERIAL_PORT not set — use POST /api/readings for testing
[truncated — 229 more characters]
```

### src/App.js

```javascript
import { useCallback, useEffect, useState } from 'react';
import './App.css';
import { getTempRangeForOrgan, getTempStatusForOrgan, activeTransports } from './data/mockTransportData';
import { DEFAULT_DESTINATION } from './data/transportConfig';
import { useLiveReadings } from './hooks/useLiveReadings';
import { MapPanel } from './components/MapPanel';
import { RouteModal } from './components/RouteModal';
import { RedirectTriage } from './components/RedirectTriage';

function formatTime(iso) {
  return new Date(iso).toLocaleTimeString('en-US', {
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false,
  });
}

function formatDate(iso) {
  return new Date(iso).toLocaleString('en-US', {
    month: 'short',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  });
}

function StatusBadge({ status }) {
  const map = {
    ok: { label: 'OK', class: 'status-ok' },
    warning: { label: 'Warning', class: 'status-warning' },
    alert: { label: 'Alert', class: 'status-alert' },
  };
  const { label, class: c } = map[status] || map.ok;
  return <span className={`badge ${c}`}>{label}</span>;
}

function formatRelativeTime(iso) {
  const sec = Math.floor((Date.now() - new Date(iso)) / 1000);
  if (sec < 60) return `${sec}s ago`;
  if (sec < 3600) return `${Math.floor(sec / 60)}m ago`;
  return `${Math.floor(sec / 3600)}h ago`;
}

function App() {
  const { primaryDevice, recentShocks, alerts, isConnected, lastReadingAt, location, path } = useLiveReadings(activeTransports);
  const [, setTick] = useState(0);
  const [destination, setDestination] = useState(DEFAULT_DESTINATION);
  const [redirectedTo, setRedirectedTo] = useState(null);
  const [routeModalOpen, setRouteModalOpen] = useState(false);
  const [selectedTransportId, setSelectedTransportId] = useState(null);
  const [routeSnapshot, setRouteSnapshot] = useState(null);
  const [redirectDismissedUntil, setRedirectDismissedUntil] = useState(0);

  useEffect(() => {
    if (!lastReadingAt) return;
    const id = setInterval(() => setTick((t) => t + 1), 1000);
    return () => clearInterval(id);
  }, [lastReadingAt]);

  const criticalAlert = alerts.find((a) => a.severity === 'critical');
  const showRedirectTriage = criticalAlert && Date.now() > redirectDismissedUntil && !redirectedTo;

  const criticalCount = alerts.filter((a) => a.severity === 'critical').length;
  const warningCount = alerts.filter((a) => a.severity === 'warning').length;

  const transportsWithLive = activeTransports.map((t, i) => {
    const isLive = isConnected && t.deviceId === primaryDevice.deviceId;
    const displayLoc = isLive ? (location || { lat: t.lat, lng: t.lng }) : { lat: t.lat, lng: t.lng };
    const displayDest = isLive
      ? destination
      : { name: t.destination, lat: t.destLat ?? t.lat + 0.01, lng: t.destLng ?? t.lng + 0.01 };
    const temp = isLive ? (primaryDevice.temperature ?? t.temperature) : t.temperature;
    const tempStatus = getTempStatusForOrgan(temp, t.organType);
    if (isLive) {
      const shockStatus =
        primaryDevice.lastShock && primaryDevice.lastShock.g >= 1.5 ? 'warning' : 'ok';
      return {
        ...t,
        temperature: primaryDevice.temperature ?? t.temperature,
        humidity: primaryDevice.humidity ?? t.humidity,
        lastShock: primaryDevice.lastShock ?? t.lastShock,
        tempStatus,
        shockStatus,
        location: displayLoc,
        destination: displayDest,
        isLive,
        lastReadingAt: isLive ? lastReadingAt : null,
      };
    }
    return {
      ...t,
      tempStatus,
      location: displayLoc,
      destination: displayDest,
      isLive: false,
      lastReadingAt: null,
    };
  });

  const primaryOrgan = transportsWithLive.find((t) => t.deviceId === primaryDevice.deviceId)?.organType;
  const primaryTempRange = getTempRangeForOrgan(primaryOrgan);

  const handleCloseRouteModal = useCallback(() => {
    setRouteModalOpen(false);
    setSelectedTransportId(null);
    setRouteSnapshot(null);
  }, []);

  const handleConfirmRedirect = (hospital) => {
    setDestination({ name: hospital.name, lat: hospital.lat, lng: hospital.lng });
    setRedirectedTo(hospital);
    setRedirectDismissedUntil(Date.now() + 60000); // Don't show again for 1 minute
  };

  const handleRejectRedirect = () => {
    setRedirectDismissedUntil(Date.now() + 60000); // Don't show again for 1 minute
  };

  return (
    <div className="dashboard">
      <header className="dashboard-header">
        <div className="header-content">
          <img src="/logo.png" alt="OrganTrail" className="header-logo" />
          <div>
            <h1 className="dashboard-title">OrganTrail</h1>
            <p className="dashboard-subtitle">
              On-ground transport logistics — temperature, shock, humidity & GPS tracking
            </p>
          </div>
        </div>
        <div className="header-meta">
          <span className={`live-dot ${isConnected ? 'live-dot-active' : ''}`} />
          {isConnected ? 'Live' : 'Disconnected'}
          <span className="header-time">{formatDate(new Date().toISOString())}</span>
        </div>
      </header>

      {redirectedTo && (
        <div className="redirect-banner">
          <span className="redirect-banner-icon">↪</span>
          <strong>Redirected</strong>
          <span> — Transport now heading to </span>
          <strong>{redirectedTo.name}</strong>
          {(redirectedTo.city || redirectedTo.state) && (
            <span className="redirect-banner-location">
              {' '}({[redirectedTo.city, redirectedTo.state].filter(Boolean).join(', ')})
            </span>
          )}
        </div>
      )}

      <main className="dashboard-main">
        <section className="stats-row">
          <div className="stat-card">
            <span className="stat-value">{activeTransports.length}</span>
            <span className="stat-label">Active transports</span>
          </div>
          <div class
[truncated — 9837 more characters]
```

### src/setupTests.js

```javascript
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

```

### src/App.test.js

```javascript
import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

```

### src/reportWebVitals.js

```javascript
const reportWebVitals = onPerfEntry => {
  if (onPerfEntry && onPerfEntry instanceof Function) {
    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
      getCLS(onPerfEntry);
      getFID(onPerfEntry);
      getFCP(onPerfEntry);
      getLCP(onPerfEntry);
      getTTFB(onPerfEntry);
    });
  }
};

export default reportWebVitals;

```

### src/index.css

```css
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&display=swap');

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background: #0f1419;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

```

[12 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]