From afbc4903837c93b6b50ec33b29b5978cef557dba Mon Sep 17 00:00:00 2001 From: Cyber Preacher <72062250+Cyber-preacher@users.noreply.github.com> Date: Sat, 6 Jun 2026 16:20:15 +0400 Subject: [PATCH] Finish phase 89 UI design system --- src/app/App.tsx | 11 +- src/app/AppShell.css | 19 +- src/app/AppShell.tsx | 9 +- src/app/AppSidebar.css | 312 +++++- src/app/AppSidebar.tsx | 127 +-- src/app/MainAtmosphere.css | 966 ++++++++++++++++++ src/app/MainAtmosphere.tsx | 153 +++ src/app/auth/AuthContext.tsx | 66 +- src/components/ActivityTile.tsx | 25 +- src/components/AppCard.tsx | 9 +- src/components/AvatarPlaceholder.tsx | 2 +- src/components/Chip.tsx | 21 + src/components/CmEconomyPanel.tsx | 226 ++-- src/components/CourtStatusBadge.tsx | 2 +- src/components/ExpandableCard.tsx | 19 +- src/components/GlassyCard.tsx | 25 + src/components/GlassyRecordCard.css | 211 ++++ src/components/GlassyRecordCard.tsx | 92 ++ src/components/GlassySection.css | 552 ++++++++++ src/components/GlassySection.tsx | 206 ++++ src/components/MetricTile.tsx | 2 +- src/components/Pill.tsx | 13 +- src/components/PipelineList.tsx | 10 +- src/components/ProposalPageHeader.tsx | 50 +- src/components/ProposalSections.tsx | 9 +- src/components/ProposalStageBar.tsx | 103 +- src/components/SearchBar.tsx | 7 +- src/components/StageChip.css | 269 +++++ src/components/StageChip.tsx | 35 +- src/components/StageDataTile.tsx | 2 +- src/components/StatGrid.tsx | 10 +- src/components/StatTile.tsx | 4 +- src/components/StatusPill.tsx | 2 +- src/components/Surface.tsx | 11 +- src/components/TitledSurface.tsx | 2 +- src/components/VoteButton.tsx | 9 +- .../discussions/ThreadPrimitives.tsx | 2 +- src/components/primitives/badge.tsx | 8 +- src/components/primitives/button.tsx | 1 + src/components/primitives/card.tsx | 21 +- src/components/primitives/input.tsx | 4 +- src/components/primitives/select.tsx | 2 +- src/components/primitives/tabs.tsx | 4 +- src/data/pageHints.ts | 32 +- src/lib/dtoParsers.ts | 4 +- src/lib/factionUi.ts | 10 + src/lib/formationActionsUi.ts | 112 ++ src/lib/humanNodesUi.ts | 2 +- src/lib/textPreview.ts | 18 + src/lib/theme.ts | 2 +- src/lib/usePrefersReducedMotion.ts | 2 + src/pages/General.tsx | 4 +- src/pages/MyGovernance.tsx | 69 +- src/pages/chambers/Chamber.tsx | 923 ++++++++--------- src/pages/chambers/Chambers.tsx | 26 +- .../chambers/components/ChamberVisuals.tsx | 155 +++ src/pages/cm/CMPanel.tsx | 43 +- src/pages/courts/Courtroom.tsx | 9 +- src/pages/courts/Courts.tsx | 10 +- src/pages/factions/FactionCreate.tsx | 10 +- src/pages/factions/Factions.tsx | 34 +- .../components/FactionChannelsSection.tsx | 10 +- .../factions/components/FactionEditCard.tsx | 10 +- src/pages/factions/components/FactionHero.tsx | 7 +- .../FactionInitiativeCreateCard.tsx | 10 +- .../components/FactionInitiativeListCard.tsx | 10 +- .../components/FactionInitiativesSection.tsx | 10 +- .../components/FactionMembersSection.tsx | 10 +- .../components/FactionModerationQueues.tsx | 12 +- .../components/FactionThreadCreateCard.tsx | 10 +- .../components/FactionThreadDetailCard.tsx | 10 +- .../components/FactionThreadListCard.tsx | 10 +- src/pages/feed/Feed.css | 132 +++ src/pages/feed/Feed.tsx | 11 +- src/pages/feed/components/FeedControls.tsx | 27 +- .../feed/components/FeedExpandedContent.tsx | 23 +- src/pages/feed/components/FeedListSection.tsx | 33 +- .../feed/components/FeedStatusMessages.tsx | 19 +- src/pages/formation/Formation.css | 204 ++++ src/pages/formation/Formation.tsx | 131 +-- .../components/FormationProjectCard.tsx | 255 +++++ src/pages/human-nodes/FullHistory.tsx | 10 +- src/pages/human-nodes/HumanNode.tsx | 81 +- .../HumanNodeActivityProjectsSection.tsx | 54 +- .../components/HumanNodeDelegationSection.tsx | 210 ++-- .../components/HumanNodesResultsCard.tsx | 38 +- src/pages/invision/Invision.tsx | 551 ++++------ .../components/MyGovernanceChambersCard.tsx | 67 +- .../components/MyGovernanceCmEconomyCard.tsx | 102 +- .../components/MyGovernanceDelegationCard.tsx | 340 ++++-- .../components/MyGovernanceLegitimacyCard.tsx | 144 ++- .../MyGovernanceProgressionCard.tsx | 236 +---- .../components/MyGovernanceThresholdCard.tsx | 94 +- .../MyGovernanceVetoProcessCard.tsx | 40 - .../hooks/useMyGovernancePageData.ts | 76 +- src/pages/profile/Profile.css | 96 ++ src/pages/profile/Profile.tsx | 94 +- .../ProfileActivityProjectsSection.tsx | 185 ++-- .../components/ProfileDelegationSection.tsx | 110 +- .../components/ProfileTierProgressSection.tsx | 77 +- src/pages/proposals/ProposalChamber.tsx | 4 +- src/pages/proposals/ProposalChamberVeto.tsx | 4 +- src/pages/proposals/ProposalCitizenVeto.tsx | 1 + src/pages/proposals/ProposalCreation.tsx | 2 +- src/pages/proposals/ProposalDeliberation.tsx | 3 +- src/pages/proposals/ProposalDrafts.tsx | 8 +- src/pages/proposals/ProposalFinished.tsx | 6 +- src/pages/proposals/ProposalFormation.tsx | 122 +-- src/pages/proposals/ProposalPP.tsx | 1 + src/pages/proposals/ProposalReferendum.tsx | 4 +- .../formation/ProposalFormationActions.tsx | 168 ++- src/pages/proposals/list/ProposalListCard.tsx | 35 +- .../list/ProposalListOutcomeSnapshots.tsx | 8 +- .../list/ProposalListVoteSnapshots.tsx | 4 +- .../pool/ProposalPoolAttentionStats.tsx | 3 +- .../shared/ProposalDelegationContext.tsx | 3 +- .../proposals/shared/ProposalStageStatus.tsx | 3 +- .../veto/ProposalChamberVetoStats.tsx | 3 +- .../veto/ProposalCitizenVetoStats.tsx | 3 +- src/styles/base.css | 177 +++- src/styles/global.css | 4 +- src/types/api.ts | 21 + tests/unit/cm-economy-panel.test.ts | 23 +- tests/unit/formation-actions-ui.test.ts | 105 ++ tests/unit/human-nodes-ui.test.ts | 5 +- tests/unit/phase89-design-rules.test.js | 227 ++++ tests/unit/phase89-visual-contract.test.ts | 99 ++ tests/unit/proposal-stage-navigation.test.ts | 46 + tests/unit/text-preview.test.ts | 25 + tests/utils/stat-grid.test.js | 14 +- 130 files changed, 7018 insertions(+), 2760 deletions(-) create mode 100644 src/app/MainAtmosphere.css create mode 100644 src/app/MainAtmosphere.tsx create mode 100644 src/components/Chip.tsx create mode 100644 src/components/GlassyCard.tsx create mode 100644 src/components/GlassyRecordCard.css create mode 100644 src/components/GlassyRecordCard.tsx create mode 100644 src/components/GlassySection.css create mode 100644 src/components/GlassySection.tsx create mode 100644 src/components/StageChip.css create mode 100644 src/lib/formationActionsUi.ts create mode 100644 src/lib/textPreview.ts create mode 100644 src/pages/chambers/components/ChamberVisuals.tsx create mode 100644 src/pages/feed/Feed.css create mode 100644 src/pages/formation/Formation.css create mode 100644 src/pages/formation/components/FormationProjectCard.tsx delete mode 100644 src/pages/my-governance/components/MyGovernanceVetoProcessCard.tsx create mode 100644 src/pages/profile/Profile.css create mode 100644 tests/unit/formation-actions-ui.test.ts create mode 100644 tests/unit/phase89-design-rules.test.js create mode 100644 tests/unit/phase89-visual-contract.test.ts create mode 100644 tests/unit/proposal-stage-navigation.test.ts create mode 100644 tests/unit/text-preview.test.ts diff --git a/src/app/App.tsx b/src/app/App.tsx index 4dacfef..4cd76e6 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -1,15 +1,12 @@ import { useEffect } from "react"; -import { BrowserRouter, useLocation } from "react-router"; +import { BrowserRouter } from "react-router"; import { AuthProvider } from "@/app/auth/AuthContext"; import AppRoutes from "./AppRoutes"; -const ScrollToTopOnRouteChange: React.FC = () => { - const { pathname } = useLocation(); - +const AppDocumentTitle: React.FC = () => { useEffect(() => { document.title = "Vortex Sim"; - window.scrollTo({ top: 0, left: 0, behavior: "auto" }); - }, [pathname]); + }, []); return null; }; @@ -17,7 +14,7 @@ const ScrollToTopOnRouteChange: React.FC = () => { const App: React.FC = () => { return ( - + diff --git a/src/app/AppShell.css b/src/app/AppShell.css index 391b40e..dbf9fd5 100644 --- a/src/app/AppShell.css +++ b/src/app/AppShell.css @@ -1,12 +1,18 @@ /* Shell */ .app-shell { + --app-sidebar-width: 260px; display: flex; min-height: 100vh; width: 100%; } .app-shell > .sidebar { - width: 260px; + position: sticky; + top: 0; + z-index: 3; + align-self: flex-start; + width: var(--app-sidebar-width); + height: 100vh; flex-shrink: 0; } @@ -17,6 +23,14 @@ display: flex; flex-direction: column; gap: 1.5rem; + position: relative; + z-index: 1; + isolation: isolate; +} + +.workspace > :not(.main-atmosphere) { + position: relative; + z-index: 1; } .workspace__top { @@ -116,7 +130,10 @@ } .app-shell > .sidebar { + position: relative; + top: auto; width: 100%; + height: auto; } .workspace { diff --git a/src/app/AppShell.tsx b/src/app/AppShell.tsx index 0682b5d..a2fb404 100644 --- a/src/app/AppShell.tsx +++ b/src/app/AppShell.tsx @@ -1,18 +1,23 @@ import AppSidebar from "./AppSidebar"; +import MainAtmosphere from "./MainAtmosphere"; import "./AppShell.css"; const AppShell: React.FC = ({ children }) => { return ( <> - + Skip to content
-
+
+ {children}
diff --git a/src/app/AppSidebar.css b/src/app/AppSidebar.css index 401e326..d440bbf 100644 --- a/src/app/AppSidebar.css +++ b/src/app/AppSidebar.css @@ -1,10 +1,115 @@ .sidebar { + --sidebar-ink-a: rgba(79, 106, 215, 0.32); + --sidebar-ink-b: rgba(91, 194, 181, 0.24); + --sidebar-ink-c: rgba(244, 179, 127, 0.2); + --sidebar-link-ink: rgba(111, 168, 255, 0.3); + background: var(--sidebar-bg); border-right: 1px solid var(--sidebar-border); display: flex; flex-direction: column; - gap: 2rem; - padding: 2rem 1.5rem; + gap: 1.5rem; + padding: 1.6rem 1.2rem; + position: relative; + overflow-x: hidden; + overflow-y: auto; + scrollbar-gutter: stable; + scrollbar-width: none; + isolation: isolate; +} + +.sidebar::-webkit-scrollbar { + display: none; +} + +.sidebar::before, +.sidebar::after { + content: ""; + position: absolute; + pointer-events: none; + z-index: 0; +} + +.sidebar::before { + inset: 0.55rem 0.35rem; + background: + linear-gradient( + 112deg, + transparent 0 14%, + var(--sidebar-ink-a) 18% 24%, + transparent 31% 48%, + var(--sidebar-ink-b) 54% 59%, + transparent 66% 100% + ), + linear-gradient( + 74deg, + transparent 0 45%, + var(--sidebar-ink-c) 50% 56%, + transparent 64% 100% + ); + opacity: 0.68; + mask-image: + radial-gradient(ellipse at 20% 16%, #000 0 16%, transparent 36%), + radial-gradient(ellipse at 78% 45%, #000 0 14%, transparent 34%), + radial-gradient(ellipse at 42% 83%, #000 0 12%, transparent 30%); + mask-composite: add; + transform: rotate(-7deg) skewX(-8deg); + animation: sidebar-ink-fade-primary 13s ease-in-out infinite alternate; +} + +.sidebar::after { + inset: 1.25rem -1.4rem 1rem -1rem; + border-radius: 14px; + background: + linear-gradient( + 168deg, + transparent 0 18%, + var(--sidebar-ink-a) 24% 31%, + transparent 38% 100% + ), + linear-gradient( + 28deg, + transparent 0 48%, + var(--sidebar-ink-b) 55% 60%, + transparent 67% 100% + ); + opacity: 0.45; + filter: blur(0.5px); + mask-image: + linear-gradient(90deg, transparent, #000 18% 78%, transparent), + radial-gradient(ellipse at 30% 28%, #000 0 12%, transparent 28%), + radial-gradient(ellipse at 72% 68%, #000 0 14%, transparent 30%); + mask-composite: add; + transform: rotate(-4deg) skewX(-6deg); + animation: sidebar-ink-fade-secondary 17s ease-in-out infinite alternate; +} + +:root[data-theme="sky"] .sidebar { + --sidebar-ink-a: rgba(64, 132, 255, 0.36); + --sidebar-ink-b: rgba(60, 198, 222, 0.3); + --sidebar-ink-c: rgba(151, 190, 255, 0.24); + --sidebar-link-ink: rgba(64, 132, 255, 0.34); +} + +:root[data-theme="light"] .sidebar { + --sidebar-ink-a: rgba(255, 144, 190, 0.3); + --sidebar-ink-b: rgba(245, 183, 90, 0.24); + --sidebar-ink-c: rgba(178, 153, 255, 0.22); + --sidebar-link-ink: rgba(255, 144, 190, 0.28); +} + +:root[data-theme="night"] .sidebar { + --sidebar-ink-a: rgba(92, 142, 255, 0.38); + --sidebar-ink-b: rgba(178, 102, 255, 0.32); + --sidebar-ink-c: rgba(134, 222, 255, 0.18); + --sidebar-link-ink: rgba(178, 102, 255, 0.32); +} + +:root[data-theme="fire"] .sidebar { + --sidebar-ink-a: rgba(255, 68, 0, 0.42); + --sidebar-ink-b: rgba(255, 158, 46, 0.34); + --sidebar-ink-c: rgba(255, 209, 102, 0.28); + --sidebar-link-ink: rgba(255, 104, 0, 0.4); } .sidebar__brand { @@ -12,14 +117,40 @@ align-items: center; justify-content: center; gap: 0.55rem; - font-weight: 700; - letter-spacing: -0.02em; - font-size: 1.25rem; + font-weight: 800; + font-size: 1.45rem; color: var(--sidebar-text); - padding: 0.65rem 0.9rem; + padding: 0.2rem 2.75rem 0.5rem; border-radius: 12px; width: 100%; position: relative; + z-index: 1; +} + +.sidebar__brand::after { + content: ""; + position: absolute; + left: 50%; + bottom: 0; + width: 6.3rem; + height: 0.45rem; + border-radius: 999px; + background: linear-gradient( + 90deg, + transparent, + var(--sidebar-ink-c) 18%, + var(--sidebar-ink-a) 48%, + var(--sidebar-ink-b) 74%, + transparent + ); + mask-image: + radial-gradient(ellipse at 10% 45%, transparent 0 8%, #000 16% 100%), + radial-gradient(ellipse at 92% 58%, transparent 0 10%, #000 18% 100%), + linear-gradient(90deg, transparent, #000 12% 88%, transparent); + mask-composite: intersect; + transform: translateX(-50%) scaleX(0.92) rotate(-1deg); + transform-origin: center; + animation: sidebar-brand-ink 5.4s ease-in-out infinite alternate; } .sidebar__auth { @@ -29,6 +160,8 @@ border: 1px solid var(--sidebar-border); background: var(--sidebar-active-bg); box-shadow: var(--shadow-control); + position: relative; + z-index: 1; } .sidebar__authRow { @@ -41,7 +174,6 @@ .sidebar__authKicker { font-size: 11px; - letter-spacing: 0.08em; text-transform: uppercase; color: var(--sidebar-muted); } @@ -113,29 +245,32 @@ line-height: 1.35; } -.sidebar__logo { - width: 48px; - height: 48px; - border-radius: 12px; - background: url("../assets/humanode_logo_128px_animated.gif") center/cover - no-repeat; - display: inline-flex; - align-items: center; - justify-content: center; - box-shadow: none; - order: 2; - will-change: transform; - animation: sidebar-logo-float 6s ease-in-out infinite; +.sidebar__nav { + display: flex; + flex-direction: column; + gap: 1.25rem; + position: relative; + z-index: 1; } -:root[data-theme="night"] .sidebar__logo { - display: none; +.sidebar__section { + display: flex; + flex-direction: column; + gap: 0.35rem; } -.sidebar__nav { +.sidebar__sectionTitle { + padding: 0 0.55rem; + font-size: 0.68rem; + font-weight: 700; + line-height: 1.4; + text-transform: uppercase; + color: var(--sidebar-muted); display: flex; - flex-direction: column; + align-items: center; gap: 0.5rem; + position: relative; + z-index: 1; } .sidebar__mobilePanel { @@ -147,7 +282,7 @@ border: 1px solid var(--sidebar-border); background: transparent; color: var(--sidebar-text); - border-radius: 10px; + border-radius: 8px; width: 2.25rem; height: 2.25rem; align-items: center; @@ -166,30 +301,60 @@ } .sidebar__link { - background: transparent; - border: 1px solid transparent; - color: var(--sidebar-muted); - border-radius: 12px; + background: rgba(255, 255, 255, 0.035); + border: 1px solid rgba(255, 255, 255, 0.08); + color: var(--sidebar-text); + border-radius: 8px; padding: 0.65rem 0.9rem; text-align: left; display: flex; align-items: center; gap: 0.6rem; + position: relative; + overflow: hidden; transition: border 0.2s, color 0.2s, background 0.2s; } +.sidebar__link::before { + content: ""; + position: absolute; + inset: -80% -35%; + background: linear-gradient( + 102deg, + transparent 0 21%, + var(--sidebar-link-ink) 31% 38%, + transparent 50% 100% + ); + mask-image: + radial-gradient(ellipse at 28% 48%, #000 0 22%, transparent 45%), + linear-gradient(90deg, transparent, #000 16% 78%, transparent); + mask-composite: add; + opacity: 0; + transform: translateX(-48%) rotate(-7deg) skewX(-10deg); + transition: opacity 180ms ease; +} + +.sidebar__link:hover::before, +.sidebar__link:focus-visible::before { + opacity: 1; +} + .sidebar__icon { width: 22px; height: 22px; flex-shrink: 0; opacity: 0.9; + position: relative; + z-index: 1; } .sidebar__link > span { white-space: nowrap; + position: relative; + z-index: 1; } .sidebar__link--active { @@ -197,38 +362,78 @@ border-color: var(--sidebar-primary-dim); background: var(--sidebar-active-bg); box-shadow: var(--sidebar-active-shadow); - animation: sidebar-active-pulse 2.8s ease-in-out infinite; } -.sidebar__link--nested { - padding-left: 1.5rem; -} +@keyframes sidebar-ink-fade-primary { + 0% { + opacity: 0.18; + } -@keyframes sidebar-logo-float { - 0%, - 100% { - transform: translateY(0); + 22% { + opacity: 0.7; + } + + 45% { + opacity: 0.58; + } + + 72% { + opacity: 0.14; } - 50% { - transform: translateY(-2px); + + 100% { + opacity: 0.64; } } -@keyframes sidebar-active-pulse { - 0%, +@keyframes sidebar-ink-fade-secondary { + 0% { + opacity: 0; + } + + 28% { + opacity: 0.46; + } + + 64% { + opacity: 0.12; + } + 100% { - box-shadow: var(--sidebar-active-shadow); + opacity: 0.42; } - 50% { - box-shadow: var(--sidebar-active-shadow-strong); +} + +@keyframes sidebar-brand-ink { + 0% { + opacity: 0.18; + } + + 35% { + opacity: 0.92; + } + + 70% { + opacity: 0.22; + } + + 100% { + opacity: 0.78; } } @media (prefers-reduced-motion: reduce) { - .sidebar__logo, - .sidebar__link--active { + .sidebar::before, + .sidebar::after, + .sidebar__brand::after, + .sidebar__link:hover::before, + .sidebar__link:focus-visible::before { animation: none; } + + .sidebar__link { + transition: none; + } } @media (max-width: 960px) { @@ -237,18 +442,13 @@ padding: 0.95rem 1rem; border-right: 0; border-bottom: 1px solid var(--sidebar-border); + overflow: visible; } .sidebar__brand { - justify-content: flex-start; + justify-content: center; gap: 0.5rem; - padding: 0.35rem 0; - } - - .sidebar__logo { - width: 34px; - height: 34px; - border-radius: 10px; + padding: 0.35rem 2.75rem; } .sidebar__mobileToggle { @@ -272,6 +472,10 @@ border-bottom: 1px solid var(--sidebar-border); } + .sidebar__section { + gap: 0.3rem; + } + .sidebar__link > span { white-space: normal; } diff --git a/src/app/AppSidebar.tsx b/src/app/AppSidebar.tsx index 471954e..3984e6e 100644 --- a/src/app/AppSidebar.tsx +++ b/src/app/AppSidebar.tsx @@ -1,5 +1,5 @@ -import { NavLink, useLocation } from "react-router"; -import { useEffect, useState } from "react"; +import { NavLink } from "react-router"; +import { useState } from "react"; import "./AppSidebar.css"; import clsx from "clsx"; import { @@ -13,7 +13,6 @@ import { Rocket, Scale, Settings, - SlidersHorizontal, User, Users, FileText, @@ -24,11 +23,6 @@ import { AuthSidebarPanel } from "@/app/auth/AuthContext"; const navClass = ({ isActive }: { isActive: boolean }) => clsx("sidebar__link", isActive && "sidebar__link--active"); -const nestedNavClass = ({ isActive }: { isActive: boolean }) => - clsx( - "sidebar__link sidebar__link--nested", - isActive && "sidebar__link--active", - ); type NavItem = { to: string; @@ -36,41 +30,52 @@ type NavItem = { Icon: React.ComponentType<{ className?: string }>; }; +type NavGroup = { + label: string; + items: NavItem[]; +}; + const AppSidebar: React.FC = ({ children }) => { - const [settingsOpen, setSettingsOpen] = useState(false); const [mobileNavOpen, setMobileNavOpen] = useState(false); - const location = useLocation(); - const settingsRouteActive = - location.pathname.startsWith("/app/settings") || - location.pathname.startsWith("/app/profile"); - - useEffect(() => { - setMobileNavOpen(false); - }, [location.pathname]); - useEffect(() => { - setSettingsOpen(settingsRouteActive); - }, [settingsRouteActive]); + const closeMobileNav = () => + setMobileNavOpen((open) => (open ? false : open)); - const navItems: NavItem[] = [ - { to: "/app/feed", label: "Feed", Icon: Activity }, - { to: "/app/my-governance", label: "My governance", Icon: Gavel }, - { to: "/app/proposals", label: "Proposals", Icon: FileText }, - { to: "/app/chambers", label: "Chambers", Icon: Lightbulb }, - { to: "/app/human-nodes", label: "Human nodes", Icon: Users }, - { to: "/app/formation", label: "Formation", Icon: Rocket }, - { to: "/app/factions", label: "Factions", Icon: Flag }, - { to: "/app/cm", label: "CM panel", Icon: Scale }, - { to: "/app/invision", label: "Invision", Icon: Eye }, - { to: "/app/courts", label: "Courts", Icon: Landmark }, - { to: "/app/vortexopedia", label: "Vortexopedia", Icon: BookOpen }, + const navGroups: NavGroup[] = [ + { + label: "Governance", + items: [ + { to: "/app/feed", label: "Feed", Icon: Activity }, + { to: "/app/my-governance", label: "My governance", Icon: Gavel }, + { to: "/app/proposals", label: "Proposals", Icon: FileText }, + { to: "/app/formation", label: "Formation", Icon: Rocket }, + ], + }, + { + label: "Institutions", + items: [ + { to: "/app/chambers", label: "Chambers", Icon: Lightbulb }, + { to: "/app/factions", label: "Factions", Icon: Flag }, + { to: "/app/cm", label: "CM panel", Icon: Scale }, + { to: "/app/courts", label: "Courts", Icon: Landmark }, + ], + }, + { + label: "System", + items: [ + { to: "/app/profile", label: "My profile", Icon: User }, + { to: "/app/invision", label: "Invision", Icon: Eye }, + { to: "/app/human-nodes", label: "Human nodes", Icon: Users }, + { to: "/app/vortexopedia", label: "Vortexopedia", Icon: BookOpen }, + { to: "/app/settings", label: "Settings", Icon: Settings }, + ], + }, ]; return (