Kairo-Jinkendo/frontend/src/components/SteeringSnapshotPanel.jsx

138 lines
4.2 KiB
React
Raw Normal View History

import { Link } from 'react-router-dom'
import {
PHASE_LABELS,
PHASE_DESCRIPTIONS,
SIGNAL_LABELS,
NEXT_ACTION_KIND_LABELS,
} from '../constants/operating.js'
import { MILESTONE_STATUS_LABELS } from '../constants/status.js'
function formatDate(iso) {
if (!iso) return '—'
try {
return new Date(iso).toLocaleDateString('de-DE')
} catch {
return iso
}
}
export function SteeringSnapshotPanel({ snapshot, loading, error }) {
if (loading) {
return (
<section className="card steering-snapshot">
<p className="muted">Steuerungszustand wird geladen …</p>
</section>
)
}
if (error || !snapshot) {
return (
<section className="card steering-snapshot steering-snapshot--unavailable">
<div className="section-header">
<h2>Steuerungszustand</h2>
</div>
<p className="muted">
{error || 'Snapshot nicht verfügbar — bitte Seite neu laden (Strg+F5).'}
</p>
</section>
)
}
const {
operating_phase,
phase_signals,
counts,
upcoming_milestones = [],
next_actions = [],
} = snapshot
const phaseLabel = PHASE_LABELS[operating_phase] || operating_phase
const phaseDesc = PHASE_DESCRIPTIONS[operating_phase] || ''
return (
<section className="card steering-snapshot">
<div className="section-header">
<h2>Steuerungszustand</h2>
<span className="badge status-badge status-active">{phaseLabel}</span>
</div>
{phaseDesc && <p className="steering-phase-desc">{phaseDesc}</p>}
{phase_signals?.length > 0 && (
<p className="muted snapshot-signals">
{phase_signals.map((s) => SIGNAL_LABELS[s] || s).join(' · ')}
</p>
)}
{next_actions.length > 0 && (
<div className="steering-next-actions">
<h3>Nächste Schritte für dieses Vorhaben</h3>
<ol className="item-list compact-list">
{next_actions.map((item, i) => (
<li key={`${item.kind}-${i}`} className="list-item card-list-item compact">
<div className="list-item-main">
<span className="next-action-rank">{i + 1}</span>
<strong>{item.title}</strong>
<span className="muted list-item-sub">
{NEXT_ACTION_KIND_LABELS[item.kind] || item.kind}
{item.recommended_action ? ` — ${item.recommended_action}` : ''}
</span>
</div>
</li>
))}
</ol>
</div>
)}
{upcoming_milestones.length > 0 && (
<div className="steering-milestones">
<h3>Meilenstein-Horizont</h3>
<ul className="item-list compact-list">
{upcoming_milestones.map((ms) => (
<li key={ms.id} className="list-item card-list-item compact">
<div className="list-item-main">
<strong>{ms.title}</strong>
<span className="muted list-item-sub">
{MILESTONE_STATUS_LABELS[ms.status] || ms.status}
{ms.target_date ? ` · Ziel: ${formatDate(ms.target_date)}` : ''}
</span>
</div>
</li>
))}
</ul>
</div>
)}
<dl className="snapshot-counts">
<div>
<dt>Offene Maßnahmen</dt>
<dd>{counts.actions_open}</dd>
</div>
<div>
<dt>Blockiert</dt>
<dd>{counts.actions_blocked}</dd>
</div>
<div>
<dt>Review nötig</dt>
<dd>{counts.actions_review_required}</dd>
</div>
<div>
<dt>Offene Blocker</dt>
<dd>{counts.blockers_open}</dd>
</div>
{(counts.unlinked_blockers > 0 || counts.unlinked_evidence > 0) && (
<div className="snapshot-warn">
<dt>Unverknüpft</dt>
<dd>
{counts.unlinked_blockers} Blocker, {counts.unlinked_evidence} Evidence
</dd>
</div>
)}
</dl>
<p className="muted snapshot-hint">
Heuristischer Steuerungszustand — später methodengeführt über Steering Core.
</p>
</section>
)
}