Skip to content

Commit 7bb4c41

Browse files
samejrmatt-aitken
authored andcommitted
Adds connector lines for the custom dashboard icons
1 parent 45cc964 commit 7bb4c41

2 files changed

Lines changed: 59 additions & 31 deletions

File tree

apps/webapp/app/components/navigation/SideMenu.tsx

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
BeakerIcon,
66
BellAlertIcon,
77
ChartBarIcon,
8-
ChartBarSquareIcon,
98
ChevronRightIcon,
109
ClockIcon,
1110
Cog8ToothIcon,
@@ -26,7 +25,8 @@ import {
2625
import { DialogClose } from "@radix-ui/react-dialog";
2726
import { Form, Link, useFetcher, useNavigation } from "@remix-run/react";
2827
import { LayoutGroup, motion } from "framer-motion";
29-
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
28+
import { LineChartIcon } from "lucide-react";
29+
import { type ReactNode, useCallback, useEffect, useRef, useState } from "react";
3030
import simplur from "simplur";
3131
import { ConcurrencyIcon } from "~/assets/icons/ConcurrencyIcon";
3232
import { DropdownIcon } from "~/assets/icons/DropdownIcon";
@@ -36,30 +36,20 @@ import { LogsIcon } from "~/assets/icons/LogsIcon";
3636
import { RunsIconExtraSmall } from "~/assets/icons/RunsIcon";
3737
import { TaskIconSmall } from "~/assets/icons/TaskIcon";
3838
import { WaitpointTokenIcon } from "~/assets/icons/WaitpointTokenIcon";
39+
import { Feedback } from "~/components/Feedback";
3940
import { Avatar } from "~/components/primitives/Avatar";
40-
import { type MatchedEnvironment, useEnvironment } from "~/hooks/useEnvironment";
41+
import { type MatchedEnvironment } from "~/hooks/useEnvironment";
4142
import { useFeatureFlags } from "~/hooks/useFeatureFlags";
4243
import { useFeatures } from "~/hooks/useFeatures";
4344
import {
4445
type MatchedOrganization,
4546
useCustomDashboards,
4647
useDashboardLimits,
4748
} from "~/hooks/useOrganizations";
48-
import { type MatchedProject, useProject } from "~/hooks/useProject";
49-
import { useHasAdminAccess } from "~/hooks/useUser";
49+
import { type MatchedProject } from "~/hooks/useProject";
5050
import { useShortcutKeys } from "~/hooks/useShortcutKeys";
51-
import { ShortcutKey } from "../primitives/ShortcutKey";
51+
import { useHasAdminAccess } from "~/hooks/useUser";
5252
import { type UserWithDashboardPreferences } from "~/models/user.server";
53-
import { type SideMenuSectionId } from "./sideMenuTypes";
54-
55-
/** Get the collapsed state for a specific side menu section from user preferences */
56-
function getSectionCollapsed(
57-
sideMenu: { collapsedSections?: Record<string, boolean> } | undefined,
58-
sectionId: SideMenuSectionId
59-
): boolean {
60-
return sideMenu?.collapsedSections?.[sectionId] ?? false;
61-
}
62-
import { Feedback } from "~/components/Feedback";
6353
import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route";
6454
import { type FeedbackType } from "~/routes/resources.feedback";
6555
import { IncidentStatusPanel } from "~/routes/resources.incidents";
@@ -98,7 +88,7 @@ import {
9888
v3UsagePath,
9989
v3WaitpointTokensPath,
10090
} from "~/utils/pathBuilder";
101-
import { AlphaBadge, BetaBadge } from "../AlphaBadge";
91+
import { AlphaBadge } from "../AlphaBadge";
10292
import { AskAI } from "../AskAI";
10393
import { FreePlanUsage } from "../billing/FreePlanUsage";
10494
import { ConnectionIcon, DevPresencePanel, useDevPresence } from "../DevPresence";
@@ -118,6 +108,7 @@ import { InputGroup } from "../primitives/InputGroup";
118108
import { Label } from "../primitives/Label";
119109
import { Paragraph } from "../primitives/Paragraph";
120110
import { Popover, PopoverContent, PopoverMenuItem, PopoverTrigger } from "../primitives/Popover";
111+
import { ShortcutKey } from "../primitives/ShortcutKey";
121112
import { TextLink } from "../primitives/TextLink";
122113
import {
123114
SimpleTooltip,
@@ -133,7 +124,15 @@ import { HelpAndFeedback } from "./HelpAndFeedbackPopover";
133124
import { SideMenuHeader } from "./SideMenuHeader";
134125
import { SideMenuItem } from "./SideMenuItem";
135126
import { SideMenuSection } from "./SideMenuSection";
136-
import { BarChart2Icon, LineChartIcon } from "lucide-react";
127+
import { type SideMenuSectionId } from "./sideMenuTypes";
128+
129+
/** Get the collapsed state for a specific side menu section from user preferences */
130+
function getSectionCollapsed(
131+
sideMenu: { collapsedSections?: Record<string, boolean> } | undefined,
132+
sectionId: SideMenuSectionId
133+
): boolean {
134+
return sideMenu?.collapsedSections?.[sectionId] ?? false;
135+
}
137136

138137
type SideMenuUser = Pick<
139138
UserWithDashboardPreferences,
@@ -497,7 +496,7 @@ export function SideMenu({
497496
/>
498497
<SideMenuItem
499498
name="Metrics"
500-
icon={BarChart2Icon}
499+
icon={ChartBarIcon}
501500
activeIconColor="text-metrics"
502501
inactiveIconColor="text-metrics"
503502
to={v3BuiltInDashboardPath(organization, project, environment, "overview")}
@@ -512,17 +511,26 @@ export function SideMenu({
512511
/>
513512
}
514513
/>
515-
{customDashboards.map((dashboard) => (
516-
<SideMenuItem
517-
key={dashboard.friendlyId}
518-
name={dashboard.title}
519-
icon={LineChartIcon}
520-
activeIconColor="text-customDashboards"
521-
inactiveIconColor="text-customDashboards"
522-
to={v3CustomDashboardPath(organization, project, environment, dashboard)}
523-
isCollapsed={isCollapsed}
524-
/>
525-
))}
514+
{customDashboards.map((dashboard, index) => {
515+
const isLast = index === customDashboards.length - 1;
516+
return (
517+
<SideMenuItem
518+
key={dashboard.friendlyId}
519+
name={dashboard.title}
520+
icon={
521+
isCollapsed
522+
? LineChartIcon
523+
: isLast
524+
? TreeConnectorEnd
525+
: TreeConnectorBranch
526+
}
527+
activeIconColor="text-customDashboards"
528+
inactiveIconColor="text-customDashboards"
529+
to={v3CustomDashboardPath(organization, project, environment, dashboard)}
530+
isCollapsed={isCollapsed}
531+
/>
532+
);
533+
})}
526534
</SideMenuSection>
527535
)}
528536

@@ -1224,6 +1232,26 @@ function AnimatedChevron({
12241232
);
12251233
}
12261234

1235+
// Tree connector icons for sub-items. The SVG viewBox is 20x20 matching the size-5 icon area.
1236+
// Lines extend to y=-6 and y=26 to fill the full 32px row height (6px gap above/below the 20px icon).
1237+
function TreeConnectorBranch({ className }: { className?: string }) {
1238+
return (
1239+
<svg className={cn("overflow-visible", className)} viewBox="0 0 20 20" fill="none">
1240+
<line x1="10" y1="-6" x2="10" y2="26" stroke="currentColor" strokeWidth="1" />
1241+
<line x1="10" y1="10" x2="20" y2="10" stroke="currentColor" strokeWidth="1" />
1242+
</svg>
1243+
);
1244+
}
1245+
1246+
function TreeConnectorEnd({ className }: { className?: string }) {
1247+
return (
1248+
<svg className={cn("overflow-visible", className)} viewBox="0 0 20 20" fill="none">
1249+
<line x1="10" y1="-6" x2="10" y2="10" stroke="currentColor" strokeWidth="1" />
1250+
<line x1="10" y1="10" x2="20" y2="10" stroke="currentColor" strokeWidth="1" />
1251+
</svg>
1252+
);
1253+
}
1254+
12271255
function CollapseToggle({ isCollapsed, onToggle }: { isCollapsed: boolean; onToggle: () => void }) {
12281256
const [isHovering, setIsHovering] = useState(false);
12291257

apps/webapp/tailwind.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ const schedules = colors.yellow[500];
161161
const queues = colors.purple[500];
162162
const query = colors.blue[500];
163163
const metrics = colors.green[500];
164-
const customDashboards = charcoal[200];
164+
const customDashboards = charcoal[400];
165165
const deployments = colors.green[500];
166166
const concurrency = colors.amber[500];
167167
const limits = colors.purple[500];

0 commit comments

Comments
 (0)