Files
guilan-ace-frontend/src/components/Navbar.tsx

161 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import type { ReactNode } from "react";
import { useMemo } from "react";
import { LayoutDashboard, LogOut, RotateCcw, UserRound } from "lucide-react";
import { Link, NavLink } from "@/lib/router";
import { useAuth } from "@/contexts/AuthContext";
import ModeToggle from "@/components/ModeToggle";
import NotificationsBell from "@/components/NotificationsBell";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { cn } from "@/lib/utils";
const NavItem = ({ to, children }: { to: string; children: ReactNode }) => (
<NavLink
to={to}
className={({ isActive }) =>
cn(
"rounded-full px-3 py-2 text-sm font-medium transition-colors",
isActive
? "bg-primary text-primary-foreground"
: "text-muted-foreground hover:bg-muted hover:text-foreground",
)
}
>
{children}
</NavLink>
);
function ProfileAvatarMenu() {
const { user, isAuthenticated } = useAuth();
const isAdminUser = isAuthenticated && Boolean(user?.is_staff || user?.is_superuser);
const avatarInitials = useMemo(
() =>
(user?.first_name?.[0] ||
user?.last_name?.[0] ||
user?.username?.[0] ||
"?").toUpperCase(),
[user?.first_name, user?.last_name, user?.username],
);
if (!isAuthenticated) {
return (
<Link to="/auth">
<Button className="rounded-full px-5">ورود / ثبتنام</Button>
</Link>
);
}
return (
<DropdownMenu dir="rtl">
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="h-11 w-11 rounded-full border-0 bg-transparent p-0 shadow-none transition hover:bg-background/45"
aria-label="منوی حساب کاربری"
>
<Avatar className="h-10 w-10 border border-white/30 shadow-sm">
<AvatarImage
src={
user?.profile_picture_preview_url ||
user?.profile_picture ||
undefined
}
alt={user?.username || "profile"}
/>
<AvatarFallback>{avatarInitials}</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" sideOffset={12} className="w-64 rounded-2xl p-2 text-right">
<DropdownMenuLabel className="text-right">
{[user?.first_name, user?.last_name].filter(Boolean).join(" ") || user?.username || "حساب کاربری"}
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem asChild className="flex-row-reverse justify-end gap-2 rounded-xl">
<Link to="/profile">
مشاهده پروفایل
<UserRound className="h-4 w-4" />
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild className="flex-row-reverse justify-end gap-2 rounded-xl">
<Link to="/reset-password">
تغییر یا بازیابی رمز
<RotateCcw className="h-4 w-4" />
</Link>
</DropdownMenuItem>
{isAdminUser ? (
<DropdownMenuItem asChild className="flex-row-reverse justify-end gap-2 rounded-xl">
<Link to="/admin">
داشبورد مدیریت
<LayoutDashboard className="h-4 w-4" />
</Link>
</DropdownMenuItem>
) : null}
<DropdownMenuSeparator />
<DropdownMenuItem asChild className="flex-row-reverse justify-end gap-2 rounded-xl text-destructive focus:text-destructive">
<Link to="/logout">
خروج از حساب
<LogOut className="h-4 w-4" />
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
export default function Navbar() {
const { isAuthenticated } = useAuth();
return (
<nav
className="sticky top-0 z-40 border-b bg-background/75 backdrop-blur-xl supports-[backdrop-filter]:bg-background/55"
dir="rtl"
>
<div className="container mx-auto px-4 py-3">
<div className="flex items-center justify-between gap-4">
<Link to="/" className="flex min-w-0 items-center gap-3">
<div className="hidden rounded-2xl sm:flex">
<img src="/favicon.ico" alt="لوگوی انجمن" className="h-10 w-10 object-contain" />
</div>
<div className="min-w-0 text-right">
<p className="truncate text-sm font-semibold text-foreground sm:text-base">
انجمن علمی مهندسی کامپیوتر
</p>
<p className="hidden text-xs text-muted-foreground sm:block">
دانشکدهی فنی و مهندسی شرق گیلان
</p>
</div>
</Link>
<div className="hidden items-center gap-2 md:flex">
<NavItem to="/">خانه</NavItem>
<NavItem to="/blog">بلاگ</NavItem>
<NavItem to="/events">رویدادها</NavItem>
<ModeToggle />
{isAuthenticated ? <NotificationsBell /> : null}
<ProfileAvatarMenu />
</div>
<div className="flex items-center gap-2 md:hidden">
{isAuthenticated ? <NotificationsBell /> : null}
<ModeToggle />
<ProfileAvatarMenu />
</div>
</div>
</div>
</nav>
);
}