Refreshed Navigation Sidebar Design

This commit is contained in:
2025-11-05 21:59:51 -07:00
parent d05b25ff9c
commit a885db45c4
8 changed files with 211 additions and 445 deletions

View File

@@ -1,23 +0,0 @@
{
"category": "script",
"description": "Import/export test script.",
"files": [],
"name": "Import Script",
"script": "V3JpdGUtSG9zdCAicm91bmQgdHJpcCBleHBvcnQi",
"script_encoding": "base64",
"sites": {
"mode": "all",
"values": []
},
"timeout_seconds": 45,
"type": "powershell",
"variables": [
{
"default": "",
"label": "Example",
"name": "example",
"type": "string"
}
],
"version": 2
}

View File

@@ -1,23 +0,0 @@
{
"category": "script",
"description": "Import/export test script.",
"files": [],
"name": "Import Script",
"script": "V3JpdGUtSG9zdCAicm91bmQgdHJpcCBleHBvcnQi",
"script_encoding": "base64",
"sites": {
"mode": "all",
"values": []
},
"timeout_seconds": 45,
"type": "powershell",
"variables": [
{
"default": "",
"label": "Example",
"name": "example",
"type": "string"
}
],
"version": 2
}

View File

@@ -1,19 +0,0 @@
{
"description": "Import/export workflow test.",
"edges": [],
"nodes": [
{
"data": {
"label": "Input",
"value": "example"
},
"id": "node-1",
"position": {
"x": 10,
"y": 20
},
"type": "DataNode"
}
],
"tab_name": "Import Workflow"
}

View File

@@ -1,19 +0,0 @@
{
"description": "Import/export workflow test.",
"edges": [],
"nodes": [
{
"data": {
"label": "Input",
"value": "example"
},
"id": "node-1",
"position": {
"x": 10,
"y": 20
},
"type": "DataNode"
}
],
"tab_name": "Import Workflow"
}

View File

@@ -1,19 +0,0 @@
{
"description": "Import/export workflow test.",
"edges": [],
"nodes": [
{
"data": {
"label": "Input",
"value": "example"
},
"id": "node-1",
"position": {
"x": 10,
"y": 20
},
"type": "DataNode"
}
],
"tab_name": "Import Workflow"
}

View File

@@ -1,19 +0,0 @@
{
"description": "Import/export workflow test.",
"edges": [],
"nodes": [
{
"data": {
"label": "Input",
"value": "example"
},
"id": "node-1",
"position": {
"x": 10,
"y": 20
},
"type": "DataNode"
}
],
"tab_name": "Import Workflow"
}

View File

@@ -1,6 +1,6 @@
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/Navigation_Sidebar.jsx // Navigation_Sidebar.jsx — Clean Modern Matte + Glass Sidebar (no accent bars or header bubbles)
import React, { useState } from "react"; import React, { useMemo, useState } from "react";
import { import {
Accordion, Accordion,
AccordionSummary, AccordionSummary,
@@ -8,7 +8,8 @@ import {
Typography, Typography,
Box, Box,
ListItemButton, ListItemButton,
ListItemText ListItemText,
Divider,
} from "@mui/material"; } from "@mui/material";
import { import {
ExpandMore as ExpandMoreIcon, ExpandMore as ExpandMoreIcon,
@@ -16,21 +17,29 @@ import {
FilterAlt as FilterIcon, FilterAlt as FilterIcon,
Groups as GroupsIcon, Groups as GroupsIcon,
Work as JobsIcon, Work as JobsIcon,
Polyline as WorkflowsIcon,
Code as ScriptIcon,
PeopleOutline as CommunityIcon, PeopleOutline as CommunityIcon,
Apps as AssembliesIcon Apps as AssembliesIcon,
} from "@mui/icons-material"; LocationCity as SitesIcon,
import { LocationCity as SitesIcon } from "@mui/icons-material";
import {
Dns as ServerInfoIcon, Dns as ServerInfoIcon,
VpnKey as CredentialIcon, VpnKey as CredentialIcon,
PersonOutline as UserIcon, PersonOutline as UserIcon,
GitHub as GitHubIcon, GitHub as GitHubIcon,
Key as KeyIcon, Key as KeyIcon,
AdminPanelSettings as AdminPanelSettingsIcon AdminPanelSettings as AdminPanelSettingsIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
const COLORS = {
cyan: "#7db7ff",
violet: "#c084fc",
text: "#cbd5e1",
textActive: "#e6f2ff",
matte: "#0f141c",
line: "rgba(125,183,255,0.14)",
hover: "rgba(255,255,255,0.05)",
itemActiveBg:
"linear-gradient(90deg, rgba(125,183,255,0.14) 0%, rgba(125,183,255,0.06) 55%, rgba(125,183,255,0.00) 100%)",
};
function NavigationSidebar({ currentPage, onNavigate, isAdmin = false }) { function NavigationSidebar({ currentPage, onNavigate, isAdmin = false }) {
const [expandedNav, setExpandedNav] = useState({ const [expandedNav, setExpandedNav] = useState({
sites: true, sites: true,
@@ -38,9 +47,71 @@ function NavigationSidebar({ currentPage, onNavigate, isAdmin = false }) {
automation: true, automation: true,
filters: true, filters: true,
access: true, access: true,
admin: true admin: true,
}); });
const groupActive = useMemo(
() => ({
sites: currentPage === "sites",
devices: [
"devices",
"ssh_devices",
"winrm_devices",
"agent_devices",
"admin_device_approvals",
"admin_enrollment_codes",
].includes(currentPage),
automation: ["jobs", "assemblies", "community"].includes(currentPage),
filters: ["filters", "groups"].includes(currentPage),
access: [
"access_credentials",
"access_users",
"access_github_token",
].includes(currentPage),
admin: ["server_info"].includes(currentPage),
}),
[currentPage]
);
const Section = ({ title, k, children }) => (
<Accordion
expanded={expandedNav[k]}
onChange={(_, e) => setExpandedNav((s) => ({ ...s, [k]: e }))}
square
disableGutters
sx={{
"&:before": { display: "none" },
m: 0,
bgcolor: "transparent",
border: 0,
}}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon sx={{ color: COLORS.cyan }} />}
sx={{
minHeight: 38,
"& .MuiAccordionSummary-content": { m: 0, py: 0.5 },
backgroundColor: "rgba(255,255,255,0.02)",
borderTopRightRadius: 8,
borderBottomRightRadius: 8,
px: 1.5,
}}
>
<Typography
sx={{
fontSize: "0.85rem",
color: COLORS.cyan,
fontWeight: 700,
letterSpacing: 0.3,
}}
>
{title}
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ p: 0 }}>{children}</AccordionDetails>
</Accordion>
);
const NavItem = ({ icon, label, pageKey, indent = 0 }) => { const NavItem = ({ icon, label, pageKey, indent = 0 }) => {
const active = currentPage === pageKey; const active = currentPage === pageKey;
return ( return (
@@ -49,47 +120,28 @@ function NavigationSidebar({ currentPage, onNavigate, isAdmin = false }) {
sx={{ sx={{
pl: indent ? 4 : 2, pl: indent ? 4 : 2,
py: 1, py: 1,
color: active ? "#e6f2ff" : "#ccc", color: active ? COLORS.textActive : COLORS.text,
position: "relative", position: "relative",
background: active background: active ? COLORS.itemActiveBg : "transparent",
? "linear-gradient(90deg, rgba(88,166,255,0.10) 0%, rgba(88,166,255,0.03) 60%, rgba(88,166,255,0.00) 100%)" borderTopRightRadius: 10,
: "transparent", borderBottomRightRadius: 10,
borderTopRightRadius: 0, transition:
borderBottomRightRadius: 0, "background 160ms ease, box-shadow 160ms ease, color 160ms ease, transform 120ms ease",
boxShadow: active
? "inset 0 0 0 1px rgba(88,166,255,0.25)"
: "none",
transition: "background 160ms ease, box-shadow 160ms ease, color 160ms ease",
"&:hover": { "&:hover": {
background: active background: active ? COLORS.itemActiveBg : COLORS.hover,
? "linear-gradient(90deg, rgba(88,166,255,0.14) 0%, rgba(88,166,255,0.06) 60%, rgba(88,166,255,0.00) 100%)" },
: "#2c2c2c" "&:active": { transform: "translateY(0.5px)" },
}
}} }}
selected={active} selected={active}
> >
<Box
sx={{
position: "absolute",
left: 0,
top: 0,
bottom: 0,
width: active ? 3 : 0,
bgcolor: "#58a6ff",
borderTopRightRadius: 2,
borderBottomRightRadius: 2,
boxShadow: active ? "0 0 6px rgba(88,166,255,0.35)" : "none",
transition: "width 180ms ease, box-shadow 200ms ease"
}}
/>
{icon && ( {icon && (
<Box <Box
sx={{ sx={{
mr: 1, mr: 1,
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
color: active ? "#7db7ff" : "#58a6ff", color: active ? COLORS.cyan : "#8fbfff",
transition: "color 160ms ease" transition: "color 160ms ease",
}} }}
> >
{icon} {icon}
@@ -97,7 +149,11 @@ function NavigationSidebar({ currentPage, onNavigate, isAdmin = false }) {
)} )}
<ListItemText <ListItemText
primary={label} primary={label}
primaryTypographyProps={{ fontSize: "0.75rem", fontWeight: active ? 600 : 400 }} primaryTypographyProps={{
fontSize: "0.8rem",
fontWeight: active ? 600 : 400,
letterSpacing: 0.2,
}}
/> />
</ListItemButton> </ListItemButton>
); );
@@ -107,301 +163,133 @@ function NavigationSidebar({ currentPage, onNavigate, isAdmin = false }) {
<Box <Box
sx={{ sx={{
width: 260, width: 260,
bgcolor: "#121212",
borderRight: "1px solid #333",
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
overflow: "hidden" overflow: "hidden",
background:
"linear-gradient(180deg, rgba(64,164,255,0.05) 0%, rgba(192,132,252,0.04) 100%), " +
COLORS.matte,
borderRight: `1px solid ${COLORS.line}`,
backdropFilter: "blur(8px) saturate(130%)",
}} }}
> >
<Box sx={{ flex: 1, overflowY: "auto" }}> <Divider sx={{ borderColor: COLORS.line }} />
<Box sx={{ flex: 1, overflowY: "auto", p: 0.25 }}>
{/* Sites */} {/* Sites */}
{(() => { <Section title="Sites" k="sites">
const groupActive = currentPage === "sites"; <NavItem
return ( icon={<SitesIcon fontSize="small" />}
<Accordion label="All Sites"
expanded={expandedNav.sites} pageKey="sites"
onChange={(_, e) => setExpandedNav((s) => ({ ...s, sites: e }))} />
square </Section>
disableGutters
sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
sx={{
position: "relative",
background: groupActive
? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)"
: "#2c2c2c",
minHeight: "36px",
"& .MuiAccordionSummary-content": { margin: 0 },
"&::before": {
content: '""',
position: "absolute",
left: 0,
top: 0,
bottom: 0,
width: groupActive ? 3 : 0,
bgcolor: "#58a6ff",
borderTopRightRadius: 2,
borderBottomRightRadius: 2,
transition: "width 160ms ease"
}
}}
>
<Typography sx={{ fontSize: "0.85rem", color: "#58a6ff" }}>
<b>Sites</b>
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ p: 0, bgcolor: "#232323" }}>
<NavItem icon={<SitesIcon fontSize="small" />} label="All Sites" pageKey="sites" />
</AccordionDetails>
</Accordion>
);
})()}
{/* Inventory */} {/* Inventory */}
{(() => { <Section title="Inventory" k="devices">
const groupActive = ["devices", "ssh_devices", "winrm_devices", "agent_devices"].includes(currentPage); <NavItem
return ( icon={<AdminPanelSettingsIcon fontSize="small" />}
<Accordion label="Device Approvals"
expanded={expandedNav.devices} pageKey="admin_device_approvals"
onChange={(_, e) => setExpandedNav((s) => ({ ...s, devices: e }))} />
square <NavItem
disableGutters icon={<KeyIcon fontSize="small" />}
sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }} label="Enrollment Codes"
> pageKey="admin_enrollment_codes"
<AccordionSummary indent
expandIcon={<ExpandMoreIcon />} />
sx={{ <NavItem
position: "relative", icon={<DevicesIcon fontSize="small" />}
background: groupActive label="Devices"
? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)" pageKey="devices"
: "#2c2c2c", />
minHeight: "36px", <NavItem
"& .MuiAccordionSummary-content": { margin: 0 }, icon={<DevicesIcon fontSize="small" />}
"&::before": { label="Agent Devices"
content: '""', pageKey="agent_devices"
position: "absolute", indent
left: 0, />
top: 0, <NavItem
bottom: 0, icon={<DevicesIcon fontSize="small" />}
width: groupActive ? 3 : 0, label="SSH Devices"
bgcolor: "#58a6ff", pageKey="ssh_devices"
borderTopRightRadius: 2, indent
borderBottomRightRadius: 2, />
transition: "width 160ms ease" <NavItem
} icon={<DevicesIcon fontSize="small" />}
}} label="WinRM Devices"
> pageKey="winrm_devices"
<Typography sx={{ fontSize: "0.85rem", color: "#58a6ff" }}> indent
<b>Inventory</b> />
</Typography> </Section>
</AccordionSummary>
<AccordionDetails sx={{ p: 0, bgcolor: "#232323" }}>
<NavItem icon={<AdminPanelSettingsIcon fontSize="small" />} label="Device Approvals" pageKey="admin_device_approvals" />
<NavItem icon={<KeyIcon fontSize="small" />} label="Enrollment Codes" pageKey="admin_enrollment_codes" indent />
<NavItem icon={<DevicesIcon fontSize="small" />} label="Devices" pageKey="devices" />
<NavItem icon={<DevicesIcon fontSize="small" />} label="Agent Devices" pageKey="agent_devices" indent />
<NavItem icon={<DevicesIcon fontSize="small" />} label="SSH Devices" pageKey="ssh_devices" indent />
<NavItem icon={<DevicesIcon fontSize="small" />} label="WinRM Devices" pageKey="winrm_devices" indent />
</AccordionDetails>
</Accordion>
);
})()}
{/* Automation */} {/* Automation */}
{(() => { <Section title="Automation" k="automation">
const groupActive = ["jobs", "assemblies", "community"].includes(currentPage); <NavItem
return ( icon={<AssembliesIcon fontSize="small" />}
<Accordion label="Assemblies"
expanded={expandedNav.automation} pageKey="assemblies"
onChange={(_, e) => setExpandedNav((s) => ({ ...s, automation: e }))} />
square <NavItem
disableGutters icon={<JobsIcon fontSize="small" />}
sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }} label="Scheduled Jobs"
> pageKey="jobs"
<AccordionSummary />
expandIcon={<ExpandMoreIcon />} <NavItem
sx={{ icon={<CommunityIcon fontSize="small" />}
position: "relative", label="Community Content"
background: groupActive pageKey="community"
? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)" />
: "#2c2c2c", </Section>
minHeight: "36px",
"& .MuiAccordionSummary-content": { margin: 0 },
"&::before": {
content: '""',
position: "absolute",
left: 0,
top: 0,
bottom: 0,
width: groupActive ? 3 : 0,
bgcolor: "#58a6ff",
borderTopRightRadius: 2,
borderBottomRightRadius: 2,
transition: "width 160ms ease"
}
}}
>
<Typography sx={{ fontSize: "0.85rem", color: "#58a6ff" }}>
<b>Automation</b>
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ p: 0, bgcolor: "#232323" }}>
<NavItem icon={<AssembliesIcon fontSize="small" />} label="Assemblies" pageKey="assemblies" />
<NavItem icon={<JobsIcon fontSize="small" />} label="Scheduled Jobs" pageKey="jobs" />
<NavItem icon={<CommunityIcon fontSize="small" />} label="Community Content" pageKey="community" />
</AccordionDetails>
</Accordion>
);
})()}
{/* Filters & Groups */} {/* Filters & Groups */}
{(() => { <Section title="Filters & Groups" k="filters">
const groupActive = currentPage === "filters" || currentPage === "groups"; <NavItem
return ( icon={<FilterIcon fontSize="small" />}
<Accordion label="Filters"
expanded={expandedNav.filters} pageKey="filters"
onChange={(_, e) => setExpandedNav((s) => ({ ...s, filters: e }))} />
square <NavItem
disableGutters icon={<GroupsIcon fontSize="small" />}
sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }} label="Groups"
> pageKey="groups"
<AccordionSummary />
expandIcon={<ExpandMoreIcon />} </Section>
sx={{
position: "relative",
background: groupActive
? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)"
: "#2c2c2c",
minHeight: "36px",
"& .MuiAccordionSummary-content": { margin: 0 },
"&::before": {
content: '""',
position: "absolute",
left: 0,
top: 0,
bottom: 0,
width: groupActive ? 3 : 0,
bgcolor: "#58a6ff",
borderTopRightRadius: 2,
borderBottomRightRadius: 2,
transition: "width 160ms ease"
}
}}
>
<Typography sx={{ fontSize: "0.85rem", color: "#58a6ff" }}>
<b>Filters & Groups</b>
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ p: 0, bgcolor: "#232323" }}>
<NavItem icon={<FilterIcon fontSize="small" />} label="Filters" pageKey="filters" />
<NavItem icon={<GroupsIcon fontSize="small" />} label="Groups" pageKey="groups" />
</AccordionDetails>
</Accordion>
);
})()}
{/* Access Management */} {/* Access Management */}
{(() => { {isAdmin && (
if (!isAdmin) return null; <Section title="Access Management" k="access">
const groupActive = <NavItem
currentPage === "access_credentials" || icon={<CredentialIcon fontSize="small" />}
currentPage === "access_users" || label="Credentials"
currentPage === "access_github_token"; pageKey="access_credentials"
return ( />
<Accordion <NavItem
expanded={expandedNav.access} icon={<GitHubIcon fontSize="small" />}
onChange={(_, e) => setExpandedNav((s) => ({ ...s, access: e }))} label="GitHub API Token"
square pageKey="access_github_token"
disableGutters />
sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }} <NavItem
> icon={<UserIcon fontSize="small" />}
<AccordionSummary label="Users"
expandIcon={<ExpandMoreIcon />} pageKey="access_users"
sx={{ />
position: "relative", </Section>
background: groupActive )}
? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)"
: "#2c2c2c",
minHeight: "36px",
"& .MuiAccordionSummary-content": { margin: 0 },
"&::before": {
content: '""',
position: "absolute",
left: 0,
top: 0,
bottom: 0,
width: groupActive ? 3 : 0,
bgcolor: "#58a6ff",
borderTopRightRadius: 2,
borderBottomRightRadius: 2,
transition: "width 160ms ease"
}
}}
>
<Typography sx={{ fontSize: "0.85rem", color: "#58a6ff" }}>
<b>Access Management</b>
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ p: 0, bgcolor: "#232323" }}>
<NavItem icon={<CredentialIcon fontSize="small" />} label="Credentials" pageKey="access_credentials" />
<NavItem icon={<GitHubIcon fontSize="small" />} label="GitHub API Token" pageKey="access_github_token" />
<NavItem icon={<UserIcon fontSize="small" />} label="Users" pageKey="access_users" />
</AccordionDetails>
</Accordion>
);
})()}
{/* Admin */} {/* Admin Settings */}
{(() => { {isAdmin && (
if (!isAdmin) return null; <Section title="Admin Settings" k="admin">
const groupActive = <NavItem
currentPage === "server_info" || icon={<ServerInfoIcon fontSize="small" />}
currentPage === "admin_enrollment_codes" || label="Server Info"
currentPage === "admin_device_approvals"; pageKey="server_info"
return ( />
<Accordion </Section>
expanded={expandedNav.admin} )}
onChange={(_, e) => setExpandedNav((s) => ({ ...s, admin: e }))}
square
disableGutters
sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
sx={{
position: "relative",
background: groupActive
? "linear-gradient(90deg, rgba(88,166,255,0.08) 0%, rgba(88,166,255,0.00) 100%)"
: "#2c2c2c",
minHeight: "36px",
"& .MuiAccordionSummary-content": { margin: 0 },
"&::before": {
content: '""',
position: "absolute",
left: 0,
top: 0,
bottom: 0,
width: groupActive ? 3 : 0,
bgcolor: "#58a6ff",
borderTopRightRadius: 2,
borderBottomRightRadius: 2,
transition: "width 160ms ease"
}
}}
>
<Typography sx={{ fontSize: "0.85rem", color: "#58a6ff" }}>
<b>Admin Settings</b>
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ p: 0, bgcolor: "#232323" }}>
<NavItem icon={<ServerInfoIcon fontSize="small" />} label="Server Info" pageKey="server_info" />
</AccordionDetails>
</Accordion>
);
})()}
</Box> </Box>
<Divider sx={{ borderColor: COLORS.line }} />
</Box> </Box>
); );
} }