Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)
Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
77 lines
2.2 KiB
TypeScript
77 lines
2.2 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
|
|
interface JurorProgress {
|
|
completed: number;
|
|
total: number;
|
|
}
|
|
|
|
/**
|
|
* Displays juror voting progress (X/11 teams completed per juror).
|
|
* Fetches from GET /api/progress. No individual scores shown.
|
|
*/
|
|
export default function ProgressMatrix() {
|
|
const [progress, setProgress] = useState<Record<string, JurorProgress>>({});
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
fetchProgress();
|
|
const interval = setInterval(fetchProgress, 5000);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
function fetchProgress() {
|
|
fetch('/api/progress')
|
|
.then((res) => res.json())
|
|
.then((data) => {
|
|
setProgress(data);
|
|
setLoading(false);
|
|
})
|
|
.catch(() => setLoading(false));
|
|
}
|
|
|
|
if (loading) {
|
|
return <div className="progress-loading">Lade Fortschritt...</div>;
|
|
}
|
|
|
|
const entries = Object.entries(progress);
|
|
const allComplete = entries.length > 0 && entries.every(([, p]) => p.completed === p.total);
|
|
|
|
return (
|
|
<div className="progress-matrix">
|
|
<h3 className="progress-title">Fortschritt</h3>
|
|
|
|
{allComplete && (
|
|
<div className="progress-complete-badge">
|
|
✓ Alle Bewertungen abgeschlossen
|
|
</div>
|
|
)}
|
|
|
|
<div className="progress-list">
|
|
{entries.map(([jurorId, { completed, total }]) => {
|
|
const percent = total > 0 ? (completed / total) * 100 : 0;
|
|
const isComplete = completed === total;
|
|
|
|
return (
|
|
<div key={jurorId} className="progress-item">
|
|
<div className="progress-item-header">
|
|
<span className="progress-juror-name">
|
|
{jurorId.charAt(0).toUpperCase() + jurorId.slice(1)}
|
|
</span>
|
|
<span className={`progress-count ${isComplete ? 'complete' : ''}`}>
|
|
{completed}/{total}
|
|
</span>
|
|
</div>
|
|
<div className="progress-bar-track">
|
|
<div
|
|
className={`progress-bar-fill ${isComplete ? 'complete' : ''}`}
|
|
style={{ width: `${percent}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|