All checks were successful
Deploy Development / deploy (push) Successful in 46s
Test Suite / pytest-backend (push) Successful in 4m13s
Test Suite / lint-backend (push) Successful in 3s
Test Suite / compose-smoke (push) Has been skipped
Test Suite / k6 /api/health Baseline (push) Successful in 19s
Test Suite / playwright-smoke (push) Successful in 12s
Methodenuebergreifende Provider auf Kernel v0.3; verbindliche Coding Rules fuer Agenten in ADP und .cursor/rules. Co-authored-by: Cursor <cursoragent@cursor.com>
108 lines
3.9 KiB
JavaScript
108 lines
3.9 KiB
JavaScript
import {
|
|
PROPOSAL_UI_CONFIG,
|
|
filterSprintProposals,
|
|
listProposalKeys,
|
|
proposalReasonLabel,
|
|
} from '../utils/steeringProposals.js'
|
|
import { backlogKindLabel, resolveBacklogVocabulary } from '../utils/resolveBacklogVocabulary.js'
|
|
import { PriorityBadge } from './PriorityBadge.jsx'
|
|
|
|
export function SteeringProposalsPanel({
|
|
proposalsByKey = {},
|
|
proposalKeys = null,
|
|
backlogVocabulary = null,
|
|
canManage = false,
|
|
onAcceptBacklogProposal,
|
|
busy = false,
|
|
filterContext = {},
|
|
}) {
|
|
const vocabulary = resolveBacklogVocabulary(backlogVocabulary)
|
|
const keys = proposalKeys || listProposalKeys(proposalsByKey)
|
|
|
|
if (!keys.length) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{keys.map((proposalKey) => {
|
|
const config = PROPOSAL_UI_CONFIG[proposalKey] || {
|
|
title: proposalKey,
|
|
lead: 'Steuerungsvorschlag',
|
|
acceptLabel: 'Übernehmen',
|
|
scopeTypes: ['backlog_item'],
|
|
}
|
|
let items = proposalsByKey[proposalKey] || []
|
|
if (proposalKey === 'sprint_commit' && filterContext.workCycleId) {
|
|
items = filterSprintProposals(items, filterContext.workCycleId)
|
|
}
|
|
if (!items.length) return null
|
|
const ranker = items[0]?.ranker_key || 'heuristic_v0'
|
|
|
|
return (
|
|
<section key={proposalKey} className="card steering-proposals">
|
|
<div className="section-header">
|
|
<div>
|
|
<h2>{config.title}</h2>
|
|
<p className="section-lead muted">
|
|
{config.lead} Ranker: {ranker}.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<ol className="item-list steering-proposals__list">
|
|
{items.map((proposal) => (
|
|
<li
|
|
key={`${proposalKey}-${proposal.scope_type}-${proposal.scope_id}`}
|
|
className="list-item card-list-item steering-proposal"
|
|
>
|
|
<div className="list-item-main">
|
|
<strong>
|
|
{proposal.rank}. {proposal.title}
|
|
</strong>
|
|
<p className="list-item-sub muted">{proposalReasonLabel(proposal)}</p>
|
|
{proposal.dependency_blocked && (
|
|
<p className="list-item-sub muted steering-proposal__blocked">
|
|
Abhängigkeit offen
|
|
</p>
|
|
)}
|
|
{proposal.scope_type === 'roadmap_item' && (
|
|
<p className="list-item-sub muted">
|
|
Gate ohne Plan — Arbeitspaket anlegen oder Eingang committen
|
|
</p>
|
|
)}
|
|
</div>
|
|
<div className="list-item-meta action-controls">
|
|
{proposal.item_kind && (
|
|
<span className="badge badge--kind muted">
|
|
{backlogKindLabel(vocabulary, proposal.item_kind)}
|
|
</span>
|
|
)}
|
|
{proposal.priority && <PriorityBadge priority={proposal.priority} />}
|
|
{canManage
|
|
&& proposal.scope_type === 'backlog_item'
|
|
&& config.scopeTypes.includes('backlog_item') && (
|
|
<button
|
|
type="button"
|
|
className="btn btn-primary btn-sm"
|
|
disabled={busy || proposal.dependency_blocked}
|
|
onClick={() =>
|
|
onAcceptBacklogProposal?.(proposal.scope_id, {
|
|
work_cycle_id: proposal.work_cycle_id,
|
|
proposal_key: proposalKey,
|
|
})
|
|
}
|
|
>
|
|
{config.acceptLabel}
|
|
</button>
|
|
)}
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ol>
|
|
</section>
|
|
)
|
|
})}
|
|
</>
|
|
)
|
|
}
|