diff --git a/Data/Server/WebUI/src/Admin/Server_Info.jsx b/Data/Server/WebUI/src/Admin/Server_Info.jsx index f4b7fd9..d218839 100644 --- a/Data/Server/WebUI/src/Admin/Server_Info.jsx +++ b/Data/Server/WebUI/src/Admin/Server_Info.jsx @@ -1,9 +1,12 @@ import React, { useEffect, useState } from "react"; -import { Paper, Box, Typography } from "@mui/material"; +import { Paper, Box, Typography, Button } from "@mui/material"; +import { GitHub as GitHubIcon, InfoOutlined as InfoIcon } from "@mui/icons-material"; +import { CreditsDialog } from "../Dialogs.jsx"; export default function ServerInfo({ isAdmin = false }) { const [serverTime, setServerTime] = useState(null); const [error, setError] = useState(null); + const [aboutOpen, setAboutOpen] = useState(false); useEffect(() => { if (!isAdmin) return; @@ -39,7 +42,32 @@ export default function ServerInfo({ isAdmin = false }) { {error ? `Error: ${error}` : (serverTime || 'Loading...')} + + + Project Links + + + + + + setAboutOpen(false)} /> ); } diff --git a/Data/Server/WebUI/src/App.jsx b/Data/Server/WebUI/src/App.jsx index d7866b4..7bbb067 100644 --- a/Data/Server/WebUI/src/App.jsx +++ b/Data/Server/WebUI/src/App.jsx @@ -5,7 +5,7 @@ import React, { useState, useEffect, useCallback, useRef } from "react"; import { ReactFlowProvider } from "reactflow"; import "reactflow/dist/style.css"; import { - CloseAllDialog, CreditsDialog, RenameTabDialog, TabContextMenu, NotAuthorizedDialog + CloseAllDialog, RenameTabDialog, TabContextMenu, NotAuthorizedDialog } from "./Dialogs"; import NavigationSidebar from "./Navigation_Sidebar"; @@ -16,9 +16,7 @@ import { } from "@mui/material"; import { KeyboardArrowDown as KeyboardArrowDownIcon, - InfoOutlined as InfoOutlinedIcon, - GitHub as GitHub, - People as PeopleIcon, + Logout as LogoutIcon, NavigateNext as NavigateNextIcon } from "@mui/icons-material"; @@ -91,8 +89,7 @@ export default function App() { const [currentPage, setCurrentPage] = useState("devices"); const [selectedDevice, setSelectedDevice] = useState(null); - const [aboutAnchorEl, setAboutAnchorEl] = useState(null); - const [creditsDialogOpen, setCreditsDialogOpen] = useState(false); + const [userMenuAnchorEl, setUserMenuAnchorEl] = useState(null); const [confirmCloseOpen, setConfirmCloseOpen] = useState(false); const [renameDialogOpen, setRenameDialogOpen] = useState(false); const [renameTabId, setRenameTabId] = useState(null); @@ -102,6 +99,7 @@ export default function App() { const fileInputRef = useRef(null); const [user, setUser] = useState(null); const [userRole, setUserRole] = useState(null); + const [userDisplayName, setUserDisplayName] = useState(null); const [editingJob, setEditingJob] = useState(null); const [jobsRefreshToken, setJobsRefreshToken] = useState(0); const [notAuthorizedOpen, setNotAuthorizedOpen] = useState(false); @@ -180,6 +178,7 @@ export default function App() { if (Date.now() - data.timestamp < 3600 * 1000) { setUser(data.username); setUserRole(data.role || null); + setUserDisplayName(data.display_name || data.username); } else { localStorage.removeItem("borealis_session"); } @@ -194,9 +193,10 @@ export default function App() { const me = await resp.json(); setUser(me.username); setUserRole(me.role || null); + setUserDisplayName(me.display_name || me.username); localStorage.setItem( "borealis_session", - JSON.stringify({ username: me.username, role: me.role, timestamp: Date.now() }) + JSON.stringify({ username: me.username, display_name: me.display_name || me.username, role: me.role, timestamp: Date.now() }) ); } } catch {} @@ -206,10 +206,25 @@ export default function App() { const handleLoginSuccess = ({ username, role }) => { setUser(username); setUserRole(role || null); + setUserDisplayName(username); localStorage.setItem( "borealis_session", - JSON.stringify({ username, role: role || null, timestamp: Date.now() }) + JSON.stringify({ username, display_name: username, role: role || null, timestamp: Date.now() }) ); + // Refresh full profile (to get display_name) in background + (async () => { + try { + const resp = await fetch('/api/auth/me', { credentials: 'include' }); + if (resp.ok) { + const me = await resp.json(); + setUserDisplayName(me.display_name || me.username); + localStorage.setItem( + "borealis_session", + JSON.stringify({ username: me.username, display_name: me.display_name || me.username, role: me.role, timestamp: Date.now() }) + ); + } + } catch {} + })(); }; useEffect(() => { @@ -257,9 +272,17 @@ export default function App() { ); }, [activeTabId]); - const handleAboutMenuOpen = (event) => setAboutAnchorEl(event.currentTarget); - const handleAboutMenuClose = () => setAboutAnchorEl(null); - const openCreditsDialog = () => { handleAboutMenuClose(); setCreditsDialogOpen(true); }; + const handleUserMenuOpen = (event) => setUserMenuAnchorEl(event.currentTarget); + const handleUserMenuClose = () => setUserMenuAnchorEl(null); + const handleLogout = async () => { + try { + await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' }); + } catch {} + try { localStorage.removeItem('borealis_session'); } catch {} + setUser(null); + setUserRole(null); + setUserDisplayName(null); + }; const handleTabRightClick = (evt, tabId) => { evt.preventDefault(); @@ -635,23 +658,19 @@ export default function App() { })} - {/* Spacer to keep About aligned right */} + {/* Spacer to keep user menu aligned right */} - - { handleAboutMenuClose(); window.open("https://github.com/bunny-lab-io/Borealis", "_blank"); }}> - Github Project - - - Credits + + { handleUserMenuClose(); handleLogout(); }}> + Logout @@ -664,7 +683,6 @@ export default function App() { setConfirmCloseOpen(false)} onConfirm={() => {}} /> - setCreditsDialogOpen(false)} />