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:
asus committed 2026-08-14 16:28:52 +07:00
1 parent 8285400254
commit 5c7c122105
80 files changed
+20074 -1412

No files matched your search

+115 -65
View File
@@ -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>
)
}