feat: complete phase 5 -- error hardening, frontend, Docker, demo, docs
Backend: - ConversationTracker: Protocol + PostgresConversationTracker for lifecycle tracking - Error handler: ErrorCategory enum, classify_error(), with_retry() exponential backoff - Wire PostgresAnalyticsRecorder + ConversationTracker into ws_handler - Rate limiting (10 msg/10s per thread), edge case hardening - Health endpoint GET /api/health, version 0.5.0 - Demo seed data script + sample OpenAPI spec Frontend (all new): - React Router with NavBar (Chat / Replay / Dashboard / Review) - ReplayListPage + ReplayPage with ReplayTimeline component - DashboardPage with MetricCard, range selector, zero-state - ReviewPage for OpenAPI classification review - ErrorBanner for WebSocket disconnect handling - API client (api.ts) with typed fetch wrappers Infrastructure: - Frontend Dockerfile (multi-stage node -> nginx) - nginx.conf with SPA routing + API/WS proxy - docker-compose.yml with frontend service + healthchecks - .env.example files (root + backend) Documentation: - README.md with quick start and architecture - Agent configuration guide - OpenAPI import guide - Deployment guide - Demo script 48 new tests, 449 total passing, 92.87% coverage
This commit is contained in:
64
frontend/src/components/NavBar.tsx
Normal file
64
frontend/src/components/NavBar.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { NavLink } from "react-router-dom";
|
||||
|
||||
const navLinks = [
|
||||
{ to: "/", label: "Chat", exact: true },
|
||||
{ to: "/replay", label: "Replay" },
|
||||
{ to: "/dashboard", label: "Dashboard" },
|
||||
{ to: "/review", label: "API Review" },
|
||||
];
|
||||
|
||||
const styles: Record<string, React.CSSProperties> = {
|
||||
nav: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "0",
|
||||
padding: "0 16px",
|
||||
borderBottom: "1px solid #e0e0e0",
|
||||
background: "#fff",
|
||||
height: "48px",
|
||||
boxShadow: "0 1px 4px rgba(0,0,0,0.06)",
|
||||
},
|
||||
brand: {
|
||||
fontWeight: 700,
|
||||
fontSize: "16px",
|
||||
color: "#1a1a1a",
|
||||
marginRight: "24px",
|
||||
textDecoration: "none",
|
||||
},
|
||||
link: {
|
||||
padding: "0 14px",
|
||||
height: "48px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
fontSize: "14px",
|
||||
color: "#555",
|
||||
textDecoration: "none",
|
||||
borderBottom: "2px solid transparent",
|
||||
transition: "color 0.15s, border-color 0.15s",
|
||||
},
|
||||
activeLink: {
|
||||
color: "#1976d2",
|
||||
borderBottom: "2px solid #1976d2",
|
||||
},
|
||||
};
|
||||
|
||||
export function NavBar() {
|
||||
return (
|
||||
<nav style={styles.nav}>
|
||||
<span style={styles.brand}>Smart Support</span>
|
||||
{navLinks.map(({ to, label }) => (
|
||||
<NavLink
|
||||
key={to}
|
||||
to={to}
|
||||
end={to === "/"}
|
||||
style={({ isActive }) => ({
|
||||
...styles.link,
|
||||
...(isActive ? styles.activeLink : {}),
|
||||
})}
|
||||
>
|
||||
{label}
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user