first commit

This commit is contained in:
Michael Dong
2026-02-05 11:24:40 +08:00
commit a98e12f286
144 changed files with 26459 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { Bell, BellDot, ListTodo, Settings, UserPlus } from "lucide-react";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarSeparator,
} from "@/components/ui/sidebar";
import Avatar from "@/components/ui/avatar";
import { useTranslation, type TranslationKey } from "@/lib/i18n";
import { useNotification } from "@/lib/notification-context";
import { useUser } from "@/lib/user-context";
const navItems: { href: string; labelKey: TranslationKey; icon: typeof ListTodo }[] = [
{ href: "/todos", labelKey: "navTodo", icon: ListTodo },
{ href: "/reminders", labelKey: "navReminder", icon: Bell },
{ href: "/invites", labelKey: "navInvites", icon: UserPlus },
{ href: "/settings", labelKey: "navSettings", icon: Settings },
{ href: "/notifications", labelKey: "navNotifications", icon: BellDot },
];
const AppSidebar = () => {
const pathname = usePathname();
const { unreadCount } = useNotification();
const { user } = useUser();
const t = useTranslation();
return (
<Sidebar variant="inset" className="h-[calc(100vh-3rem)] self-start bg-white">
<SidebarHeader className="gap-2 px-3 py-4">
<div className="flex items-center gap-2">
<span className="flex h-8 w-8 items-center justify-center rounded-full bg-blue-500/10 text-blue-600">
<span className="text-base font-semibold"></span>
</span>
<span className="text-base font-semibold group-data-[state=collapsed]/sidebar:hidden">
notify
</span>
</div>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel className="group-data-[state=collapsed]/sidebar:hidden">
{t("navigation")}
</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{navItems.map((item) => {
const isActive = pathname?.startsWith(item.href);
const isNotifications = item.href === "/notifications";
return (
<SidebarMenuItem key={item.href}>
<SidebarMenuButton
asChild
isActive={isActive}
className="group-data-[state=collapsed]/sidebar:justify-center group-data-[state=collapsed]/sidebar:px-2"
>
<Link href={item.href}>
<span className="relative">
<item.icon className="h-4 w-4" />
{isNotifications && unreadCount > 0 && (
<span className="absolute -right-1 -top-1 hidden h-2 w-2 rounded-full bg-red-500 group-data-[state=collapsed]/sidebar:inline-flex" />
)}
</span>
<span className="group-data-[state=collapsed]/sidebar:hidden">
{t(item.labelKey)}
</span>
{isNotifications && unreadCount > 0 && (
<span className="ml-auto rounded-full bg-red-500 px-2 py-0.5 text-xs font-semibold text-white group-data-[state=collapsed]/sidebar:hidden">
{unreadCount}
</span>
)}
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
);
})}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarSeparator />
<SidebarFooter className="px-3 py-4">
<div className="flex items-center gap-2 group-data-[state=collapsed]/sidebar:justify-center">
<Avatar username={user?.username} src={user?.avatar} size="sm" />
<span className="text-sm font-medium text-slate-700 group-data-[state=collapsed]/sidebar:hidden">
{user?.username}
</span>
</div>
</SidebarFooter>
</Sidebar>
);
};
export default AppSidebar;