import { useEffect, useMemo, useState } from "react"; import { AnimatePresence, motion } from "framer-motion"; import toast, { Toaster } from "react-hot-toast"; import SearchRoomsPage from "./pages/SearchRoomsPage.jsx"; import CreateReservationPage from "./pages/CreateReservationPage.jsx"; import ApprovalsPage from "./pages/ApprovalsPage.jsx"; import ReportsPage from "./pages/ReportsPage.jsx"; import AdminPanelPage from "./pages/AdminPanelPage.jsx"; import { Activity, ArrowRight, BarChart3, Boxes, CalendarCheck, CheckCircle2, ClipboardCheck, Database, DoorOpen, LogOut, PlusCircle, Search, ShieldCheck, Sparkles, UserRound, Users, } from "lucide-react"; import { getDashboardStats, getHealth, loginUser, registerUser, } from "./api.js"; function App() { const [activeUser, setActiveUser] = useState(() => { try { const savedUser = localStorage.getItem("activeUser"); return savedUser ? JSON.parse(savedUser) : null; } catch { localStorage.removeItem("activeUser"); return null; } }); const [authView, setAuthView] = useState(() => { return localStorage.getItem("authView") || "landing"; }); const [activePage, setActivePage] = useState(() => { return localStorage.getItem("activePage") || "dashboard"; }); function changeAuthView(view) { localStorage.setItem("authView", view); setAuthView(view); } function handleLogin(user) { localStorage.setItem("activeUser", JSON.stringify(user)); localStorage.setItem("activePage", "dashboard"); localStorage.removeItem("authView"); setActiveUser(user); setActivePage("dashboard"); toast.success(`Logged in as ${user.fullName}`); } function handleLogout() { localStorage.removeItem("activeUser"); localStorage.removeItem("activePage"); localStorage.setItem("authView", "landing"); setActiveUser(null); setAuthView("landing"); setActivePage("dashboard"); } useEffect(() => { if (activeUser) { localStorage.setItem("activePage", activePage); } }, [activePage, activeUser]); if (!activeUser) { return ( <> {authView === "landing" && ( changeAuthView("login")} onRegisterClick={() => changeAuthView("register")} /> )} {authView === "login" && ( changeAuthView("landing")} onRegisterClick={() => changeAuthView("register")} /> )} {authView === "register" && ( changeAuthView("landing")} onLoginClick={() => changeAuthView("login")} onRegistered={handleLogin} /> )} ); } return ( <> {activePage === "dashboard" && ( )} {activePage === "search" && ( )} {activePage === "create" && ( )} {activePage === "approvals" && ( )} {activePage === "reports" && ( )} {activePage === "admin" && ( )} ); } function LandingScreen({ onLoginClick, onRegisterClick }) { return (
Room Management Platform

Smart Room Reservation System

Search available rooms, create reservation requests, manage approvals, and monitor resource usage through live PostgreSQL reports.

Live dashboard

Reservation Control Center

Online
Rooms Availability search
Approvals Role-based decisions
Reports Advanced analytics
Search available rooms
Create pending reservation
Approve through stored function
); } function LoginScreen({ onLogin, onBack, onRegisterClick }) { const [form, setForm] = useState({ identifier: "", password: "", }); const [loading, setLoading] = useState(false); function updateField(field, value) { setForm((prev) => ({ ...prev, [field]: value })); } async function submit(e) { e.preventDefault(); if (!form.identifier.trim()) { toast.error("Username or email is required."); return; } if (!form.password.trim()) { toast.error("Password is required."); return; } setLoading(true); try { const user = await loginUser({ identifier: form.identifier, password: form.password, }); onLogin(user); } catch (err) { toast.error(err.message); } finally { setLoading(false); } } return (
Role-based access

Access the reservation workspace.

Continue to the reservation system with the permissions assigned to your user role. Manage reservations, approvals, reports and administrative modules.

Sign In

Enter your account details and continue with role-based access.

); } function RegisterScreen({ onBack, onLoginClick, onRegistered }) { const [form, setForm] = useState({ fullName: "", email: "", username: "", password: "", confirmPassword: "", }); const [loading, setLoading] = useState(false); function updateField(field, value) { setForm((prev) => ({ ...prev, [field]: value, })); } async function submit(e) { e.preventDefault(); if (!form.fullName.trim()) { toast.error("Full name is required."); return; } if (!form.email.trim()) { toast.error("Email is required."); return; } if (!form.username.trim()) { toast.error("Username is required."); return; } if (!form.password.trim()) { toast.error("Password is required."); return; } if (form.password.length < 8) { toast.error("Password must contain at least 8 characters."); return; } if (form.password !== form.confirmPassword) { toast.error("Passwords do not match."); return; } setLoading(true); try { const user = await registerUser({ fullName: form.fullName.trim(), email: form.email.trim(), username: form.username.trim(), password: form.password, }); toast.success("Account created successfully."); onRegistered(user); } catch (err) { toast.error(err.message); } finally { setLoading(false); } } return (
Account creation

Create your account

Create a standard user account to submit room and equipment reservation requests. Administrative permissions are assigned separately by the system administrator.

); } function AppShell({ activeUser, activePage, setActivePage, onLogout, children }) { const items = useMemo(() => { const base = [ { key: "dashboard", label: "Dashboard", icon: Activity, roles: ["regular", "approver", "admin"] }, { key: "search", label: "Search Rooms", icon: Search, roles: ["regular", "approver", "admin"] }, { key: "create", label: "Create Reservation", icon: PlusCircle, roles: ["regular", "approver", "admin"] }, { key: "approvals", label: "Approvals", icon: ClipboardCheck, roles: ["approver", "admin"] }, { key: "reports", label: "Reports", icon: BarChart3, roles: ["regular", "approver", "admin"] }, { key: "admin", label: "Admin Panel", icon: ShieldCheck, roles: ["admin"] }, ]; return base.filter((item) => item.roles.includes(activeUser.role)); }, [activeUser.role]); const activeItem = items.find((item) => item.key === activePage); useEffect(() => { const pageIsAllowed = items.some((item) => item.key === activePage); if (!pageIsAllowed) { localStorage.setItem("activePage", "dashboard"); setActivePage("dashboard"); } }, [items, activePage, setActivePage]); return (

{activeItem?.label || "Room Reservation"}

Live database
{activeUser.fullName} {activeUser.role}
{children}
); } function Dashboard({ activeUser, setActivePage }) { const [stats, setStats] = useState(null); const [databaseOnline, setDatabaseOnline] = useState(false); const [loading, setLoading] = useState(true); useEffect(() => { let mounted = true; async function loadDashboard() { setLoading(true); try { const [statsResponse, healthResponse] = await Promise.all([ getDashboardStats(), getHealth().catch(() => null), ]); if (!mounted) { return; } setStats(statsResponse); setDatabaseOnline(Boolean(healthResponse)); } catch (err) { if (!mounted) { return; } setDatabaseOnline(false); toast.error(err.message || "Dashboard data could not be loaded."); } finally { if (mounted) { setLoading(false); } } } loadDashboard(); return () => { mounted = false; }; }, []); const safeStats = stats || { totalRooms: 0, totalEquipmentTypes: 0, totalUsers: 0, pendingReservations: 0, approvedReservations: 0, }; const firstName = activeUser?.fullName?.split(" ")?.[0] || "User"; const role = activeUser?.role || "regular"; const statCards = [ { label: "Rooms", value: safeStats.totalRooms, meta: "Available spaces", icon: DoorOpen, tone: "blue", }, { label: "Equipment", value: safeStats.totalEquipmentTypes, meta: "Resource types", icon: Boxes, tone: "purple", }, { label: "Users", value: safeStats.totalUsers, meta: "System accounts", icon: Users, tone: "cyan", }, { label: "Pending", value: safeStats.pendingReservations, meta: "Awaiting decision", icon: CalendarCheck, tone: "orange", }, { label: "Approved", value: safeStats.approvedReservations, meta: "Confirmed bookings", icon: CheckCircle2, tone: "green", }, ]; const quickActions = [ { page: "search", title: "Search available rooms", description: "Check capacity, type, equipment and time overlap.", icon: Search, }, { page: "create", title: "Create reservation", description: "Submit a room or equipment request for approval.", icon: PlusCircle, }, ...(role === "approver" || role === "admin" ? [ { page: "approvals", title: "Review approvals", description: "Approve or reject pending reservation requests.", icon: ClipboardCheck, }, ] : []), { page: "reports", title: "Open reports", description: "View advanced SQL analytical reports.", icon: BarChart3, }, ...(role === "admin" ? [ { page: "admin", title: "Admin panel", description: "Manage administrative project modules.", icon: ShieldCheck, }, ] : []), ]; return (
System overview

Welcome back, {firstName}

Manage reservations, approvals, room availability and analytical PostgreSQL reports from one control center.

{statCards.map((card) => { const Icon = card.icon; return (
{card.label} {loading ? "—" : card.value}

{card.meta}

); })}

Quick actions

Frequently used reservation workflows.

{quickActions.map((action) => { const Icon = action.icon; return ( ); })}
); } function PageMotion({ children }) { return ( {children} ); } function getInitials(name) { if (!name) { return "U"; } return name .split(" ") .map((part) => part[0]) .slice(0, 2) .join("") .toUpperCase(); } export default App;