- Timestamp:
- 05/05/26 17:55:27 (2 months ago)
- Branches:
- master
- Children:
- b3d5fed
- Parents:
- 4f85b41
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
frontend/src/pages/Dashboard/components/DashboardSidebar.jsx
r4f85b41 re9a0543 1 import React from "react";1 import React, { useEffect, useMemo, useState } from "react"; 2 2 import { NavLink, useLocation } from "react-router-dom"; 3 3 … … 20 20 MdChecklist, 21 21 MdAdd, 22 MdBook, 23 MdFlag, 24 MdStar, 25 MdBolt, 26 MdNoteAlt, 27 MdAutoGraph, 22 28 } from "react-icons/md"; 23 29 24 30 import logo from "../../../assets/logo.png"; 31 32 import AddCustomCategoryModal from "./AddCustomCategoryModal.jsx"; 33 import { 34 createCustomTrackingCategory, 35 getCustomTrackingCategories, 36 } from "../../../api/discipline.js"; 25 37 26 38 const navItems = [ … … 33 45 ]; 34 46 47 const CUSTOM_ICONS = [MdBook, MdFlag, MdStar, MdBolt, MdNoteAlt, MdAutoGraph]; 48 49 function stableIconForId(id) { 50 const n = Number(id); 51 if (!Number.isFinite(n)) return CUSTOM_ICONS[0]; 52 return CUSTOM_ICONS[Math.abs(n) % CUSTOM_ICONS.length]; 53 } 54 35 55 const DashboardSidebar = () => { 36 56 const location = useLocation(); 57 const [customOpen, setCustomOpen] = useState(false); 58 const [customCategories, setCustomCategories] = useState([]); 59 60 useEffect(() => { 61 let mounted = true; 62 (async () => { 63 try { 64 const res = await getCustomTrackingCategories(); 65 const items = res?.items ?? []; 66 if (mounted) setCustomCategories(items); 67 } catch { 68 // If the user isn't authenticated yet or the endpoint fails, 69 // keep the sidebar usable (no custom categories). 70 if (mounted) setCustomCategories([]); 71 } 72 })(); 73 return () => { 74 mounted = false; 75 }; 76 }, []); 77 78 const customNavItems = useMemo(() => { 79 return (customCategories ?? []).map((c) => { 80 const Icon = stableIconForId(c.customTrackingId); 81 return { 82 title: c.name, 83 to: `/dashboard/custom/${c.customTrackingId}`, 84 icon: Icon, 85 }; 86 }); 87 }, [customCategories]); 37 88 38 89 return ( … … 52 103 {navItems.map((item) => ( 53 104 <SidebarMenuItem key={item.title}> 105 {(() => { 106 const isActive = 107 location.pathname === item.to || 108 location.pathname.startsWith(`${item.to}/`); 109 110 return ( 111 <SidebarMenuButton 112 asChild 113 isActive={isActive} 114 className={ 115 isActive 116 ? "!bg-green-400 !text-black hover:!bg-green-400 active:!bg-green-400" 117 : "" 118 } 119 > 120 <NavLink 121 to={item.to} 122 className="flex w-full items-center gap-3" 123 > 124 <item.icon 125 className={ 126 isActive 127 ? "size-6 shrink-0 text-black" 128 : "size-6 shrink-0" 129 } 130 /> 131 <span className={isActive ? "text-black" : ""}> 132 {item.title} 133 </span> 134 </NavLink> 135 </SidebarMenuButton> 136 ); 137 })()} 138 </SidebarMenuItem> 139 ))} 140 141 {customNavItems.length > 0 ? ( 142 <div className="my-4 border-t border-white/10" /> 143 ) : null} 144 145 {customNavItems.map((item) => ( 146 <SidebarMenuItem key={`custom-${item.to}`}> 54 147 {(() => { 55 148 const isActive = … … 94 187 className="w-full justify-start gap-2 !bg-green-400 !text-black hover:!bg-green-500" 95 188 type="button" 189 onClick={() => setCustomOpen(true)} 96 190 > 97 191 <MdAdd className="size-5" /> … … 99 193 </Button> 100 194 </div> 195 196 <AddCustomCategoryModal 197 open={customOpen} 198 onClose={() => setCustomOpen(false)} 199 onSubmit={async (payload) => { 200 const created = await createCustomTrackingCategory(payload); 201 setCustomCategories((prev) => [created, ...(prev ?? [])]); 202 }} 203 /> 101 204 </SidebarContent> 102 205 </Sidebar>
Note:
See TracChangeset
for help on using the changeset viewer.
