c8f0c5f298
Calendar day clicks now navigate to /experiments/:id/day/:date (with ?field= param). ExperimentDayView shows a sticky-column table of all subjects with data that day, inline edit via modal, and a session-metrics bar chart from analysis_summary. Subject name links to /daily-statuses/:id. Backend adds ?date= filtering to GET /experiments/:id/daily-statuses. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
71 lines
2.8 KiB
React
71 lines
2.8 KiB
React
import React, { useEffect, useState } from 'react';
|
|
import { Routes, Route, Link, useLocation } from 'react-router-dom';
|
|
import { systemApi } from './api/client';
|
|
import Dashboard from './pages/Dashboard';
|
|
import ExperimentDetail from './pages/ExperimentDetail';
|
|
import AnimalDetail from './pages/AnimalDetail';
|
|
import DailyStatusDetail from './pages/DailyStatusDetail';
|
|
import ExperimentDayView from './pages/ExperimentDayView';
|
|
|
|
function Navbar() {
|
|
return (
|
|
<header className="bg-white border-b border-gray-200 sticky top-0 z-40">
|
|
<div className="max-w-5xl mx-auto px-4 h-14 flex items-center justify-between">
|
|
<Link to="/" className="flex items-center gap-2 font-bold text-gray-900 hover:text-blue-700 transition-colors">
|
|
<svg className="w-6 h-6 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
|
|
d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" />
|
|
</svg>
|
|
Animal Experiments DB
|
|
</Link>
|
|
<nav className="text-sm text-gray-500">
|
|
<Link to="/" className="hover:text-blue-600 transition-colors">Dashboard</Link>
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|
|
|
|
function NotFound() {
|
|
return (
|
|
<div className="max-w-5xl mx-auto px-4 py-20 text-center">
|
|
<h1 className="text-4xl font-bold text-gray-300 mb-4">404</h1>
|
|
<p className="text-gray-500 mb-6">Page not found.</p>
|
|
<Link to="/" className="text-blue-600 hover:underline">← Return to Dashboard</Link>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function Footer() {
|
|
const [dbTimezone, setDbTimezone] = useState(null);
|
|
useEffect(() => {
|
|
systemApi.info().then((d) => setDbTimezone(d.dbTimezone)).catch(() => {});
|
|
}, []);
|
|
return (
|
|
<footer className="border-t border-gray-200 mt-12 py-3">
|
|
<div className="max-w-5xl mx-auto px-4 text-xs text-gray-400 text-right">
|
|
Database timezone: <span className="font-mono">{dbTimezone ?? '…'}</span>
|
|
</div>
|
|
</footer>
|
|
);
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 flex flex-col">
|
|
<Navbar />
|
|
<main className="flex-1">
|
|
<Routes>
|
|
<Route path="/" element={<Dashboard />} />
|
|
<Route path="/experiments/:id" element={<ExperimentDetail />} />
|
|
<Route path="/experiments/:id/day/:date" element={<ExperimentDayView />} />
|
|
<Route path="/animals/:id" element={<AnimalDetail />} />
|
|
<Route path="/daily-statuses/:id" element={<DailyStatusDetail />} />
|
|
<Route path="*" element={<NotFound />} />
|
|
</Routes>
|
|
</main>
|
|
<Footer />
|
|
</div>
|
|
);
|
|
}
|