chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
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<UserWithDashboardPreferences, "dashboardPreferences"> & {
|
||||
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 (
|
||||
<div ref={containerRef}>
|
||||
{canReorder ? (
|
||||
<ReactGridLayout
|
||||
layout={layout}
|
||||
width={gridWidth}
|
||||
gridConfig={{
|
||||
cols: 1,
|
||||
rowHeight: 32,
|
||||
margin: [0, 0] as const,
|
||||
containerPadding: [0, 0] as const,
|
||||
}}
|
||||
resizeConfig={{ enabled: false }}
|
||||
dragConfig={{ enabled: !isCollapsed, handle: ".sidebar-drag-handle" }}
|
||||
onDrag={handleDrag}
|
||||
onDragStop={handleDragStop}
|
||||
className="sidebar-reorder-grid"
|
||||
autoSize
|
||||
>
|
||||
{orderedItems.map((item, index) => {
|
||||
const isLast = getIsLast(item.key, index);
|
||||
return (
|
||||
<div key={item.key}>
|
||||
<DashboardChildMenuItem
|
||||
item={item}
|
||||
isCollapsed={isCollapsed}
|
||||
isLast={isLast}
|
||||
showDragHandle
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ReactGridLayout>
|
||||
) : (
|
||||
orderedItems.map((item, index) => (
|
||||
<DashboardChildMenuItem
|
||||
key={item.key}
|
||||
item={item}
|
||||
isCollapsed={isCollapsed}
|
||||
isLast={index === orderedItems.length - 1}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<SideMenuItem
|
||||
name={item.label}
|
||||
icon={isCollapsed ? collapsedIcon : expandedIcon}
|
||||
activeIconColor={activeIconColor}
|
||||
inactiveIconColor={inactiveIconColor}
|
||||
to={item.path}
|
||||
isCollapsed={isCollapsed}
|
||||
disableIconHover
|
||||
action={
|
||||
showDragHandle ? (
|
||||
<div className="sidebar-drag-handle flex h-full w-full cursor-grab items-center justify-center rounded text-text-dimmed opacity-0 group-hover/menuitem:opacity-100 hover:text-text-bright active:cursor-grabbing">
|
||||
<GripVerticalIcon className="size-3.5" />
|
||||
</div>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user