added button themes and fixed alignment

This commit is contained in:
Deon Sanchez 2025-09-30 13:26:24 -06:00
parent 52dd723bba
commit baaf624eae
4 changed files with 139 additions and 52 deletions

View file

@ -1,7 +1,10 @@
"use client"; "use client";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { useGetConversationsQuery, type ChatConversation } from "@/app/api/queries/useGetConversationsQuery"; import {
useGetConversationsQuery,
type ChatConversation,
} from "@/app/api/queries/useGetConversationsQuery";
import { KnowledgeFilterDropdown } from "@/components/knowledge-filter-dropdown"; import { KnowledgeFilterDropdown } from "@/components/knowledge-filter-dropdown";
import { ModeToggle } from "@/components/mode-toggle"; import { ModeToggle } from "@/components/mode-toggle";
import { Navigation } from "@/components/navigation"; import { Navigation } from "@/components/navigation";

View file

@ -2,7 +2,10 @@
import { Bell, Loader2 } from "lucide-react"; import { Bell, Loader2 } from "lucide-react";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { useGetConversationsQuery, type ChatConversation } from "@/app/api/queries/useGetConversationsQuery"; import {
useGetConversationsQuery,
type ChatConversation,
} from "@/app/api/queries/useGetConversationsQuery";
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery"; import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
import { KnowledgeFilterPanel } from "@/components/knowledge-filter-panel"; import { KnowledgeFilterPanel } from "@/components/knowledge-filter-panel";
import Logo from "@/components/logo/logo"; import Logo from "@/components/logo/logo";
@ -50,10 +53,10 @@ export function LayoutWrapper({ children }: { children: React.ReactNode }) {
// Calculate active tasks for the bell icon // Calculate active tasks for the bell icon
const activeTasks = tasks.filter( const activeTasks = tasks.filter(
(task) => task =>
task.status === "pending" || task.status === "pending" ||
task.status === "running" || task.status === "running" ||
task.status === "processing", task.status === "processing"
); );
// Show loading state when backend isn't ready // Show loading state when backend isn't ready
@ -85,7 +88,7 @@ export function LayoutWrapper({ children }: { children: React.ReactNode }) {
</div> </div>
</div> </div>
<div className="header-end-division"> <div className="header-end-division">
<div className="header-end-display"> <div className="justify-end flex items-center">
{/* Knowledge Filter Dropdown */} {/* Knowledge Filter Dropdown */}
{/* <KnowledgeFilterDropdown {/* <KnowledgeFilterDropdown
selectedFilter={selectedFilter} selectedFilter={selectedFilter}
@ -99,20 +102,18 @@ export function LayoutWrapper({ children }: { children: React.ReactNode }) {
{/* <DiscordLink inviteCode="EqksyE2EX9" /> */} {/* <DiscordLink inviteCode="EqksyE2EX9" /> */}
{/* Task Notification Bell */} {/* Task Notification Bell */}
<Button <button
variant="ghost"
size="iconSm"
onClick={toggleMenu} onClick={toggleMenu}
className="relative" className="h-9 w-9 hover:bg-muted rounded-lg flex items-center justify-center"
> >
<Bell className="h-4 w-4 text-muted-foreground" /> <Bell size={20} className="text-muted-foreground" />
{activeTasks.length > 0 && ( {activeTasks.length > 0 && (
<div className="header-notifications" /> <div className="header-notifications" />
)} )}
</Button> </button>
{/* Separator */} {/* Separator */}
<div className="w-px h-6 bg-border" /> <div className="w-px h-6 bg-border ml-3 mr-4" />
<UserNav /> <UserNav />
</div> </div>

View file

@ -0,0 +1,68 @@
import { useEffect, useState } from "react";
import { Monitor, Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
export const ThemeButtons = () => {
const { theme, setTheme } = useTheme();
const [selectedTheme, setSelectedTheme] = useState("dark");
// Sync local state with theme context
useEffect(() => {
if (theme) {
setSelectedTheme(theme);
}
}, [theme]);
const handleThemeChange = (newTheme: string) => {
setSelectedTheme(newTheme);
setTheme(newTheme);
};
return (
<div className="flex items-center gap-1 border border-border rounded-full">
{/* Light Theme Button */}
<button
className={`h-7 w-7 rounded-full flex items-center justify-center ${
selectedTheme === "light"
? "bg-amber-400 text-primary"
: "text-foreground hover:bg-amber-400 hover:text-background"
}`}
onClick={() => handleThemeChange("light")}
data-testid="menu_light_button"
id="menu_light_button"
>
<Sun className="h-4 w-4 rounded-full" />
</button>
{/* Dark Theme Button */}
<button
className={`h-7 w-7 rounded-full flex items-center justify-center ${
selectedTheme === "dark"
? "bg-purple-500/20 text-purple-500 hover:bg-purple-500/20 hover:text-purple-500"
: "text-foreground hover:bg-purple-500/20 hover:text-purple-500"
}`}
onClick={() => handleThemeChange("dark")}
data-testid="menu_dark_button"
id="menu_dark_button"
>
<Moon className="h-4 w-4" />
</button>
{/* System Theme Button */}
<button
className={`h-7 w-7 rounded-full flex items-center justify-center ${
selectedTheme === "system"
? "bg-foreground text-background"
: "hover:bg-foreground hover:text-background"
}`}
onClick={() => handleThemeChange("system")}
data-testid="menu_system_button"
id="menu_system_button"
>
<Monitor className="h-4 w-4" />
</button>
</div>
);
};
export default ThemeButtons;

View file

@ -1,7 +1,7 @@
"use client" "use client";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button";
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
@ -9,65 +9,79 @@ import {
DropdownMenuLabel, DropdownMenuLabel,
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu" } from "@/components/ui/dropdown-menu";
import { useAuth } from "@/contexts/auth-context" import { useAuth } from "@/contexts/auth-context";
import { LogIn, LogOut, User, Moon, Sun, ChevronsUpDown } from "lucide-react" import { LogIn, LogOut, User, Moon, Sun, ChevronsUpDown } from "lucide-react";
import { useTheme } from "next-themes" import { useTheme } from "next-themes";
import ThemeButtons from "./ui/buttonTheme";
export function UserNav() { export function UserNav() {
const { user, isLoading, isAuthenticated, isNoAuthMode, login, logout } = useAuth() const { user, isLoading, isAuthenticated, isNoAuthMode, login, logout } =
const { theme, setTheme } = useTheme() useAuth();
const { theme, setTheme } = useTheme();
if (isLoading) { if (isLoading) {
return ( return <div className="h-8 w-8 rounded-full bg-muted animate-pulse" />;
<div className="h-8 w-8 rounded-full bg-muted animate-pulse" />
)
} }
// In no-auth mode, show a simple theme switcher instead of auth UI // In no-auth mode, show a simple theme switcher instead of auth UI
if (isNoAuthMode) { if (isNoAuthMode) {
return ( return (
<Button <Button
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')} onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
variant="outline" variant="outline"
size="sm" size="sm"
className="flex items-center gap-2" className="flex items-center gap-2"
> >
{theme === 'dark' ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />} {theme === "dark" ? (
<Sun className="h-4 w-4" />
) : (
<Moon className="h-4 w-4" />
)}
</Button> </Button>
) );
} }
if (!isAuthenticated) { if (!isAuthenticated) {
return ( return (
<Button <Button
onClick={login} onClick={login}
variant="outline" variant="outline"
size="sm" size="sm"
className="flex items-center gap-2" className="flex items-center gap-2"
> >
<LogIn className="h-4 w-4" /> <LogIn className="h-4 w-4" />
Sign In Sign In
</Button> </Button>
) );
} }
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="ghost" className="flex items-center gap-1 h-8 px-1 rounded-full"> <button className="hover:bg-muted rounded-lg flex items-center justify-center">
<Avatar className="h-6 w-6"> <Avatar className="rounded-md w-8 h-8">
<AvatarImage src={user?.picture} alt={user?.name} /> <AvatarImage
<AvatarFallback className="text-xs"> width={16}
{user?.name ? user.name.charAt(0).toUpperCase() : <User className="h-3 w-3" />} height={16}
src={user?.picture}
alt={user?.name}
className="rounded-md"
/>
<AvatarFallback className="text-xs rounded-md">
{user?.name ? (
user.name.charAt(0).toUpperCase()
) : (
<User className="h-3 w-3" />
)}
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
<ChevronsUpDown className="h-3 w-3 text-muted-foreground" /> <ChevronsUpDown size={16} className="text-muted-foreground mx-2" />
</Button> </button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent className="w-56" align="end" forceMount> <DropdownMenuContent className="w-56" align="end" forceMount>
<DropdownMenuLabel className="font-normal"> <DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-2">
<p className="text-sm font-medium leading-none">{user?.name}</p> <p className="text-sm font-medium leading-none">{user?.name}</p>
<p className="text-xs leading-none text-muted-foreground"> <p className="text-xs leading-none text-muted-foreground">
{user?.email} {user?.email}
@ -75,20 +89,21 @@ export function UserNav() {
</div> </div>
</DropdownMenuLabel> </DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem onClick={() => setTheme(theme === "light" ? "dark" : "light")}> {/* <DropdownMenuItem className="flex items-center justify-between"> */}
{theme === "light" ? ( <div className="flex items-center justify-between px-2 h-8">
<Moon className="mr-2 h-4 w-4" /> <span className="text-sm">Theme</span>
) : ( <ThemeButtons />
<Sun className="mr-2 h-4 w-4" /> </div>
)} {/* </DropdownMenuItem> */}
<span>Toggle Theme</span>
</DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem onClick={logout} className="text-red-600 focus:text-red-600"> <DropdownMenuItem
onClick={logout}
className="text-red-600 focus:text-red-600"
>
<LogOut className="mr-2 h-4 w-4" /> <LogOut className="mr-2 h-4 w-4" />
<span>Log out</span> <span>Log out</span>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
) );
} }