214 lines
7.2 KiB
TypeScript
214 lines
7.2 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import ReactDOM from 'react-dom/client';
|
|
import {
|
|
Button,
|
|
SessionDrawer,
|
|
SessionSidebar,
|
|
TimelineFeed,
|
|
getSampleAgentSessions,
|
|
getSampleAgentSteps
|
|
} from '@ra-aid/common';
|
|
// The CSS import happens through the common package's index.ts
|
|
|
|
// Theme management helper function
|
|
const setupTheme = () => {
|
|
// Check if theme preference is stored in localStorage
|
|
const storedTheme = localStorage.getItem('theme');
|
|
|
|
// Default to dark mode unless explicitly set to light
|
|
const isDark = storedTheme ? storedTheme === 'dark' : true;
|
|
|
|
// Apply theme class to document element (html) for better CSS specificity
|
|
if (isDark) {
|
|
document.documentElement.classList.add('dark');
|
|
} else {
|
|
document.documentElement.classList.remove('dark');
|
|
}
|
|
|
|
// Store the current theme preference
|
|
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
|
|
|
return isDark;
|
|
};
|
|
|
|
const App = () => {
|
|
// State for drawer open/close
|
|
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
|
|
|
// State for selected session
|
|
const [selectedSessionId, setSelectedSessionId] = useState<string | null>(null);
|
|
|
|
// State for theme (dark is default)
|
|
const [isDarkTheme, setIsDarkTheme] = useState(true);
|
|
|
|
// Get sample data
|
|
const sessions = getSampleAgentSessions();
|
|
const allSteps = getSampleAgentSteps();
|
|
|
|
// Set up theme on component mount
|
|
useEffect(() => {
|
|
const isDark = setupTheme();
|
|
setIsDarkTheme(isDark);
|
|
}, []);
|
|
|
|
// Set initial selected session if none selected
|
|
useEffect(() => {
|
|
if (!selectedSessionId && sessions.length > 0) {
|
|
setSelectedSessionId(sessions[0].id);
|
|
}
|
|
}, [sessions, selectedSessionId]);
|
|
|
|
// Filter steps for selected session
|
|
const selectedSessionSteps = selectedSessionId
|
|
? allSteps.filter(step => sessions.find(s => s.id === selectedSessionId)?.steps.some(s => s.id === step.id))
|
|
: [];
|
|
|
|
// Handle session selection
|
|
const handleSessionSelect = (sessionId: string) => {
|
|
setSelectedSessionId(sessionId);
|
|
setIsDrawerOpen(false); // Close drawer on selection (mobile)
|
|
};
|
|
|
|
// Toggle theme function
|
|
const toggleTheme = () => {
|
|
const newIsDark = !isDarkTheme;
|
|
setIsDarkTheme(newIsDark);
|
|
|
|
// Update document element class
|
|
if (newIsDark) {
|
|
document.documentElement.classList.add('dark');
|
|
} else {
|
|
document.documentElement.classList.remove('dark');
|
|
}
|
|
|
|
// Save to localStorage
|
|
localStorage.setItem('theme', newIsDark ? 'dark' : 'light');
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-background text-foreground flex flex-col">
|
|
{/* Header */}
|
|
<header className="border-b border-border py-4 px-4 sticky top-0 z-10 bg-background">
|
|
<div className="flex justify-between items-center">
|
|
<h1 className="text-2xl font-bold bg-gradient-to-r from-blue-400 to-purple-500 inline-block text-transparent bg-clip-text">
|
|
RA-Aid
|
|
</h1>
|
|
|
|
<div className="flex items-center gap-2">
|
|
{/* Theme toggle button */}
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={toggleTheme}
|
|
aria-label={isDarkTheme ? "Switch to light mode" : "Switch to dark mode"}
|
|
className="mr-2"
|
|
>
|
|
{isDarkTheme ? (
|
|
// Sun icon for light mode toggle
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
>
|
|
<circle cx="12" cy="12" r="5" />
|
|
<line x1="12" y1="1" x2="12" y2="3" />
|
|
<line x1="12" y1="21" x2="12" y2="23" />
|
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
|
<line x1="1" y1="12" x2="3" y2="12" />
|
|
<line x1="21" y1="12" x2="23" y2="12" />
|
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
|
</svg>
|
|
) : (
|
|
// Moon icon for dark mode toggle
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
>
|
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
|
</svg>
|
|
)}
|
|
</Button>
|
|
|
|
{/* Mobile drawer toggle - show only on small screens */}
|
|
<div className="md:hidden">
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => setIsDrawerOpen(true)}
|
|
aria-label="Open menu"
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="lucide lucide-menu"><line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/></svg>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Main content */}
|
|
<div className="flex flex-1 overflow-hidden">
|
|
{/* Desktop sidebar - hidden on mobile */}
|
|
<SessionSidebar
|
|
sessions={sessions}
|
|
currentSessionId={selectedSessionId || undefined}
|
|
onSelectSession={handleSessionSelect}
|
|
className="shrink-0"
|
|
/>
|
|
|
|
{/* Mobile drawer */}
|
|
<SessionDrawer
|
|
sessions={sessions}
|
|
currentSessionId={selectedSessionId || undefined}
|
|
onSelectSession={handleSessionSelect}
|
|
/>
|
|
|
|
{/* Main content area */}
|
|
<main className="flex-1 overflow-auto p-4">
|
|
{selectedSessionId ? (
|
|
<>
|
|
<h2 className="text-xl font-semibold mb-4">
|
|
Session: {sessions.find(s => s.id === selectedSessionId)?.name || 'Unknown'}
|
|
</h2>
|
|
<TimelineFeed
|
|
steps={selectedSessionSteps}
|
|
maxHeight="calc(100vh - 14rem)"
|
|
/>
|
|
</>
|
|
) : (
|
|
<div className="flex items-center justify-center h-full">
|
|
<p className="text-muted-foreground">Select a session to view details</p>
|
|
</div>
|
|
)}
|
|
</main>
|
|
</div>
|
|
|
|
<footer className="border-t border-border py-4 px-4 text-center text-muted-foreground text-sm">
|
|
<p>Built with shadcn/ui components from the RA-Aid common package</p>
|
|
</footer>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// Initialize theme before rendering the app
|
|
setupTheme();
|
|
|
|
const root = ReactDOM.createRoot(document.getElementById('root')!);
|
|
root.render(
|
|
<React.StrictMode>
|
|
<App />
|
|
</React.StrictMode>
|
|
); |