gadget/gadget-code/frontend/src/App.tsx
2026-04-30 09:40:27 -04:00

150 lines
4.6 KiB
TypeScript

import { useState, useEffect, createContext, useContext } from 'react';
import { BrowserRouter, Routes, Route, Navigate, useNavigate } from 'react-router-dom';
import { User } from './lib/api';
import { socketClient } from './lib/socket';
import Header from './components/Header';
import StatusBar from './components/StatusBar';
import Home from './pages/Home';
import ProjectManager from './pages/ProjectManager';
import SignIn from './pages/SignIn';
import SignUp from './pages/SignUp';
import ChatSessionView from './pages/ChatSessionView';
import DroneManager from './pages/DroneManager';
const TOKEN_KEY = 'dtp_auth_token';
const USER_KEY = 'dtp_user';
const PROJECT_KEY = 'dtp_current_project';
function getStoredUser(): User | null {
try {
const userData = localStorage.getItem(USER_KEY);
return userData ? JSON.parse(userData) : null;
} catch {
return null;
}
}
function setStoredUser(user: User | null) {
if (user) {
localStorage.setItem(USER_KEY, JSON.stringify(user));
} else {
localStorage.removeItem(USER_KEY);
}
}
export function getStoredProject(): string | null {
return localStorage.getItem(PROJECT_KEY);
}
export function setStoredProject(slug: string | null) {
if (slug) {
localStorage.setItem(PROJECT_KEY, slug);
} else {
localStorage.removeItem(PROJECT_KEY);
}
}
interface AppContextType {
user: User | null;
currentProject: string | null;
setCurrentProject: (slug: string | null) => void;
onSignOut: () => void;
}
const AppContext = createContext<AppContextType | null>(null);
export function useAppContext(): AppContextType {
const ctx = useContext(AppContext);
if (!ctx) throw new Error('useAppContext must be used within App provider');
return ctx;
}
export default function App() {
const [user, setUser] = useState<User | null>(null);
const [currentProject, setCurrentProject] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const storedUser = getStoredUser();
const storedToken = localStorage.getItem(TOKEN_KEY);
const storedProject = localStorage.getItem(PROJECT_KEY);
if (storedUser && storedToken) {
setUser(storedUser);
socketClient.connect(storedToken);
}
if (storedProject) {
setCurrentProject(storedProject);
}
setLoading(false);
}, []);
const handleSignInSuccess = (newUser: User, token: string) => {
localStorage.setItem(TOKEN_KEY, token);
setStoredUser(newUser);
setUser(newUser);
socketClient.connect(token);
};
const handleSignOut = () => {
localStorage.removeItem(TOKEN_KEY);
localStorage.removeItem(PROJECT_KEY);
setStoredUser(null);
setUser(null);
setCurrentProject(null);
socketClient.disconnect();
};
const handleSetCurrentProject = (slug: string | null) => {
setStoredProject(slug);
setCurrentProject(slug);
};
if (loading) {
return (
<div className="h-screen flex items-center justify-center bg-bg-primary">
<div className="text-text-muted">Loading...</div>
</div>
);
}
return (
<AppContext.Provider value={{ user, currentProject, setCurrentProject: handleSetCurrentProject, onSignOut: handleSignOut }}>
<BrowserRouter>
<div className="h-screen flex flex-col bg-bg-primary">
<Header user={user} onSignOut={handleSignOut} />
<main className="flex-1 flex overflow-hidden">
<Routes>
<Route path="/" element={<Home user={user} />} />
<Route path="/projects" element={<ProjectManager user={user} />} />
<Route path="/projects/new" element={<ProjectManager user={user} />} />
<Route path="/projects/:slug" element={<ProjectManager user={user} />} />
<Route path="/drones" element={<DroneManager user={user} />} />
<Route path="/projects/:projectId/chat-session/:sessionId" element={<ChatSessionView />} />
<Route
path="/sign-in"
element={
user ? (
<Navigate to="/" replace />
) : (
<SignIn onSuccess={handleSignInSuccess} />
)
}
/>
<Route
path="/sign-up"
element={
user ? (
<Navigate to="/" replace />
) : (
<SignUp onSuccess={handleSignInSuccess} />
)
}
/>
</Routes>
</main>
<StatusBar projectSlug={currentProject} />
</div>
</BrowserRouter>
</AppContext.Provider>
);
}