Phase 2 UI Adjustments/Edits by bonamin

This commit is contained in:
2026-02-17 09:56:07 +02:00
parent 2b48426fe5
commit 59c5049305
16 changed files with 1588 additions and 609 deletions

View File

@@ -1,16 +1,32 @@
import { NavLink } from "react-router-dom";
import { useState } from "react";
import { NavLink, useLocation } from "react-router-dom";
import { useAuth } from "../auth/AuthContext";
const navItems = [
{ to: "/", label: "Dashboard", roles: null },
{ to: "/melodies", label: "Melodies", roles: ["superadmin", "melody_editor", "viewer"] },
{
label: "Melodies",
roles: ["superadmin", "melody_editor", "viewer"],
children: [
{ to: "/melodies", label: "Editor" },
{ to: "/melodies/settings", label: "Settings" },
],
},
{ to: "/devices", label: "Devices", roles: ["superadmin", "device_manager", "viewer"] },
{ to: "/users", label: "Users", roles: ["superadmin", "user_manager", "viewer"] },
{ to: "/mqtt", label: "MQTT", roles: ["superadmin", "device_manager", "viewer"] },
];
const linkClass = (isActive) =>
`block px-3 py-2 rounded-md text-sm transition-colors ${
isActive
? "bg-gray-700 text-white"
: "text-gray-300 hover:bg-gray-800 hover:text-white"
}`;
export default function Sidebar() {
const { hasRole } = useAuth();
const location = useLocation();
const visibleItems = navItems.filter(
(item) => item.roles === null || hasRole(...item.roles)
@@ -20,23 +36,82 @@ export default function Sidebar() {
<aside className="w-56 bg-gray-900 text-white min-h-screen p-4">
<div className="text-xl font-bold mb-8 px-2">BellSystems</div>
<nav className="space-y-1">
{visibleItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
end={item.to === "/"}
className={({ isActive }) =>
`block px-3 py-2 rounded-md text-sm transition-colors ${
isActive
? "bg-gray-700 text-white"
: "text-gray-300 hover:bg-gray-800 hover:text-white"
}`
}
>
{item.label}
</NavLink>
))}
{visibleItems.map((item) =>
item.children ? (
<CollapsibleGroup
key={item.label}
label={item.label}
children={item.children}
currentPath={location.pathname}
/>
) : (
<NavLink
key={item.to}
to={item.to}
end={item.to === "/"}
className={({ isActive }) => linkClass(isActive)}
>
{item.label}
</NavLink>
)
)}
</nav>
</aside>
);
}
function CollapsibleGroup({ label, children, currentPath }) {
const isChildActive = children.some(
(child) =>
currentPath === child.to ||
(child.to !== "/" && currentPath.startsWith(child.to + "/"))
);
const [open, setOpen] = useState(isChildActive);
// Auto-expand when a child becomes active
const shouldBeOpen = open || isChildActive;
return (
<div>
<button
type="button"
onClick={() => setOpen(!shouldBeOpen)}
className={`w-full flex items-center justify-between px-3 py-2 rounded-md text-sm transition-colors ${
isChildActive
? "text-white"
: "text-gray-300 hover:bg-gray-800 hover:text-white"
}`}
>
<span>{label}</span>
<svg
className={`w-4 h-4 transition-transform ${shouldBeOpen ? "rotate-90" : ""}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</button>
{shouldBeOpen && (
<div className="ml-3 mt-1 space-y-1">
{children.map((child) => (
<NavLink
key={child.to}
to={child.to}
end
className={({ isActive }) =>
`block pl-4 pr-3 py-1.5 rounded-md text-sm transition-colors ${
isActive
? "bg-gray-700 text-white"
: "text-gray-400 hover:bg-gray-800 hover:text-white"
}`
}
>
{child.label}
</NavLink>
))}
</div>
)}
</div>
);
}