repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
Projectflow
stars
latest
clone command
git clone gitlawb://did:key:z6Mkfh4Y...QBEi/projectflowgit clone gitlawb://did:key:z6Mkfh4Y.../projectflowb3cded1async from playground1d ago| #1 | import { motion, AnimatePresence } from "framer-motion"; |
| #2 | import { |
| #3 | LayoutDashboard, |
| #4 | FolderKanban, |
| #5 | ChevronLeft, |
| #6 | ChevronRight, |
| #7 | Zap, |
| #8 | Users, |
| #9 | BarChart3, |
| #10 | ListTodo, |
| #11 | CalendarDays, |
| #12 | Layers, |
| #13 | } from "lucide-react"; |
| #14 | import clsx from "clsx"; |
| #15 | import { useStore } from "../../application/stores"; |
| #16 | import { Tooltip } from "../ui"; |
| #17 | |
| #18 | export function Sidebar() { |
| #19 | const { |
| #20 | sidebarCollapsed, |
| #21 | toggleSidebar, |
| #22 | projects, |
| #23 | currentProject, |
| #24 | setCurrentProject, |
| #25 | activeView, |
| #26 | setActiveView, |
| #27 | setCurrentIssue, |
| #28 | } = useStore(); |
| #29 | |
| #30 | const navItems = [ |
| #31 | { id: "dashboard", label: "Dashboard", icon: LayoutDashboard }, |
| #32 | { id: "projects", label: "Projects", icon: FolderKanban }, |
| #33 | ]; |
| #34 | |
| #35 | const projectViews = currentProject |
| #36 | ? [ |
| #37 | { id: "board", label: "Board", icon: Layers }, |
| #38 | { id: "backlog", label: "Backlog", icon: ListTodo }, |
| #39 | { id: "sprints", label: "Sprints", icon: CalendarDays }, |
| #40 | { id: "analytics", label: "Analytics", icon: BarChart3 }, |
| #41 | { id: "members", label: "Members", icon: Users }, |
| #42 | ] |
| #43 | : []; |
| #44 | |
| #45 | return ( |
| #46 | <motion.aside |
| #47 | animate={{ width: sidebarCollapsed ? 64 : 260 }} |
| #48 | transition={{ duration: 0.2, ease: "easeInOut" }} |
| #49 | className="h-screen flex flex-col border-r border-border bg-surface-1 shrink-0 overflow-hidden" |
| #50 | > |
| #51 | {/* Logo */} |
| #52 | <div className="h-14 flex items-center px-4 border-b border-border shrink-0"> |
| #53 | <div className="flex items-center gap-2.5 min-w-0"> |
| #54 | <div className="w-7 h-7 rounded-lg bg-accent flex items-center justify-center shrink-0"> |
| #55 | <Zap size={14} className="text-white" /> |
| #56 | </div> |
| #57 | <AnimatePresence> |
| #58 | {!sidebarCollapsed && ( |
| #59 | <motion.span |
| #60 | initial={{ opacity: 0, width: 0 }} |
| #61 | animate={{ opacity: 1, width: "auto" }} |
| #62 | exit={{ opacity: 0, width: 0 }} |
| #63 | className="text-sm font-semibold text-zinc-100 whitespace-nowrap overflow-hidden" |
| #64 | > |
| #65 | ProjectFlow |
| #66 | </motion.span> |
| #67 | )} |
| #68 | </AnimatePresence> |
| #69 | </div> |
| #70 | </div> |
| #71 | |
| #72 | {/* Navigation */} |
| #73 | <div className="flex-1 overflow-y-auto py-3 px-3 space-y-1"> |
| #74 | {navItems.map((item) => ( |
| #75 | <Tooltip key={item.id} content={sidebarCollapsed ? item.label : ""}> |
| #76 | <button |
| #77 | onClick={() => { |
| #78 | setActiveView(item.id); |
| #79 | setCurrentIssue(null); |
| #80 | }} |
| #81 | className={clsx( |
| #82 | "sidebar-item w-full", |
| #83 | activeView === item.id && "active" |
| #84 | )} |
| #85 | > |
| #86 | <item.icon size={18} className="shrink-0" /> |
| #87 | <AnimatePresence> |
| #88 | {!sidebarCollapsed && ( |
| #89 | <motion.span |
| #90 | initial={{ opacity: 0 }} |
| #91 | animate={{ opacity: 1 }} |
| #92 | exit={{ opacity: 0 }} |
| #93 | className="text-sm whitespace-nowrap" |
| #94 | > |
| #95 | {item.label} |
| #96 | </motion.span> |
| #97 | )} |
| #98 | </AnimatePresence> |
| #99 | </button> |
| #100 | </Tooltip> |
| #101 | ))} |
| #102 | |
| #103 | <AnimatePresence> |
| #104 | {!sidebarCollapsed && ( |
| #105 | <motion.div |
| #106 | initial={{ opacity: 0 }} |
| #107 | animate={{ opacity: 1 }} |
| #108 | exit={{ opacity: 0 }} |
| #109 | > |
| #110 | <div className="pt-4 pb-2 px-3"> |
| #111 | <span className="text-2xs font-medium uppercase tracking-wider text-zinc-500"> |
| #112 | Projects |
| #113 | </span> |
| #114 | </div> |
| #115 | {projects.map((project) => ( |
| #116 | <button |
| #117 | key={project.id} |
| #118 | onClick={() => { |
| #119 | setCurrentProject(project.id); |
| #120 | setActiveView("board"); |
| #121 | setCurrentIssue(null); |
| #122 | }} |
| #123 | className={clsx( |
| #124 | "sidebar-item w-full", |
| #125 | currentProject?.id === project.id && "active" |
| #126 | )} |
| #127 | > |
| #128 | <span className="text-base shrink-0">{project.emoji}</span> |
| #129 | <span className="text-sm whitespace-nowrap truncate"> |
| #130 | {project.name} |
| #131 | </span> |
| #132 | </button> |
| #133 | ))} |
| #134 | </motion.div> |
| #135 | )} |
| #136 | </AnimatePresence> |
| #137 | |
| #138 | {currentProject && ( |
| #139 | <AnimatePresence> |
| #140 | {!sidebarCollapsed && ( |
| #141 | <motion.div |
| #142 | initial={{ opacity: 0 }} |
| #143 | animate={{ opacity: 1 }} |
| #144 | exit={{ opacity: 0 }} |
| #145 | > |
| #146 | <div className="pt-4 pb-2 px-3"> |
| #147 | <span className="text-2xs font-medium uppercase tracking-wider text-zinc-500"> |
| #148 | {currentProject.emoji} {currentProject.name} |
| #149 | </span> |
| #150 | </div> |
| #151 | {projectViews.map((view) => ( |
| #152 | <button |
| #153 | key={view.id} |
| #154 | onClick={() => { |
| #155 | setActiveView(view.id); |
| #156 | setCurrentIssue(null); |
| #157 | }} |
| #158 | className={clsx( |
| #159 | "sidebar-item w-full", |
| #160 | activeView === view.id && "active" |
| #161 | )} |
| #162 | > |
| #163 | <view.icon size={16} className="shrink-0" /> |
| #164 | <span className="text-sm whitespace-nowrap"> |
| #165 | {view.label} |
| #166 | </span> |
| #167 | </button> |
| #168 | ))} |
| #169 | </motion.div> |
| #170 | )} |
| #171 | </AnimatePresence> |
| #172 | )} |
| #173 | </div> |
| #174 | |
| #175 | <div className="p-3 border-t border-border shrink-0"> |
| #176 | <button |
| #177 | onClick={toggleSidebar} |
| #178 | className="sidebar-item w-full justify-center" |
| #179 | > |
| #180 | {sidebarCollapsed ? ( |
| #181 | <ChevronRight size={16} /> |
| #182 | ) : ( |
| #183 | <ChevronLeft size={16} /> |
| #184 | )} |
| #185 | </button> |
| #186 | </div> |
| #187 | </motion.aside> |
| #188 | ); |
| #189 | } |
| #190 |