Kairo-Jinkendo/frontend/src/pages/modes/TeamPage.jsx
Lars 0b57e2ec75 AP2.2b/c: A1-Stufenwechsel, Team-Fix, Agent-Anlage, Steuerungs-Hinweise.
AP2.0e rotiert Recurring bei maturity_stage reached. Maturity-Strategie priorisiert Routinen. Team zeigt Namen und Agent-Formular. Archetyp-Hints in Kontrolle und Stufe-A-Gruppierung bei Anlage.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 16:19:55 +02:00

137 lines
4.4 KiB
JavaScript

import { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import { getActorWorkload } from '../../api/workspace.js'
import { createActor, listActors } from '../../api/actors.js'
import { useSession } from '../../context/SessionContext.jsx'
import { ModeShell } from '../../components/ModeShell.jsx'
import { LoadingState } from '../../components/LoadingState.jsx'
import { ErrorState } from '../../components/ErrorState.jsx'
import { scopedPath } from '../../utils/routes.js'
const ACTOR_TYPE_LABELS = {
human: 'Person',
agent: 'Agent / Vibe-Coder',
working_group: 'Gruppe',
external_system: 'Externes System',
}
export function TeamPage() {
const { hasCapability } = useSession()
const [rows, setRows] = useState(null)
const [actors, setActors] = useState([])
const [error, setError] = useState(null)
const [agentName, setAgentName] = useState('')
const [busy, setBusy] = useState(false)
async function load() {
setError(null)
try {
const [workload, directory] = await Promise.all([
getActorWorkload(),
listActors({ include_inactive: false }),
])
setRows(workload)
setActors(directory)
} catch (err) {
setError(err.message)
}
}
useEffect(() => {
load()
}, [])
async function handleCreateAgent(e) {
e.preventDefault()
const name = agentName.trim()
if (!name) return
setBusy(true)
try {
await createActor({ name, actor_type: 'agent' })
setAgentName('')
await load()
} catch (err) {
setError(err.message)
} finally {
setBusy(false)
}
}
const canManage = hasCapability('kairo.actor.manage')
return (
<div className="app-page">
<ModeShell modeKey="team" />
{rows === null && !error && <LoadingState message="Lade Team …" />}
{error && <ErrorState message={error} onRetry={load} />}
{rows && (
<>
<section className="card">
<h2 className="card-title">Actor-Verzeichnis</h2>
{actors.length === 0 ? (
<p className="muted">Keine Actors im Tenant.</p>
) : (
<ul className="team-workload-list">
{actors.map((actor) => (
<li key={actor.id} className="team-workload-list__item">
<div>
<strong>{actor.name}</strong>
<span className="muted">
{' '}
· {ACTOR_TYPE_LABELS[actor.actor_type] || actor.actor_type}
</span>
</div>
</li>
))}
</ul>
)}
{canManage && (
<form className="form inline-form team-agent-form" onSubmit={handleCreateAgent}>
<label>
Agent anlegen (Vibe-Coder)
<input
value={agentName}
onChange={(e) => setAgentName(e.target.value)}
placeholder="z. B. Cursor Agent"
maxLength={255}
/>
</label>
<button type="submit" className="btn btn-primary btn-sm" disabled={busy}>
{busy ? 'Anlegen …' : 'Agent anlegen'}
</button>
</form>
)}
</section>
<section className="card">
<h2 className="card-title">Auslastung nach Actor</h2>
{rows.length === 0 ? (
<p className="muted">Keine Zuweisungen im Portfolio.</p>
) : (
<ul className="team-workload-list">
{rows.map((row) => (
<li key={row.actor_id} className="team-workload-list__item">
<div>
<strong>{row.display_name || row.actor_name || row.actor_id}</strong>
<span className="muted">
{' '}
· {row.open_actions ?? 0} offen
{(row.blocked_actions ?? 0) > 0
? ` · ${row.blocked_actions} blockiert`
: ''}
</span>
</div>
<Link to={scopedPath('/work/mine')} className="link-inline">
Queue ansehen
</Link>
</li>
))}
</ul>
)}
</section>
</>
)}
</div>
)
}