pmtool / src /components /QuickAccess.tsx
devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
8.28 kB
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { Button } from "@/components/ui/button";
import {
CheckSquare,
Grid3x3,
X,
Plus,
FileText,
LayoutDashboard,
Settings,
Bell,
ChevronLeft,
Bug
} from "lucide-react";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { motion, AnimatePresence } from "framer-motion";
import { useAuth } from "@/lib/auth-context";
const QuickAccess: React.FC = () => {
const [isOpen, setIsOpen] = useState(false);
const [activeIndex, setActiveIndex] = useState<number | null>(null);
const navigate = useNavigate();
const { userData } = useAuth();
// Don't render if userData is null or user is client role
if (!userData || userData?.roleName?.toLowerCase() === "client") {
return null;
}
const handleNavigate = (path: string, index: number) => {
setActiveIndex(index);
setTimeout(() => {
navigate(path);
setIsOpen(false);
setActiveIndex(null);
}, 300);
};
const tools = [
{
name: "Tasks",
icon: <CheckSquare className="h-5 w-5" />,
path: "/tasks",
shortcut: "Alt+T",
color: "from-blue-400 to-indigo-500"
},
{
name: "Features",
icon: <Grid3x3 className="h-5 w-5" />,
path: "/issues",
shortcut: "Alt+F",
color: "from-emerald-400 to-teal-500"
},
{
name: "Bugs",
icon: <Bug className="h-5 w-5" />,
path: "/bugs",
shortcut: "Alt+B",
color: "from-red-400 to-rose-500"
},
{
name: "Dashboard",
icon: <LayoutDashboard className="h-5 w-5" />,
path: "/",
shortcut: "Alt+D",
color: "from-purple-400 to-violet-500"
},
// {
// name: "Files",
// icon: <FileText className="h-5 w-5" />,
// path: "/files",
// shortcut: "Alt+L",
// color: "from-amber-400 to-orange-500"
// },
// {
// name: "Notifications",
// icon: <Bell className="h-5 w-5" />,
// path: "/notifications",
// shortcut: "Alt+N",
// color: "from-pink-400 to-rose-500"
// },
{
name: "Settings",
icon: <Settings className="h-5 w-5" />,
path: "/settings",
shortcut: "Alt+S",
color: "from-gray-400 to-gray-500"
}
];
const slideVariants = {
hidden: { opacity: 0, x: 100 },
visible: { opacity: 1, x: 0 },
exit: { opacity: 0, x: 100 }
};
return (
<div className="fixed inset-y-0 right-0 z-50 flex items-center justify-end overflow-hidden">
<AnimatePresence mode="wait">
{isOpen && (
<motion.div
variants={slideVariants}
initial="hidden"
animate="visible"
exit="exit"
transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
className="bg-white/50 dark:bg-gray-800/50 backdrop-blur-md rounded-l-2xl shadow-[0_0_25px_rgba(0,0,0,0.1)] dark:shadow-[0_0_25px_rgba(0,0,0,0.3)] py-4 px-6 flex flex-col gap-3 border-y border-l border-gray-200/60 dark:border-gray-700/60 overflow-hidden"
style={{ maxHeight: '90vh', width: '280px' }}
>
<div className="flex items-center justify-between">
<div className="text-lg font-medium text-gray-800 dark:text-gray-200">
Quick Access
</div>
<Button
size="sm"
variant="ghost"
className="rounded-full w-8 h-8 flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-700"
onClick={() => setIsOpen(false)}
>
<X className="h-4 w-4" />
</Button>
</div>
<div className="flex flex-col space-y-2 overflow-y-auto overflow-x-hidden py-2 px-1 custom-scrollbar"
style={{ maxHeight: 'calc(90vh - 80px)' }}>
{tools.map((tool, index) => (
<motion.div
key={tool.name}
initial={{ opacity: 0, x: 30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.05, duration: 0.2 }}
whileHover={{ scale: 1.03, x: -5, transition: { duration: 0.2 } }}
whileTap={{ scale: 0.97 }}
className={`relative flex items-center p-3 bg-gray-50/80 dark:bg-gray-800/80 rounded-xl cursor-pointer transition-all overflow-hidden ${
activeIndex === index ? "pointer-events-none" : ""
}`}
onClick={() => handleNavigate(tool.path, index)}
>
{activeIndex === index && (
<motion.div
className={`absolute inset-0 bg-gradient-to-r ${tool.color} opacity-10`}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}
/>
)}
<div className={`size-10 flex-shrink-0 flex items-center justify-center rounded-full bg-gradient-to-br ${tool.color} text-white shadow-sm mr-3`}>
{tool.icon}
</div>
<div className="flex flex-col min-w-0">
<span className="text-sm font-medium text-gray-700 dark:text-gray-300 truncate">
{tool.name}
</span>
<kbd className="mt-1 w-fit px-1.5 py-0.5 text-xs font-mono text-gray-500 bg-gray-100 dark:bg-gray-700 dark:text-gray-300 border border-gray-200 dark:border-gray-600 rounded-md shadow-sm">
{tool.shortcut}
</kbd>
</div>
{activeIndex === index && (
<motion.div
className="absolute inset-0 bg-white/70 dark:bg-gray-800/70 flex items-center justify-center"
initial={{ scale: 0, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 0.3, delay: 0.1 }}
>
<div className={`size-10 flex items-center justify-center rounded-full bg-gradient-to-br ${tool.color} animate-pulse`}>
{tool.icon}
</div>
</motion.div>
)}
</motion.div>
))}
</div>
</motion.div>
)}
</AnimatePresence>
{!isOpen && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<motion.div
whileTap={{ scale: 0.95 }}
whileHover={{ x: -5 }}
className="group"
>
<Button
size="icon"
className="h-12 w-12 rounded-l-xl rounded-r-none shadow-lg border-y border-l border-gray-300 dark:border-gray-600 bg-indigo-500 hover:bg-indigo-600 dark:bg-indigo-600 dark:hover:bg-indigo-700 text-white"
onClick={() => setIsOpen(true)}
>
<ChevronLeft className="h-6 w-6" />
</Button>
</motion.div>
</TooltipTrigger>
<TooltipContent side="left" className="bg-gray-800 text-white border-gray-700">
<p>Open Quick Access</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
);
};
export default QuickAccess;
// Add this CSS to your global styles
/*
.custom-scrollbar::-webkit-scrollbar {
width: 5px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background-color: rgba(156, 163, 175, 0.3);
border-radius: 20px;
}
.custom-scrollbar {
scrollbar-width: thin;
scrollbar-color: rgba(156, 163, 175, 0.3) transparent;
}
*/