// App.js
import React, { useState, useEffect } from "react";
import Navbar from "./components/Navbar";
import HomePage from "./pages/HomePage";
import VoiceChatPage from "./pages/VoiceChatPage";
import SwarmControlPage from "./pages/SwarmControlPage";
import LoginPage from "./pages/LoginPage";
import SettingsPage from "./pages/SettingsPage";
import ProfilePage from "./pages/ProfilePage";
import NodesPage from "./pages/NodesPage";
import SkillsPage from "./pages/SkillsPage";
import { getUserStatus, logout, getUserProfile } from "./services/apiService";
const Icon = ({ path, onClick, className }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={`w-6 h-6 cursor-pointer transition-colors duration-200 ${className}`}
onClick={onClick}
>
<path d={path} />
</svg>
);
export default function App() {
const [currentPage, setCurrentPage] = useState("home");
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [userId, setUserId] = useState(null);
const [userProfile, setUserProfile] = useState(null);
const authenticatedPages = ["voice-chat", "swarm-control", "settings", "profile", "nodes", "skills"];
const pageToPath = {
"home": "/",
"voice-chat": "/voice",
"swarm-control": "/swarm",
"settings": "/settings",
"profile": "/profile",
"nodes": "/nodes",
"skills": "/skills",
"login": "/login"
};
const pathToPage = Object.fromEntries(Object.entries(pageToPath).map(([pk, pv]) => [pv, pk]));
// Sync state with URL on mount and handle popstate
useEffect(() => {
const handlePopState = () => {
const path = window.location.pathname;
const page = pathToPage[path] || "home";
setCurrentPage(page);
};
window.addEventListener("popstate", handlePopState);
// Initial sync
const initialPath = window.location.pathname;
const initialPage = pathToPage[initialPath] || "home";
if (initialPage !== currentPage) {
setCurrentPage(initialPage);
}
return () => window.removeEventListener("popstate", handlePopState);
}, []);
useEffect(() => {
const urlParams = new URLSearchParams(window.location.search);
const userIdFromUrl = urlParams.get('user_id');
if (userIdFromUrl && !localStorage.getItem('userId')) {
localStorage.setItem('userId', userIdFromUrl);
console.log('User ID from URL saved to localStorage:', userIdFromUrl);
window.history.replaceState({}, document.title, window.location.pathname);
}
}, []);
useEffect(() => {
const checkLoginStatus = async () => {
const storedUserId = localStorage.getItem("userId");
if (storedUserId) {
try {
const status = await getUserStatus(storedUserId);
if (status.is_logged_in) {
setIsLoggedIn(true);
setUserId(storedUserId);
// Fetch profile on success
const profile = await getUserProfile();
setUserProfile(profile);
if (currentPage === "login") {
setCurrentPage("home");
}
} else {
setIsLoggedIn(false);
setUserId(null);
setUserProfile(null);
localStorage.removeItem("userId");
if (authenticatedPages.includes(currentPage)) {
setCurrentPage("login");
}
}
} catch (error) {
console.error("Failed to check user status:", error);
setIsLoggedIn(false);
setUserId(null);
setUserProfile(null);
localStorage.removeItem("userId");
if (authenticatedPages.includes(currentPage)) {
setCurrentPage("login");
}
}
} else {
setIsLoggedIn(false);
setUserId(null);
setUserProfile(null);
if (authenticatedPages.includes(currentPage)) {
setCurrentPage("login");
}
}
};
checkLoginStatus();
}, [currentPage]);
const handleLogout = async () => {
try {
await logout();
setIsLoggedIn(false);
setUserId(null);
setUserProfile(null);
localStorage.removeItem("userId");
setCurrentPage("home");
} catch (error) {
console.error("Logout failed:", error);
}
};
const handleNavigate = (page) => {
if (authenticatedPages.includes(page) && !isLoggedIn) {
setCurrentPage("login");
window.history.pushState({}, "", pageToPath["login"]);
} else {
setCurrentPage(page);
window.history.pushState({}, "", pageToPath[page] || "/");
}
};
const toggleSidebar = () => {
setIsSidebarOpen(!isSidebarOpen);
};
const renderPage = () => {
switch (currentPage) {
case "home":
// Pass both isLoggedIn and handleLogout to HomePage
return <HomePage onNavigate={handleNavigate} isLoggedIn={isLoggedIn} />;
case "voice-chat":
return <VoiceChatPage Icon={Icon} />;
case "swarm-control":
return <SwarmControlPage onNavigate={handleNavigate} Icon={Icon} />;
case "settings":
// Only admins can see global settings
if (userProfile?.role !== "admin") {
return <ProfilePage onLogout={handleLogout} />;
}
return <SettingsPage />;
case "profile":
return <ProfilePage onLogout={handleLogout} />;
case "nodes":
return <NodesPage user={userProfile} />;
case "skills":
return <SkillsPage user={userProfile} Icon={Icon} />;
case "login":
return <LoginPage />;
default:
return <HomePage onNavigate={handleNavigate} isLoggedIn={isLoggedIn} />;
}
};
return (
<div className="flex h-screen overflow-hidden bg-gray-100 dark:bg-gray-900 font-sans">
{currentPage !== "login" && (
<Navbar
isOpen={isSidebarOpen}
onToggle={toggleSidebar}
onNavigate={handleNavigate}
onLogout={handleLogout}
isLoggedIn={isLoggedIn}
user={userProfile}
Icon={Icon}
/>
)}
<div className="flex-grow overflow-hidden">
{renderPage()}
</div>
</div>
);
}