mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 20:53:06 +00:00
changed styling
This commit is contained in:
@@ -4,11 +4,12 @@ import { useMemo } from "react";
|
||||
import { usePageSections } from "../../hooks/usePageSections";
|
||||
import { PageSectionItem } from "../../contexts/SidebarContext";
|
||||
import {
|
||||
GearIcon, FolderIcon, ArrowPathIcon,
|
||||
NoSymbolIcon, KeyIcon,
|
||||
DocumentTextIcon, SparklesIcon, ArrowDownTrayIcon,
|
||||
GearIcon,
|
||||
NoSymbolIcon,
|
||||
DocumentTextIcon,
|
||||
ChartBarIcon,
|
||||
BuildingOfficeIcon,
|
||||
ShieldCheckIcon,
|
||||
} from "../../lib/sidebarIcons";
|
||||
|
||||
// Inline icon (same as MetricsSidebar had)
|
||||
@@ -46,33 +47,33 @@ function GlobeIcon({ className = "w-5 h-5" }: { className?: string }) {
|
||||
export function AdminPageSections() {
|
||||
const items: PageSectionItem[] = useMemo(() => [
|
||||
// Tenants
|
||||
{ id: "management", label: "Tenant Management", icon: <BuildingOfficeIcon />, href: "/admin/tenants/management", group: "Tenants" },
|
||||
{ id: "config-report", label: "Config Report", icon: <DocumentTextIcon />, href: "/admin/tenants/config-report", group: "Tenants" },
|
||||
{ id: "management", label: "Tenant Management", href: "/admin/tenants/management", group: "Tenants", groupIcon: <BuildingOfficeIcon /> },
|
||||
{ id: "config-report", label: "Config Report", href: "/admin/tenants/config-report", group: "Tenants" },
|
||||
|
||||
// Metrics
|
||||
{ id: "agent-metrics", label: "Agent Metrics", icon: <ChartBarIcon />, href: "/admin/metrics/agent-metrics", group: "Metrics" },
|
||||
{ id: "usage", label: "Platform Usage", icon: <TrendingUpIcon />, href: "/admin/metrics/usage", group: "Metrics" },
|
||||
{ id: "agent-metrics", label: "Agent Metrics", href: "/admin/metrics/agent-metrics", group: "Metrics", groupIcon: <ChartBarIcon /> },
|
||||
{ id: "usage", label: "Platform Usage", href: "/admin/metrics/usage", group: "Metrics" },
|
||||
|
||||
// Reports
|
||||
{ id: "session-reports", label: "Session Reports", icon: <DocumentTextIcon />, href: "/admin/reports/session-reports", group: "Reports" },
|
||||
{ id: "user-feedback", label: "User Feedback", icon: <SparklesIcon />, href: "/admin/reports/user-feedback", group: "Reports" },
|
||||
{ id: "session-export", label: "Session Export", icon: <ArrowDownTrayIcon />, href: "/admin/reports/session-export", group: "Reports" },
|
||||
{ id: "session-reports", label: "Session Reports", href: "/admin/reports/session-reports", group: "Reports", groupIcon: <DocumentTextIcon /> },
|
||||
{ id: "user-feedback", label: "User Feedback", href: "/admin/reports/user-feedback", group: "Reports" },
|
||||
{ id: "session-export", label: "Session Export", href: "/admin/reports/session-export", group: "Reports" },
|
||||
|
||||
// Security
|
||||
{ id: "device-block", label: "Device Block", icon: <NoSymbolIcon />, href: "/admin/security/device-block", group: "Security" },
|
||||
{ id: "version-block", label: "Version Block", icon: <NoSymbolIcon />, href: "/admin/security/version-block", group: "Security" },
|
||||
{ id: "vulnerability-data", label: "Vulnerability Data", icon: <KeyIcon />, href: "/admin/security/vulnerability-data", group: "Security" },
|
||||
{ id: "device-block", label: "Device Block", href: "/admin/security/device-block", group: "Security", groupIcon: <ShieldCheckIcon /> },
|
||||
{ id: "version-block", label: "Version Block", href: "/admin/security/version-block", group: "Security" },
|
||||
{ id: "vulnerability-data", label: "Vulnerability Data", href: "/admin/security/vulnerability-data", group: "Security" },
|
||||
|
||||
// Settings
|
||||
{ id: "global", label: "Global Settings", icon: <GearIcon />, href: "/admin/settings/global", group: "Settings" },
|
||||
{ id: "diagnostics-log-paths", label: "Diagnostics Log Paths", icon: <FolderIcon />, href: "/admin/settings/diagnostics-log-paths", group: "Settings" },
|
||||
{ id: "config-reseed", label: "Config Reseed", icon: <ArrowPathIcon />, href: "/admin/settings/config-reseed", group: "Settings" },
|
||||
{ id: "global", label: "Global Settings", href: "/admin/settings/global", group: "Settings", groupIcon: <GearIcon /> },
|
||||
{ id: "diagnostics-log-paths", label: "Diagnostics Log Paths", href: "/admin/settings/diagnostics-log-paths", group: "Settings" },
|
||||
{ id: "config-reseed", label: "Config Reseed", href: "/admin/settings/config-reseed", group: "Settings" },
|
||||
|
||||
// Ops (single page, no sub-items — still in a group for consistency)
|
||||
{ id: "ops", label: "Maintenance", icon: <WrenchIcon />, href: "/admin/ops", group: "Ops" },
|
||||
// Ops (single page)
|
||||
{ id: "ops", label: "Maintenance", href: "/admin/ops", group: "Ops", groupIcon: <WrenchIcon /> },
|
||||
|
||||
// Software (single page)
|
||||
{ id: "software", label: "Software Mapping", icon: <GlobeIcon />, href: "/admin/software", group: "Software" },
|
||||
{ id: "software", label: "Software Mapping", href: "/admin/software", group: "Software", groupIcon: <GlobeIcon /> },
|
||||
], []);
|
||||
|
||||
usePageSections(items, "Galactic Admin", "route");
|
||||
|
||||
@@ -5,68 +5,66 @@ import { usePageSections } from "../../hooks/usePageSections";
|
||||
import { PageSectionItem } from "../../contexts/SidebarContext";
|
||||
import { useTenantConfig } from "./TenantConfigContext";
|
||||
import {
|
||||
ShieldCheckIcon, CpuChipIcon, GearIcon, MagnifyingGlassIcon,
|
||||
LockClosedIcon, UsersIcon, KeyIcon, BellIcon, CloudArrowUpIcon,
|
||||
CircleStackIcon, ArrowRightOnRectangleIcon,
|
||||
ShieldCheckIcon, GearIcon, UsersIcon, BellIcon, CircleStackIcon,
|
||||
} from "../../lib/sidebarIcons";
|
||||
|
||||
/**
|
||||
* Registers all tenant settings sections in the global sidebar
|
||||
* with group information for expandable rendering.
|
||||
* Placed inside TenantConfigProvider so it can read config for conditional sections.
|
||||
* with group information for expandable rendering (GitHub-style).
|
||||
*/
|
||||
export function SettingsPageSections() {
|
||||
const { config, user } = useTenantConfig();
|
||||
|
||||
const items: PageSectionItem[] = useMemo(() => {
|
||||
const sections: PageSectionItem[] = [];
|
||||
const isAdmin = user?.isTenantAdmin || user?.isGalacticAdmin;
|
||||
|
||||
// 1. Validation
|
||||
if (user?.isTenantAdmin || user?.isGalacticAdmin) {
|
||||
if (isAdmin) {
|
||||
sections.push(
|
||||
{ id: "autopilot", label: "Autopilot Validation", icon: <ShieldCheckIcon />, href: "/settings/validation/autopilot", group: "Validation" },
|
||||
{ id: "hardware-whitelist", label: "Hardware Whitelist", icon: <CpuChipIcon />, href: "/settings/validation/hardware-whitelist", group: "Validation" },
|
||||
{ id: "autopilot", label: "Autopilot Validation", href: "/settings/validation/autopilot", group: "Validation", groupIcon: <ShieldCheckIcon /> },
|
||||
{ id: "hardware-whitelist", label: "Hardware Whitelist", href: "/settings/validation/hardware-whitelist", group: "Validation" },
|
||||
);
|
||||
}
|
||||
|
||||
// 2. Agent
|
||||
if (user?.isTenantAdmin || user?.isGalacticAdmin) {
|
||||
if (isAdmin) {
|
||||
sections.push(
|
||||
{ id: "settings", label: "Agent Settings", icon: <GearIcon />, href: "/settings/agent/settings", group: "Agent" },
|
||||
{ id: "analyzers", label: "Agent Analyzers", icon: <MagnifyingGlassIcon />, href: "/settings/agent/analyzers", group: "Agent" },
|
||||
{ id: "settings", label: "Agent Settings", href: "/settings/agent/settings", group: "Agent", groupIcon: <GearIcon /> },
|
||||
{ id: "analyzers", label: "Agent Analyzers", href: "/settings/agent/analyzers", group: "Agent" },
|
||||
);
|
||||
if (config?.unrestrictedModeEnabled) {
|
||||
sections.push(
|
||||
{ id: "unrestricted-mode", label: "Unrestricted Mode", icon: <LockClosedIcon />, href: "/settings/agent/unrestricted-mode", group: "Agent" },
|
||||
{ id: "unrestricted-mode", label: "Unrestricted Mode", href: "/settings/agent/unrestricted-mode", group: "Agent" },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Access
|
||||
if (user?.isTenantAdmin || user?.isGalacticAdmin) {
|
||||
if (isAdmin) {
|
||||
sections.push(
|
||||
{ id: "admin-management", label: "Admin Management", icon: <UsersIcon />, href: "/settings/access/admin-management", group: "Access" },
|
||||
{ id: "admin-management", label: "Admin Management", href: "/settings/access/admin-management", group: "Access", groupIcon: <UsersIcon /> },
|
||||
);
|
||||
}
|
||||
if (config?.bootstrapTokenEnabled && (user?.isTenantAdmin || user?.isGalacticAdmin || user?.canManageBootstrapTokens)) {
|
||||
if (config?.bootstrapTokenEnabled && (isAdmin || user?.canManageBootstrapTokens)) {
|
||||
sections.push(
|
||||
{ id: "bootstrap-sessions", label: "Bootstrap Sessions", icon: <KeyIcon />, href: "/settings/access/bootstrap-sessions", group: "Access" },
|
||||
{ id: "bootstrap-sessions", label: "Bootstrap Sessions", href: "/settings/access/bootstrap-sessions", group: "Access", ...(!isAdmin ? { groupIcon: <UsersIcon /> } : {}) },
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Integrations
|
||||
if (user?.isTenantAdmin || user?.isGalacticAdmin) {
|
||||
if (isAdmin) {
|
||||
sections.push(
|
||||
{ id: "notifications", label: "Notifications", icon: <BellIcon />, href: "/settings/integrations/notifications", group: "Integrations" },
|
||||
{ id: "diagnostics", label: "Diagnostics", icon: <CloudArrowUpIcon />, href: "/settings/integrations/diagnostics", group: "Integrations" },
|
||||
{ id: "notifications", label: "Notifications", href: "/settings/integrations/notifications", group: "Integrations", groupIcon: <BellIcon /> },
|
||||
{ id: "diagnostics", label: "Diagnostics", href: "/settings/integrations/diagnostics", group: "Integrations" },
|
||||
);
|
||||
}
|
||||
|
||||
// 5. Management
|
||||
if (user?.isTenantAdmin || user?.isGalacticAdmin) {
|
||||
if (isAdmin) {
|
||||
sections.push(
|
||||
{ id: "data", label: "Data Management", icon: <CircleStackIcon />, href: "/settings/management/data", group: "Management" },
|
||||
{ id: "offboarding", label: "Offboarding", icon: <ArrowRightOnRectangleIcon />, href: "/settings/management/offboarding", group: "Management" },
|
||||
{ id: "data", label: "Data Management", href: "/settings/management/data", group: "Management", groupIcon: <CircleStackIcon /> },
|
||||
{ id: "offboarding", label: "Offboarding", href: "/settings/management/offboarding", group: "Management" },
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -129,13 +129,13 @@ export function GlobalSidebar({ children }: { children: ReactNode }) {
|
||||
// Group items by their group name (preserving order of first appearance)
|
||||
const groupedSections = useMemo(() => {
|
||||
if (!hasGroups) return null;
|
||||
const groups: { name: string; items: PageSectionItem[] }[] = [];
|
||||
const groups: { name: string; icon?: ReactNode; items: PageSectionItem[] }[] = [];
|
||||
const seen = new Map<string, number>();
|
||||
for (const item of pageSections) {
|
||||
const g = item.group ?? "";
|
||||
if (!seen.has(g)) {
|
||||
seen.set(g, groups.length);
|
||||
groups.push({ name: g, items: [] });
|
||||
groups.push({ name: g, icon: item.groupIcon, items: [] });
|
||||
}
|
||||
groups[seen.get(g)!].items.push(item);
|
||||
}
|
||||
@@ -401,52 +401,110 @@ export function GlobalSidebar({ children }: { children: ReactNode }) {
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Grouped sections with expand/collapse */}
|
||||
{/* Grouped sections with expand/collapse (GitHub-style) */}
|
||||
{groupedSections ? (
|
||||
groupedSections.map((group) => {
|
||||
<ul className="space-y-0.5">
|
||||
{groupedSections.map((group) => {
|
||||
const isExpanded = expandedGroups.has(group.name);
|
||||
const groupHasActive = group.items.some((item) => item.id === activeSectionId);
|
||||
const firstHref = group.items[0]?.href;
|
||||
|
||||
if (collapseState === "icons" && !isMobile) {
|
||||
// In icons mode, show all items flat (no headers)
|
||||
// In icons mode: show group icon as a link to the first item
|
||||
return (
|
||||
<ul key={group.name} className="space-y-0.5">
|
||||
{group.items.map(renderSectionItem)}
|
||||
</ul>
|
||||
<li key={group.name}>
|
||||
<Link
|
||||
href={firstHref ?? "#"}
|
||||
onClick={() => setMobileDrawerOpen(false)}
|
||||
className={`flex items-center justify-center px-2 py-2 rounded-md text-sm transition-colors relative group ${
|
||||
groupHasActive
|
||||
? "bg-blue-50 text-blue-700 font-semibold dark:bg-blue-900/30 dark:text-blue-300"
|
||||
: "text-gray-600 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200"
|
||||
}`}
|
||||
title={group.name}
|
||||
>
|
||||
<span className="shrink-0 w-4.5 h-4.5">
|
||||
{group.icon ? group.icon : renderIcon(undefined, "w-4.5 h-4.5")}
|
||||
</span>
|
||||
<span className="absolute left-full ml-2 px-2 py-1 rounded bg-gray-900 text-white text-xs whitespace-nowrap opacity-0 pointer-events-none group-hover:opacity-100 transition-opacity z-50 dark:bg-gray-700">
|
||||
{group.name}
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
// Full mode: GitHub-style with icon + label + chevron
|
||||
return (
|
||||
<div key={group.name} className="mt-1">
|
||||
{/* Group header — clickable to expand/collapse */}
|
||||
<li key={group.name} className="mt-0.5">
|
||||
<button
|
||||
onClick={() => toggleGroup(group.name)}
|
||||
className={`w-full flex items-center gap-1.5 px-3 py-1 text-[11px] font-semibold uppercase tracking-wider transition-colors rounded-md ${
|
||||
className={`w-full flex items-center gap-2.5 px-3 py-1.5 rounded-md text-sm transition-colors ${
|
||||
groupHasActive
|
||||
? "text-blue-600 dark:text-blue-400"
|
||||
: "text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300"
|
||||
? "text-blue-700 font-semibold dark:text-blue-300"
|
||||
: "text-gray-600 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200"
|
||||
}`}
|
||||
>
|
||||
{/* Group icon */}
|
||||
<span className="shrink-0 w-4 h-4">
|
||||
{group.icon ? group.icon : renderIcon(undefined, "w-4 h-4")}
|
||||
</span>
|
||||
{/* Group label */}
|
||||
<span className="truncate flex-1 text-left">{group.name}</span>
|
||||
{/* Chevron */}
|
||||
<svg
|
||||
className={`w-3 h-3 transition-transform duration-150 ${isExpanded ? "rotate-90" : ""}`}
|
||||
className={`w-3.5 h-3.5 shrink-0 text-gray-400 transition-transform duration-150 ${isExpanded ? "rotate-90" : ""}`}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2.5}
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
<span>{group.name}</span>
|
||||
</button>
|
||||
{/* Group items — shown when expanded */}
|
||||
{/* Sub-items — smaller, indented, no icons (GitHub-style) */}
|
||||
{isExpanded && (
|
||||
<ul className="space-y-0.5 mt-0.5">
|
||||
{group.items.map(renderSectionItem)}
|
||||
<ul className="mt-0.5 space-y-px">
|
||||
{group.items.map((item) => {
|
||||
const active = activeSectionId === item.id;
|
||||
if (pageSectionsMode === "route" && item.href) {
|
||||
return (
|
||||
<li key={item.id}>
|
||||
<Link
|
||||
href={item.href}
|
||||
onClick={() => setMobileDrawerOpen(false)}
|
||||
className={`block pl-10 pr-3 py-1 rounded-md text-[13px] transition-colors ${
|
||||
active
|
||||
? "bg-blue-50 text-blue-700 font-medium dark:bg-blue-900/30 dark:text-blue-300"
|
||||
: "text-gray-500 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200"
|
||||
}`}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<li key={item.id}>
|
||||
<button
|
||||
onClick={() => scrollTo(item.id)}
|
||||
className={`w-full text-left block pl-10 pr-3 py-1 rounded-md text-[13px] transition-colors ${
|
||||
active
|
||||
? "bg-blue-50 text-blue-700 font-medium dark:bg-blue-900/30 dark:text-blue-300"
|
||||
: "text-gray-500 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200"
|
||||
}`}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
})
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
/* Flat (ungrouped) sections — existing behavior */
|
||||
<ul className="space-y-0.5">
|
||||
|
||||
@@ -10,6 +10,8 @@ export interface PageSectionItem {
|
||||
href?: string;
|
||||
/** Optional group name — items sharing the same group are rendered under a collapsible header */
|
||||
group?: string;
|
||||
/** Icon for the group header (only needs to be set on the first item of a group) */
|
||||
groupIcon?: ReactNode;
|
||||
}
|
||||
|
||||
interface SidebarContextValue {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user