import { IconChartHistogram } from "@tabler/icons-react"; import { GripVerticalIcon } from "lucide-react"; import ReactGridLayout from "react-grid-layout"; import { AIMetricsIcon } from "~/assets/icons/AIMetricsIcon"; import { ChartArrowIcon } from "~/assets/icons/ChartArrowIcon"; import { type MatchedOrganization, useCustomDashboards } from "~/hooks/useOrganizations"; import { type UserWithDashboardPreferences } from "~/models/user.server"; import { type RenderIcon } from "~/components/primitives/Icon"; import { v3BuiltInDashboardPath, v3CustomDashboardPath } from "~/utils/pathBuilder"; import { type SideMenuEnvironment, type SideMenuProject } from "./SideMenu"; import { SideMenuItem } from "./SideMenuItem"; import { TreeConnectorBranch, TreeConnectorEnd } from "./TreeConnectors"; import { useReorderableList } from "./useReorderableList"; type SideMenuUser = Pick & { isImpersonating: boolean; }; /** * Unified item for the reorderable dashboards-section list. Combines the two * built-in dashboards (Runs, Agents) and any user-created custom dashboards so * they can be reordered together under the Dashboards parent. */ type DashboardChild = | { key: string; kind: "builtin"; label: string; path: string; collapsedIcon: RenderIcon; activeColor: string; } | { key: string; kind: "custom"; label: string; path: string; }; export function DashboardList({ organization, project, environment, isCollapsed, user, }: { organization: MatchedOrganization; project: SideMenuProject; environment: SideMenuEnvironment; isCollapsed: boolean; user: SideMenuUser; }) { const customDashboards = useCustomDashboards(); const items: DashboardChild[] = [ { key: "builtin:overview", kind: "builtin", label: "Run metrics", path: v3BuiltInDashboardPath(organization, project, environment, "overview"), collapsedIcon: ChartArrowIcon, activeColor: "text-runs", }, { key: "builtin:llm", kind: "builtin", label: "AI metrics", path: v3BuiltInDashboardPath(organization, project, environment, "llm"), collapsedIcon: AIMetricsIcon, activeColor: "text-aiMetrics", }, ...customDashboards.map( (d): DashboardChild => ({ key: `custom:${d.friendlyId}`, kind: "custom", label: d.title, path: v3CustomDashboardPath(organization, project, environment, d), }) ), ]; const initialOrder = user.dashboardPreferences.sideMenu?.organizations?.[organization.id]?.orderedItems?.[ "dashboardChildren" ]; const { orderedItems, layout, containerRef, gridWidth, canReorder, handleDrag, handleDragStop, getIsLast, } = useReorderableList({ organizationId: organization.id, listId: "dashboardChildren", items, itemKey: (item) => item.key, initialOrder, isImpersonating: user.isImpersonating, }); return (
{canReorder ? ( {orderedItems.map((item, index) => { const isLast = getIsLast(item.key, index); return (
); })}
) : ( orderedItems.map((item, index) => ( )) )}
); } function DashboardChildMenuItem({ item, isCollapsed, isLast, showDragHandle = false, }: { item: DashboardChild; isCollapsed: boolean; isLast: boolean; showDragHandle?: boolean; }) { const collapsedIcon: RenderIcon = item.kind === "builtin" ? item.collapsedIcon : IconChartHistogram; const expandedIcon: RenderIcon = isLast ? TreeConnectorEnd : TreeConnectorBranch; const activeIconColor = item.kind === "builtin" ? isCollapsed ? item.activeColor : undefined : isCollapsed ? "text-text-bright" : undefined; const inactiveIconColor = isCollapsed ? "text-text-dimmed" : "text-tertiary"; return ( ) : undefined } /> ); }