mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-19 19:21:33 +10:00
refactor(v4.0.0-alpha): beginning of a new era
This commit is contained in:
129
apps/client/src/pages/dashboard/_components/sidebar.tsx
Normal file
129
apps/client/src/pages/dashboard/_components/sidebar.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import { FadersHorizontal, ReadCvLogo } from "@phosphor-icons/react";
|
||||
import { Button, KeyboardShortcut, Separator } from "@reactive-resume/ui";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
import { motion } from "framer-motion";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import useKeyboardShortcut from "use-keyboard-shortcut";
|
||||
|
||||
import { Copyright } from "@/client/components/copyright";
|
||||
import { Icon } from "@/client/components/icon";
|
||||
import { UserAvatar } from "@/client/components/user-avatar";
|
||||
import { UserOptions } from "@/client/components/user-options";
|
||||
import { useUser } from "@/client/services/user";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const ActiveIndicator = ({ className }: Props) => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
className={cn(
|
||||
"h-1.5 w-1.5 animate-pulse rounded-full bg-info shadow-[0_0_12px] shadow-info",
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
interface SidebarItem {
|
||||
path: string;
|
||||
name: string;
|
||||
shortcut?: string;
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
const sidebarItems: SidebarItem[] = [
|
||||
{
|
||||
path: "/dashboard/resumes",
|
||||
name: "Resumes",
|
||||
shortcut: "⇧R",
|
||||
icon: <ReadCvLogo />,
|
||||
},
|
||||
{
|
||||
path: "/dashboard/settings",
|
||||
name: "Settings",
|
||||
shortcut: "⇧S",
|
||||
icon: <FadersHorizontal />,
|
||||
},
|
||||
];
|
||||
|
||||
type SidebarItemProps = SidebarItem & {
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
const SidebarItem = ({ path, name, shortcut, icon, onClick }: SidebarItemProps) => {
|
||||
const isActive = useLocation().pathname === path;
|
||||
|
||||
return (
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
variant="ghost"
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
"h-auto justify-start px-4 py-3",
|
||||
isActive && "pointer-events-none bg-secondary/50 text-secondary-foreground",
|
||||
)}
|
||||
>
|
||||
<Link to={path}>
|
||||
<div className="mr-3">{icon}</div>
|
||||
<span>{name}</span>
|
||||
{!isActive && <KeyboardShortcut className="ml-auto">{shortcut}</KeyboardShortcut>}
|
||||
{isActive && <ActiveIndicator className="ml-auto" />}
|
||||
</Link>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
type SidebarProps = {
|
||||
setOpen?: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export const Sidebar = ({ setOpen }: SidebarProps) => {
|
||||
const { user } = useUser();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useKeyboardShortcut(["shift", "r"], () => {
|
||||
navigate("/dashboard/resumes");
|
||||
setOpen?.(false);
|
||||
});
|
||||
|
||||
useKeyboardShortcut(["shift", "s"], () => {
|
||||
navigate("/dashboard/settings");
|
||||
setOpen?.(false);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col gap-y-4">
|
||||
<div className="ml-12 flex justify-center lg:ml-0">
|
||||
<Button asChild size="icon" variant="ghost" className="h-10 w-10 p-0">
|
||||
<Link to="/">
|
||||
<Icon size={24} className="mx-auto hidden lg:block" />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Separator className="opacity-50" />
|
||||
|
||||
<div className="grid gap-y-2">
|
||||
{sidebarItems.map((item) => (
|
||||
<SidebarItem {...item} key={item.path} onClick={() => setOpen?.(false)} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex-1" />
|
||||
|
||||
<Separator className="opacity-50" />
|
||||
|
||||
<UserOptions>
|
||||
<Button size="lg" variant="ghost" className="w-full justify-start px-3">
|
||||
<UserAvatar size={24} className="mr-3" />
|
||||
<span>{user?.name}</span>
|
||||
</Button>
|
||||
</UserOptions>
|
||||
|
||||
<Copyright className="ml-2" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user