Skip to content

Commit 88705f5

Browse files
Add navigation indicators
1 parent d0cb69f commit 88705f5

2 files changed

Lines changed: 100 additions & 61 deletions

File tree

packages/web/src/app/[domain]/components/navigationMenu/index.tsx

Lines changed: 10 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
import { getRepos, getReposStats } from "@/actions";
22
import { SourcebotLogo } from "@/app/components/sourcebotLogo";
33
import { auth } from "@/auth";
4-
import { Badge } from "@/components/ui/badge";
54
import { Button } from "@/components/ui/button";
6-
import { NavigationMenu as NavigationMenuBase, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, navigationMenuTriggerStyle } from "@/components/ui/navigation-menu";
5+
import { NavigationMenu as NavigationMenuBase } from "@/components/ui/navigation-menu";
76
import { Separator } from "@/components/ui/separator";
8-
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
97
import { getSubscriptionInfo } from "@/ee/features/billing/actions";
108
import { IS_BILLING_ENABLED } from "@/ee/features/billing/stripe";
119
import { env } from "@/env.mjs";
1210
import { ServiceErrorException } from "@/lib/serviceError";
13-
import { cn, getShortenedNumberDisplayString, isServiceError } from "@/lib/utils";
11+
import { isServiceError } from "@/lib/utils";
1412
import { DiscordLogoIcon, GitHubLogoIcon } from "@radix-ui/react-icons";
1513
import { RepoJobStatus, RepoJobType } from "@sourcebot/db";
16-
import { BookMarkedIcon, CircleIcon, MessageCircleIcon, SearchIcon, SettingsIcon } from "lucide-react";
1714
import Link from "next/link";
1815
import { redirect } from "next/navigation";
1916
import { OrgSelector } from "../orgSelector";
2017
import { SettingsDropdown } from "../settingsDropdown";
2118
import WhatsNewIndicator from "../whatsNewIndicator";
19+
import { NavigationItems } from "./navigationItems";
2220
import { ProgressIndicator } from "./progressIndicator";
2321
import { TrialIndicator } from "./trialIndicator";
2422

@@ -70,7 +68,7 @@ export const NavigationMenu = async ({
7068

7169
return (
7270
<div className="flex flex-col w-full h-fit bg-background">
73-
<div className="flex flex-row justify-between items-center py-1.5 px-3">
71+
<div className="flex flex-row justify-between items-center py-0.5 px-3">
7472
<div className="flex flex-row items-center">
7573
<Link
7674
href={`/${domain}`}
@@ -92,61 +90,12 @@ export const NavigationMenu = async ({
9290
)}
9391

9492
<NavigationMenuBase>
95-
<NavigationMenuList className="gap-2">
96-
<NavigationMenuItem>
97-
<NavigationMenuLink
98-
href={`/${domain}`}
99-
className={cn(navigationMenuTriggerStyle(), "gap-2")}
100-
>
101-
<SearchIcon className="w-4 h-4 mr-1" />
102-
Search
103-
</NavigationMenuLink>
104-
</NavigationMenuItem>
105-
<NavigationMenuItem>
106-
<NavigationMenuLink
107-
href={`/${domain}/chat`}
108-
className={navigationMenuTriggerStyle()}
109-
>
110-
<MessageCircleIcon className="w-4 h-4 mr-1" />
111-
Ask
112-
</NavigationMenuLink>
113-
</NavigationMenuItem>
114-
<NavigationMenuItem className="relative">
115-
<Tooltip>
116-
<TooltipTrigger asChild>
117-
<NavigationMenuLink
118-
href={`/${domain}/repos`}
119-
className={navigationMenuTriggerStyle()}
120-
>
121-
<BookMarkedIcon className="w-4 h-4 mr-1" />
122-
<span className="mr-2">Repositories</span>
123-
<Badge variant="secondary" className="px-1.5 relative">
124-
{getShortenedNumberDisplayString(numberOfRepos)}
125-
{numberOfReposWithFirstTimeIndexingJobsInProgress > 0 && (
126-
<CircleIcon className="absolute -right-0.5 -top-0.5 h-2 w-2 text-green-600" fill="currentColor" />
127-
)}
128-
</Badge>
129-
</NavigationMenuLink>
130-
</TooltipTrigger>
131-
<TooltipContent>
132-
<p>{numberOfRepos} total {numberOfRepos === 1 ? 'repository' : 'repositories'}</p>
133-
</TooltipContent>
134-
</Tooltip>
135-
</NavigationMenuItem>
136-
{isAuthenticated && (
137-
<>
138-
<NavigationMenuItem>
139-
<NavigationMenuLink
140-
href={`/${domain}/settings`}
141-
className={navigationMenuTriggerStyle()}
142-
>
143-
<SettingsIcon className="w-4 h-4 mr-1" />
144-
Settings
145-
</NavigationMenuLink>
146-
</NavigationMenuItem>
147-
</>
148-
)}
149-
</NavigationMenuList>
93+
<NavigationItems
94+
domain={domain}
95+
numberOfRepos={numberOfRepos}
96+
numberOfReposWithFirstTimeIndexingJobsInProgress={numberOfReposWithFirstTimeIndexingJobsInProgress}
97+
isAuthenticated={isAuthenticated}
98+
/>
15099
</NavigationMenuBase>
151100
</div>
152101

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"use client";
2+
3+
import { NavigationMenuItem, NavigationMenuLink, NavigationMenuList, navigationMenuTriggerStyle } from "@/components/ui/navigation-menu";
4+
import { Badge } from "@/components/ui/badge";
5+
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
6+
import { cn, getShortenedNumberDisplayString } from "@/lib/utils";
7+
import { SearchIcon, MessageCircleIcon, BookMarkedIcon, SettingsIcon, CircleIcon } from "lucide-react";
8+
import { usePathname } from "next/navigation";
9+
10+
interface NavigationItemsProps {
11+
domain: string;
12+
numberOfRepos: number;
13+
numberOfReposWithFirstTimeIndexingJobsInProgress: number;
14+
isAuthenticated: boolean;
15+
}
16+
17+
export const NavigationItems = ({
18+
domain,
19+
numberOfRepos,
20+
numberOfReposWithFirstTimeIndexingJobsInProgress,
21+
isAuthenticated,
22+
}: NavigationItemsProps) => {
23+
const pathname = usePathname();
24+
25+
const isActive = (href: string) => {
26+
if (href === `/${domain}`) {
27+
return pathname === `/${domain}`;
28+
}
29+
return pathname.startsWith(href);
30+
};
31+
32+
return (
33+
<NavigationMenuList className="gap-2">
34+
<NavigationMenuItem className="relative">
35+
<NavigationMenuLink
36+
href={`/${domain}`}
37+
className={cn(navigationMenuTriggerStyle(), "gap-2")}
38+
>
39+
<SearchIcon className="w-4 h-4 mr-1" />
40+
Search
41+
</NavigationMenuLink>
42+
{isActive(`/${domain}`) && <ActiveIndicator />}
43+
</NavigationMenuItem>
44+
<NavigationMenuItem className="relative">
45+
<NavigationMenuLink
46+
href={`/${domain}/chat`}
47+
className={navigationMenuTriggerStyle()}
48+
>
49+
<MessageCircleIcon className="w-4 h-4 mr-1" />
50+
Ask
51+
</NavigationMenuLink>
52+
{isActive(`/${domain}/chat`) && <ActiveIndicator />}
53+
</NavigationMenuItem>
54+
<NavigationMenuItem className="relative">
55+
<NavigationMenuLink
56+
href={`/${domain}/repos`}
57+
className={navigationMenuTriggerStyle()}
58+
>
59+
<BookMarkedIcon className="w-4 h-4 mr-1" />
60+
<span className="mr-2">Repositories</span>
61+
<Badge variant="secondary" className="px-1.5 relative">
62+
{getShortenedNumberDisplayString(numberOfRepos)}
63+
{numberOfReposWithFirstTimeIndexingJobsInProgress > 0 && (
64+
<CircleIcon className="absolute -right-0.5 -top-0.5 h-2 w-2 text-green-600" fill="currentColor" />
65+
)}
66+
</Badge>
67+
</NavigationMenuLink>
68+
{isActive(`/${domain}/repos`) && <ActiveIndicator />}
69+
</NavigationMenuItem>
70+
{isAuthenticated && (
71+
<NavigationMenuItem className="relative">
72+
<NavigationMenuLink
73+
href={`/${domain}/settings`}
74+
className={navigationMenuTriggerStyle()}
75+
>
76+
<SettingsIcon className="w-4 h-4 mr-1" />
77+
Settings
78+
</NavigationMenuLink>
79+
{isActive(`/${domain}/settings`) && <ActiveIndicator />}
80+
</NavigationMenuItem>
81+
)}
82+
</NavigationMenuList>
83+
);
84+
};
85+
86+
const ActiveIndicator = () => {
87+
return (
88+
<div className="absolute -bottom-2 left-0 right-0 h-0.5 bg-foreground" />
89+
);
90+
};

0 commit comments

Comments
 (0)