diff --git a/src/app/(frontend)/styles.css b/src/app/(frontend)/styles.css index 1a2b794..babd4b9 100644 --- a/src/app/(frontend)/styles.css +++ b/src/app/(frontend)/styles.css @@ -245,4 +245,44 @@ body { @apply bg-background text-foreground; } +} + +/* +---break--- +*/ + +/* Top-down ORBAT-style tree connectors */ +.org-tree-child { + position: relative; + padding-top: 1rem; +} + +.org-tree-child::before { + content: ""; + position: absolute; + top: 0; + left: 50%; + height: 1rem; + border-left: 2px solid var(--color-border); +} + +.org-tree-child::after { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + border-top: 2px solid var(--color-border); +} + +.org-tree-child:first-child::after { + left: 50%; +} + +.org-tree-child:last-child::after { + right: 50%; +} + +.org-tree-child:only-child::after { + display: none; } \ No newline at end of file diff --git a/src/components/frontend/blocks/AppSidebar.tsx b/src/components/frontend/blocks/AppSidebar.tsx index 7040e67..e745665 100644 --- a/src/components/frontend/blocks/AppSidebar.tsx +++ b/src/components/frontend/blocks/AppSidebar.tsx @@ -1,7 +1,7 @@ "use client"; import * as React from "react"; -import { Bolt, Send, Settings2, Shield, Users, Warehouse, Crosshair } from "lucide-react"; +import { Bolt, Send, Shield, Users, Warehouse, Crosshair } from "lucide-react"; import { NavCore } from "@/components/frontend/blocks/NavCore"; import { NavSecondary } from "@/components/frontend/blocks/NavSecondary"; @@ -32,29 +32,6 @@ const data = { url: "/roster", icon: Users, }, - { - title: "Settings", - url: "#", - icon: Settings2, - items: [ - { - title: "General", - url: "#", - }, - { - title: "Team", - url: "#", - }, - { - title: "Billing", - url: "#", - }, - { - title: "Limits", - url: "#", - }, - ], - }, ], navIntelligence: [ { @@ -95,10 +72,6 @@ const data = { title: "Vehicles", url: "/logistics/vehicles", }, - { - title: "Market", - url: "#", - }, { title: "Shipments", url: "/logistics/shipments", @@ -138,7 +111,7 @@ export function AppSidebar({ - + {/*
diff --git a/src/components/frontend/blocks/NavCore.tsx b/src/components/frontend/blocks/NavCore.tsx index 40239db..d8e6a1c 100644 --- a/src/components/frontend/blocks/NavCore.tsx +++ b/src/components/frontend/blocks/NavCore.tsx @@ -12,7 +12,6 @@ import { SidebarGroup, SidebarGroupLabel, SidebarMenu, - SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, @@ -41,42 +40,52 @@ export function NavCore({ {label} - {items.map((item) => ( - - - - - - {item.title} - - - {item.items?.length ? ( - <> - - - - Toggle - - - - - {item.items?.map((subItem) => ( - - - - {subItem.icon ? : null} - {subItem.title} - - - - ))} - - - - ) : null} - - - ))} + {items.map((item) => { + const hasChildren = (item.items?.length ?? 0) > 0; + return ( + + + {hasChildren ? ( + <> + + + + {item.title} + + + + + + {item.items?.map((subItem) => ( + + + + {subItem.icon ? : null} + {subItem.title} + + + + ))} + + + + ) : ( + + + + {item.title} + + + )} + + + ); + })} ); diff --git a/src/components/frontend/roster/RosterView.tsx b/src/components/frontend/roster/RosterView.tsx index 6a332e5..d1d7425 100644 --- a/src/components/frontend/roster/RosterView.tsx +++ b/src/components/frontend/roster/RosterView.tsx @@ -126,7 +126,7 @@ function collectAllMembers(node: TreeNode): RosterMember[] { return result; } -// ─── Org Chart ────────────────────────────────────────────────────── +// ─── Org Chart (top-down ORBAT tree) ──────────────────────────────── function OrgChartView({ assignments, @@ -144,10 +144,12 @@ function OrgChartView({ return (
{roots.length > 0 ? ( -
- {roots.map((root) => ( - - ))} +
+
+ {roots.map((root) => ( + + ))} +
) : (

No assignments configured.

@@ -171,68 +173,96 @@ function OrgChartView({ function OrgChartNode({ node, depth }: { node: TreeNode; depth: number }) { const allMembers = collectAllMembers(node); - const hasChildren = node.children.length > 0; + const leader = node.leaderId + ? allMembers.find((m) => m.user.id === node.leaderId) + : null; + const directMembers = node.members.filter((m) => m.user.id !== node.leaderId); + + return ( +
+ + + {node.children.length > 0 && ( + <> +
+
+ {node.children.map((child) => ( +
+ +
+ ))} +
+ + )} +
+ ); +} + +function UnitBox({ + node, + depth, + leader, + directMembers, + memberCount, +}: { + node: TreeNode; + depth: number; + leader: RosterMember | null | undefined; + directMembers: RosterMember[]; + memberCount: number; +}) { const depthBorders = [ "border-l-blue-600", "border-l-amber-600", "border-l-emerald-600", ]; const borderClass = depthBorders[depth] ?? "border-l-zinc-600"; - const leader = node.leaderId - ? allMembers.find((m) => m.user.id === node.leaderId) - : null; return ( -
0 && "ml-5 border-l-2 border-border pl-4")}> -
-
- - {node.name} - - {TYPE_LABELS[node.type] ?? node.type} +
+
+ + {node.name} + + {TYPE_LABELS[node.type] ?? node.type} + + {node.callsign && ( + + [{node.callsign}] - {node.callsign && ( - - [{node.callsign}] - - )} - - {allMembers.length} member{allMembers.length !== 1 ? "s" : ""} - -
- - {leader && ( -
- - Commanding - - -
- )} - - {node.members.filter((m) => m.user.id !== node.leaderId).length > 0 && ( -
- {node.members - .filter((m) => m.user.id !== node.leaderId) - .map((m) => ( - - ))} -
- )} - - {hasChildren && ( -
- {node.children.map((child) => ( - - ))} -
)} + + {memberCount} +
+ + {leader && ( +
+ + Commanding + + +
+ )} + + {directMembers.length > 0 && ( +
+ {directMembers.map((m) => ( + + ))} +
+ )}
); }