- Timestamp:
- 05/05/26 17:55:27 (2 months ago)
- Branches:
- master
- Children:
- b3d5fed
- Parents:
- 4f85b41
- Location:
- frontend
- Files:
-
- 5 added
- 5 edited
-
package-lock.json (modified) (2 diffs)
-
package.json (modified) (1 diff)
-
src/App.jsx (modified) (2 diffs)
-
src/api/discipline.js (modified) (1 diff)
-
src/pages/Dashboard/components/AddCustomCategoryModal.jsx (added)
-
src/pages/Dashboard/components/DashboardSidebar.jsx (modified) (6 diffs)
-
src/pages/Dashboard/pages/CustomCategory/CustomCategoryKanban.jsx (added)
-
src/pages/Dashboard/pages/CustomCategory/components/CustomCategoryKanbanBoard.jsx (added)
-
src/pages/Dashboard/pages/CustomCategory/components/KanbanColumn.jsx (added)
-
src/pages/Dashboard/pages/CustomCategory/components/TaskTicketCard.jsx (added)
Legend:
- Unmodified
- Added
- Removed
-
frontend/package-lock.json
r4f85b41 re9a0543 9 9 "version": "0.0.0", 10 10 "dependencies": { 11 "@dnd-kit/core": "^6.3.1", 12 "@dnd-kit/sortable": "^10.0.0", 13 "@dnd-kit/utilities": "^3.2.2", 11 14 "@relume_io/relume-tailwind": "^1.3.0", 12 15 "@relume_io/relume-ui": "^1.3.1", … … 337 340 "integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==", 338 341 "license": "MIT" 342 }, 343 "node_modules/@dnd-kit/accessibility": { 344 "version": "3.1.1", 345 "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", 346 "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", 347 "license": "MIT", 348 "dependencies": { 349 "tslib": "^2.0.0" 350 }, 351 "peerDependencies": { 352 "react": ">=16.8.0" 353 } 354 }, 355 "node_modules/@dnd-kit/core": { 356 "version": "6.3.1", 357 "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", 358 "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", 359 "license": "MIT", 360 "dependencies": { 361 "@dnd-kit/accessibility": "^3.1.1", 362 "@dnd-kit/utilities": "^3.2.2", 363 "tslib": "^2.0.0" 364 }, 365 "peerDependencies": { 366 "react": ">=16.8.0", 367 "react-dom": ">=16.8.0" 368 } 369 }, 370 "node_modules/@dnd-kit/sortable": { 371 "version": "10.0.0", 372 "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", 373 "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", 374 "license": "MIT", 375 "dependencies": { 376 "@dnd-kit/utilities": "^3.2.2", 377 "tslib": "^2.0.0" 378 }, 379 "peerDependencies": { 380 "@dnd-kit/core": "^6.3.0", 381 "react": ">=16.8.0" 382 } 383 }, 384 "node_modules/@dnd-kit/utilities": { 385 "version": "3.2.2", 386 "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", 387 "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", 388 "license": "MIT", 389 "dependencies": { 390 "tslib": "^2.0.0" 391 }, 392 "peerDependencies": { 393 "react": ">=16.8.0" 394 } 339 395 }, 340 396 "node_modules/@esbuild/aix-ppc64": { -
frontend/package.json
r4f85b41 re9a0543 11 11 }, 12 12 "dependencies": { 13 "@dnd-kit/core": "^6.3.1", 14 "@dnd-kit/sortable": "^10.0.0", 15 "@dnd-kit/utilities": "^3.2.2", 13 16 "@relume_io/relume-tailwind": "^1.3.0", 14 17 "@relume_io/relume-ui": "^1.3.1", -
frontend/src/App.jsx
r4f85b41 re9a0543 20 20 import Discipline from "./pages/Dashboard/pages/Discipline/Discipline.jsx"; 21 21 import DisciplineTracking from "./pages/Dashboard/pages/Discipline/DisciplineTracking.jsx"; 22 import CustomCategoryKanban from "./pages/Dashboard/pages/CustomCategory/CustomCategoryKanban.jsx"; 22 23 23 24 function RequireAuth({ children }) { … … 61 62 <Route path="discipline" element={<Discipline />} /> 62 63 <Route path="discipline/tracking" element={<DisciplineTracking />} /> 64 <Route path="custom/:customTrackingId" element={<CustomCategoryKanban />} /> 63 65 </Route> 64 66 <Route path="*" element={<Navigate to="/" replace />} /> -
frontend/src/api/discipline.js
r4f85b41 re9a0543 36 36 } 37 37 38 export async function getCustomTrackingCategories() { 39 const res = await api.get("/discipline/custom-tracking-categories"); 40 return res?.data; 41 } 42 43 export async function createCustomTrackingCategory(payload) { 44 const res = await api.post("/discipline/custom-tracking-categories", payload); 45 return res?.data; 46 } 47 48 export async function getCustomCategoryTasks(customTrackingId) { 49 const res = await api.get( 50 `/discipline/custom-tracking-categories/${customTrackingId}/tasks` 51 ); 52 return res?.data; 53 } 54 55 export async function createCustomCategoryTask(customTrackingId, payload) { 56 const res = await api.post( 57 `/discipline/custom-tracking-categories/${customTrackingId}/tasks`, 58 payload 59 ); 60 return res?.data; 61 } 62 63 export async function updateCustomCategoryTask(customTrackingId, taskId, payload) { 64 const res = await api.put( 65 `/discipline/custom-tracking-categories/${customTrackingId}/tasks/${taskId}`, 66 payload 67 ); 68 return res?.data; 69 } 70 71 export async function updateCustomCategoryTaskFinished( 72 customTrackingId, 73 taskId, 74 isFinished 75 ) { 76 const res = await api.patch( 77 `/discipline/custom-tracking-categories/${customTrackingId}/tasks/${taskId}/finished`, 78 { isFinished } 79 ); 80 return res?.data; 81 } 82 83 export async function deleteCustomCategoryTask(customTrackingId, taskId) { 84 return api.delete( 85 `/discipline/custom-tracking-categories/${customTrackingId}/tasks/${taskId}` 86 ); 87 } 88 -
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.
