"use client" import * as React from "react" import { ChevronRight, Clock3, FileText, Folder, Play, Plug, Rocket, Users } from "lucide-react" import { NavUser } from "@/components/nav-user" import { TeamSwitcher } from "@/components/team-switcher" import { NavProjects } from "@/components/nav-projects" import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarRail, SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem, useSidebar, } from "@/components/ui/sidebar" import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible" // This is sample data. const data = { user: { name: "user", email: "user@example.com", avatar: "/avatars/user.jpg", }, teams: [ { name: "RowboatX", logo: Users, plan: "Workspace", }, ], chatHistory: [ { name: "Building a React Dashboard", url: "#" }, { name: "API Integration Best Practices", url: "#" }, { name: "TypeScript Migration Guide", url: "#" }, { name: "Database Optimization Tips", url: "#" }, { name: "Docker Container Setup", url: "#" }, { name: "GraphQL vs REST API", url: "#" }, ], navMain: [ { title: "Scheduled", url: "#", icon: Clock3, isActive: false, items: [ { title: "View Schedule", url: "#", }, { title: "Create Schedule", url: "#", }, { title: "Recurring Tasks", url: "#", }, ], }, { title: "Applets", url: "#", icon: Rocket, items: [ { title: "Browse Applets", url: "#", }, { title: "Create Applet", url: "#", }, { title: "My Applets", url: "#", }, ], }, ], } type RowboatSummary = { agents: string[] config: string[] runs: string[] } type ResourceKind = "agent" | "config" | "run" type SidebarSelect = (item: { kind: ResourceKind; name: string }) => void type AppSidebarProps = React.ComponentProps & { onSelectResource?: SidebarSelect } export function AppSidebar({ onSelectResource, ...props }: AppSidebarProps) { const { state: sidebarState } = useSidebar() const [summary, setSummary] = React.useState({ agents: [], config: [], runs: [], }) const [loading, setLoading] = React.useState(true) React.useEffect(() => { const load = async () => { try { const res = await fetch("/api/rowboat/summary") if (!res.ok) return const data = await res.json() setSummary({ agents: data.agents || [], config: data.config || [], runs: data.runs || [], }) } catch (error) { console.error("Failed to load rowboat summary", error) } finally { setLoading(false) } } load() }, []) // Limit runs shown and provide "View more" affordance similar to chat history. const runsLimit = 8 const visibleRuns = summary.runs.slice(0, runsLimit) const hasMoreRuns = summary.runs.length > runsLimit const handleSelect = (kind: ResourceKind, name: string) => { onSelectResource?.({ kind, name }) } const navInitial = React.useMemo( () => data.navMain.reduce>((acc, item) => { acc[item.title] = false return acc }, {}), [] ) const [openGroups, setOpenGroups] = React.useState>({ agents: false, config: false, runs: false, ...navInitial, }) const isCollapsed = sidebarState === "collapsed" React.useEffect(() => { if (isCollapsed) { setOpenGroups((prev) => { const closed: Record = {} for (const key of Object.keys(prev)) closed[key] = false return closed }) } }, [isCollapsed]) const handleOpenChange = (key: string, next: boolean) => { if (isCollapsed) return setOpenGroups((prev) => ({ ...prev, [key]: next })) } return ( Platform handleOpenChange("agents", open)} > Agents {loading ? (
Loading…
) : summary.agents.length === 0 ? (
No agents found
) : ( summary.agents.map((name) => ( handleSelect("agent", name)} > {name} )) )}
handleOpenChange("config", open)} > Config {loading ? (
Loading…
) : summary.config.length === 0 ? (
No config files
) : ( summary.config.map((name) => ( handleSelect("config", name)} > {name} )) )}
handleOpenChange("runs", open)} > Runs {loading ? (
Loading…
) : summary.runs.length === 0 ? (
No runs found
) : ( <> {visibleRuns.map((name) => ( handleSelect("run", name)} > {name} ))} {hasMoreRuns && ( View more… )} )}
{data.navMain.map((item) => ( handleOpenChange(item.title, open)} > {item.title === "Scheduled" ? ( ) : item.title === "Applets" ? ( ) : ( )} {item.title} {item.items?.map((sub) => ( {sub.title} ))} ))}
) }