feat: add counting bench, triage, and dataset modules
This commit includes major additions and updates to the frontend and backend architectures, introducing new dataset management, live counting features, batch processing, and triage logic. Includes new UI pages, components, and API routes.
This commit is contained in:
1 parent
8285400254
commit
5c7c122105
80 files changed
+20074
-1412
No files matched your search
@@ -9,24 +9,25 @@ import {
|
||||
BarChartIcon,
|
||||
SunIcon,
|
||||
MoonIcon,
|
||||
ChevronRightIcon,
|
||||
CpuIcon,
|
||||
LayersIcon,
|
||||
ZapIcon,
|
||||
} from './Icons'
|
||||
|
||||
|
||||
export default function Sidebar({ route, currentProject, theme, onToggleTheme }) {
|
||||
const [health, setHealth] = useState(null)
|
||||
const [collapsed, setCollapsed] = useState(() => localStorage.getItem('sidebar_collapsed') === 'true')
|
||||
|
||||
useEffect(() => {
|
||||
api.health().then(setHealth).catch(() => {})
|
||||
let timer
|
||||
const fetchHealth = () => {
|
||||
api.health().then(setHealth).catch(() => {})
|
||||
}
|
||||
fetchHealth()
|
||||
timer = setInterval(fetchHealth, 3000)
|
||||
return () => clearInterval(timer)
|
||||
}, [])
|
||||
|
||||
const toggleCollapse = () => {
|
||||
setCollapsed((prev) => {
|
||||
const next = !prev
|
||||
localStorage.setItem('sidebar_collapsed', String(next))
|
||||
return next
|
||||
})
|
||||
}
|
||||
|
||||
const pId = currentProject?.id || route.projectId || 1
|
||||
|
||||
@@ -36,73 +37,122 @@ export default function Sidebar({ route, currentProject, theme, onToggleTheme })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`roboflow-sidebar ${collapsed ? 'collapsed' : ''}`}>
|
||||
<div className="sidebar-header">
|
||||
<div className="sidebar-logo">{collapsed ? 'DE' : 'Dataset Enrichment'}</div>
|
||||
<button className="sidebar-collapse-btn" onClick={toggleCollapse} title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}>
|
||||
{collapsed ? <ChevronRightIcon size={14} /> : '❮'}
|
||||
</button>
|
||||
<header className="roboflow-topbar">
|
||||
<div className="topbar-logo" onClick={(e) => handleNav(e, '/projects')} style={{ cursor: 'pointer' }}>
|
||||
<span style={{ fontWeight: 700, fontSize: '0.95rem', letterSpacing: '-0.01em', color: '#f4f4f5' }}>
|
||||
Dataset Enrichment
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="sidebar-section">
|
||||
{!collapsed && <div className="sidebar-section-title">WORKSPACE</div>}
|
||||
<a href="#/projects" onClick={(e) => handleNav(e, '/projects')} className={`sidebar-item ${route.name === 'projects' ? 'active' : ''}`} title="Projects">
|
||||
<span className="sidebar-icon"><FolderIcon size={16} /></span>
|
||||
{!collapsed && <span>Projects</span>}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="sidebar-section">
|
||||
{!collapsed && <div className="sidebar-section-title">DATA</div>}
|
||||
<a href={`#/projects/${pId}`} onClick={(e) => handleNav(e, `/projects/${pId}`)} className={`sidebar-item ${route.name === 'library' ? 'active' : ''}`} title="Video Archive">
|
||||
<span className="sidebar-icon"><DatabaseIcon size={16} /></span>
|
||||
{!collapsed && <span>Video Archive</span>}
|
||||
</a>
|
||||
<nav className="topbar-nav">
|
||||
<a
|
||||
href={route.batchId ? `#/batches/${route.batchId}` : `#/projects/${pId}`}
|
||||
onClick={(e) => handleNav(e, route.batchId ? `/batches/${route.batchId}` : `/projects/${pId}`)}
|
||||
className={`sidebar-item ${route.name === 'review' || route.name === 'trim' ? 'active' : ''}`}
|
||||
title="Annotate / Review"
|
||||
href="#/projects"
|
||||
onClick={(e) => handleNav(e, '/projects')}
|
||||
className={`topbar-item ${route.name === 'projects' ? 'active' : ''}`}
|
||||
>
|
||||
<span className="sidebar-icon"><TagIcon size={16} /></span>
|
||||
{!collapsed && <span>Annotate / Review</span>}
|
||||
<FolderIcon size={15} />
|
||||
<span>Projects</span>
|
||||
</a>
|
||||
<a href={`#/projects/${pId}/models`} onClick={(e) => handleNav(e, `/projects/${pId}/models`)} className={`sidebar-item ${route.name === 'models' ? 'active' : ''}`} title="Master Dataset">
|
||||
<span className="sidebar-icon"><BarChartIcon size={16} /></span>
|
||||
{!collapsed && <span>Master Dataset</span>}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="sidebar-section">
|
||||
{!collapsed && <div className="sidebar-section-title">MODELS & TOOLS</div>}
|
||||
<a href={`#/projects/${pId}/models`} onClick={(e) => handleNav(e, `/projects/${pId}/models`)} className={`sidebar-item ${route.name === 'models' ? 'active' : ''}`} title="Train & Select Engine">
|
||||
<span className="sidebar-icon"><RocketIcon size={16} /></span>
|
||||
{!collapsed && <span>Train & Select Engine</span>}
|
||||
<a
|
||||
href={`#/projects/${pId}`}
|
||||
onClick={(e) => handleNav(e, `/projects/${pId}`)}
|
||||
className={`topbar-item ${route.name === 'library' ? 'active' : ''}`}
|
||||
>
|
||||
<DatabaseIcon size={15} />
|
||||
<span>Video Archive</span>
|
||||
</a>
|
||||
<a href="#/sam3-playground" onClick={(e) => handleNav(e, '/sam3-playground')} className={`sidebar-item ${route.name === 'sam3-playground' ? 'active' : ''}`} title="SAM3 Playground">
|
||||
<span className="sidebar-icon">🤖</span>
|
||||
{!collapsed && <span>SAM3 Playground</span>}
|
||||
|
||||
<a
|
||||
href={`#/projects/${pId}/batches`}
|
||||
onClick={(e) => handleNav(e, `/projects/${pId}/batches`)}
|
||||
className={`topbar-item ${route.name === 'batches' || route.name === 'review' ? 'active' : ''}`}
|
||||
>
|
||||
<TagIcon size={15} />
|
||||
<span>Batches</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="sidebar-spacer" />
|
||||
<a
|
||||
href={`#/projects/${pId}/data-prep`}
|
||||
onClick={(e) => handleNav(e, `/projects/${pId}/data-prep`)}
|
||||
className={`topbar-item ${route.name === 'data-prep' ? 'active' : ''}`}
|
||||
>
|
||||
<BarChartIcon size={15} />
|
||||
<span>Data Preparation</span>
|
||||
</a>
|
||||
|
||||
<div className="sidebar-footer">
|
||||
<button className="sidebar-theme-toggle" onClick={onToggleTheme} title={theme === 'dark' ? 'Switch to Light Mode' : 'Switch to Dark Mode'}>
|
||||
{collapsed ? (theme === 'dark' ? <SunIcon size={14} /> : <MoonIcon size={14} />) : (theme === 'dark' ? 'Light Mode' : 'Dark Mode')}
|
||||
</button>
|
||||
{health && !collapsed && (
|
||||
<div className="sidebar-health">
|
||||
<div className="health-item" title={health.gpu || ''} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', display: 'block' }}>
|
||||
<span style={{ float: 'left' }}>GPU:</span>
|
||||
<span style={{ float: 'right' }}>{health.gpu ? health.gpu.replace('NVIDIA GeForce ', '').replace(' Laptop GPU', '') : 'N/A'}</span>
|
||||
</div>
|
||||
<div className="health-item"><span>VRAM:</span> <span>{health.vram_free_gb ? `${health.vram_free_gb.toFixed(1)}GB` : 'N/A'}</span></div>
|
||||
<div className="health-item"><span>SAM3:</span> <span>{health.sam3_ready ? 'Ready' : 'Not Ready'}</span></div>
|
||||
<div className="health-item"><span>FFmpeg:</span> <span>{health.ffmpeg ? 'OK' : 'Error'}</span></div>
|
||||
|
||||
|
||||
<a
|
||||
href={`#/projects/${pId}/datasets`}
|
||||
onClick={(e) => handleNav(e, `/projects/${pId}/datasets`)}
|
||||
className={`topbar-item ${route.name === 'datasets' ? 'active' : ''}`}
|
||||
>
|
||||
<LayersIcon size={15} />
|
||||
<span>Datasets</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={`#/projects/${pId}/models`}
|
||||
onClick={(e) => handleNav(e, `/projects/${pId}/models`)}
|
||||
className={`topbar-item ${route.name === 'models' ? 'active' : ''}`}
|
||||
>
|
||||
<RocketIcon size={15} />
|
||||
<span>Models & Training</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={`#/projects/${pId}/live-count`}
|
||||
onClick={(e) => handleNav(e, `/projects/${pId}/live-count`)}
|
||||
className={`topbar-item ${route.name === 'live-count' ? 'active' : ''}`}
|
||||
>
|
||||
<ZapIcon size={15} />
|
||||
<span>Live Counting</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={`#/projects/${pId}/counting-bench`}
|
||||
onClick={(e) => handleNav(e, `/projects/${pId}/counting-bench`)}
|
||||
className={`topbar-item ${route.name === 'counting-bench' ? 'active' : ''}`}
|
||||
>
|
||||
<DatabaseIcon size={15} />
|
||||
<span>Counting Accuracy</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="#/sam3-playground"
|
||||
onClick={(e) => handleNav(e, '/sam3-playground')}
|
||||
className={`topbar-item ${route.name === 'sam3-playground' ? 'active' : ''}`}
|
||||
>
|
||||
<CpuIcon size={15} />
|
||||
<span>SAM3 Playground</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div className="topbar-right">
|
||||
{health && (
|
||||
<div className="topbar-health">
|
||||
<span className="health-badge" title={health.gpu || ''}>
|
||||
GPU: {health.gpu ? health.gpu.replace('NVIDIA GeForce ', '').replace(' Laptop GPU', '') : 'CPU'}
|
||||
</span>
|
||||
<span className="health-badge">
|
||||
VRAM: {health.vram_free_gb ? `${health.vram_free_gb.toFixed(1)}GB` : 'N/A'}
|
||||
</span>
|
||||
<span className={`health-badge ${health.sam3_ready ? 'ready' : ''}`}>
|
||||
SAM3: {health.sam3_ready ? 'Ready' : 'Off'}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
className="topbar-theme-toggle"
|
||||
onClick={onToggleTheme}
|
||||
title={theme === 'dark' ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
|
||||
>
|
||||
{theme === 'dark' ? <SunIcon size={15} /> : <MoonIcon size={15} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user