fix: Sprint-Zuordnung im Eingang sichtbarer machen.
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Test Suite / pytest-backend (push) Successful in 2m52s
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 18s
Test Suite / playwright-smoke (push) Successful in 13s

Ziel-Sprint-Dropdown immer wenn Sprint existiert, Setup-Hinweis ohne Sprint, Planen fuer alle commit-faehigen Backlog-Status.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Lars 2026-07-12 19:30:36 +02:00
parent 00567bb097
commit c19b6296ed
2 changed files with 43 additions and 20 deletions

View File

@ -10,6 +10,8 @@ import { computeDropPatches, computeMovePatches, sortByOrder } from '../utils/re
import { useMinWidth } from '../hooks/useMinWidth.js' import { useMinWidth } from '../hooks/useMinWidth.js'
const PLANNING_STATUSES = new Set(['planned', 'active', 'at_risk']) const PLANNING_STATUSES = new Set(['planned', 'active', 'at_risk'])
/** Backend erlaubt Convert für diese Status — UI war nur bei „Freigegeben“ sichtbar. */
const COMMITTABLE_STATUSES = new Set(['accepted', 'triaged', 'new'])
export function BacklogSection({ export function BacklogSection({
items, items,
@ -137,8 +139,8 @@ export function BacklogSection({
const modalTitle = const modalTitle =
modalMode?.kind === 'create' ? 'Backlog-Item anlegen' : 'Backlog-Item bearbeiten' modalMode?.kind === 'create' ? 'Backlog-Item anlegen' : 'Backlog-Item bearbeiten'
const acceptedItems = sortedItems.filter((item) => item.status === 'accepted') const committableItems = sortedItems.filter((item) => COMMITTABLE_STATUSES.has(item.status))
const selectedAccepted = acceptedItems.filter((item) => selectedIds.has(item.id)) const selectedCommittable = committableItems.filter((item) => selectedIds.has(item.id))
function toggleSelected(itemId) { function toggleSelected(itemId) {
setSelectedIds((prev) => { setSelectedIds((prev) => {
@ -163,14 +165,14 @@ export function BacklogSection({
} }
async function handleBulkPlan() { async function handleBulkPlan() {
if (!selectedAccepted.length) return if (!selectedCommittable.length) return
if (typeof onBulkConvert === 'function') { if (typeof onBulkConvert === 'function') {
await onBulkConvert( await onBulkConvert(
selectedAccepted.map((item) => item.id), selectedCommittable.map((item) => item.id),
convertOptions(), convertOptions(),
) )
} else { } else {
for (const item of selectedAccepted) { for (const item of selectedCommittable) {
await onConvert(item.id, convertOptions()) await onConvert(item.id, convertOptions())
} }
} }
@ -195,10 +197,22 @@ export function BacklogSection({
)} )}
</div> </div>
{showSprintPlanning && canManage && acceptedItems.length > 0 && ( {sprintPlanningEnabled && canManage && !showSprintPlanning && (
<div className="backlog-sprint-setup card-list-item">
<p className="backlog-sprint-setup__title">
<strong>Sprint-Planung:</strong> Lege zuerst einen Sprint an.
</p>
<p className="muted backlog-sprint-planning__hint">
Plan Sprint Sprint anlegen (optional Sofort aktivieren). Danach erscheint hier
die Sprint-Auswahl zum Committen aus dem Eingang.
</p>
</div>
)}
{showSprintPlanning && canManage && (
<div className="backlog-sprint-planning card-list-item"> <div className="backlog-sprint-planning card-list-item">
<label className="backlog-sprint-planning__field"> <label className="backlog-sprint-planning__field">
<span className="muted">Sprint-Ziel</span> <span className="muted">Ziel-Sprint</span>
<select <select
value={sprintTargetId} value={sprintTargetId}
onChange={(e) => setSprintTargetId(e.target.value)} onChange={(e) => setSprintTargetId(e.target.value)}
@ -207,33 +221,30 @@ export function BacklogSection({
{planableSprints.map((cycle) => ( {planableSprints.map((cycle) => (
<option key={cycle.id} value={cycle.id}> <option key={cycle.id} value={cycle.id}>
{cycle.title} {cycle.title}
{cycle.status === 'active' ? ' (aktiv)' : ''} {cycle.status === 'active' ? ' (aktiv)' : cycle.status === 'planned' ? ' (geplant)' : ''}
</option> </option>
))} ))}
</select> </select>
</label> </label>
{selectedAccepted.length > 0 && ( {selectedCommittable.length > 0 && (
<button <button
type="button" type="button"
className="btn btn-primary btn-sm" className="btn btn-primary btn-sm"
disabled={busy || !sprintTargetId} disabled={busy || !sprintTargetId}
onClick={handleBulkPlan} onClick={handleBulkPlan}
> >
{selectedAccepted.length} Item{selectedAccepted.length === 1 ? '' : 's'} {convertLabel} {selectedCommittable.length} Item{selectedCommittable.length === 1 ? '' : 's'}{' '}
{convertLabel}
</button> </button>
)} )}
<p className="muted backlog-sprint-planning__hint"> <p className="muted backlog-sprint-planning__hint">
Items markieren und in den gewählten Sprint committen Planung unter Plan Sprint. {committableItems.length === 0
? 'Lege Backlog-Items an — dann per Checkbox markieren und in den Sprint committen.'
: 'Checkbox am Item → „In Sprint planen“. Committen erzeugt ein Arbeitspaket mit Sprint-Zuweisung.'}
</p> </p>
</div> </div>
)} )}
{sprintPlanningEnabled && canManage && acceptedItems.length > 0 && planableSprints.length === 0 && (
<p className="muted backlog-sprint-planning__hint">
Lege zuerst einen Sprint unter Plan Sprint an, um Backlog-Items zu planen.
</p>
)}
{items.length === 0 && <EmptyState message="Backlog ist leer." />} {items.length === 0 && <EmptyState message="Backlog ist leer." />}
<ul className="item-list backlog-reorder-list"> <ul className="item-list backlog-reorder-list">
@ -262,7 +273,7 @@ export function BacklogSection({
</span> </span>
)} )}
{showSprintPlanning && canManage && item.status === 'accepted' && ( {showSprintPlanning && canManage && COMMITTABLE_STATUSES.has(item.status) && (
<label className="backlog-select-checkbox"> <label className="backlog-select-checkbox">
<input <input
type="checkbox" type="checkbox"
@ -304,14 +315,14 @@ export function BacklogSection({
<PriorityBadge priority={item.priority} /> <PriorityBadge priority={item.priority} />
{canManage && item.status !== 'converted' && ( {canManage && item.status !== 'converted' && (
<> <>
{item.status === 'accepted' && ( {COMMITTABLE_STATUSES.has(item.status) && (
<button <button
type="button" type="button"
className="btn btn-primary btn-sm" className="btn btn-primary btn-sm"
onClick={() => handleSingleConvert(item.id)} onClick={() => handleSingleConvert(item.id)}
disabled={busy || (showSprintPlanning && !sprintTargetId)} disabled={busy || (showSprintPlanning && !sprintTargetId)}
> >
{convertLabel} {showSprintPlanning ? convertLabel : 'In Arbeitspaket umwandeln'}
</button> </button>
)} )}
<button <button

View File

@ -670,6 +670,18 @@
background: var(--jk-surface); background: var(--jk-surface);
} }
.backlog-sprint-setup {
margin-bottom: 16px;
padding: 12px;
border-left: 3px solid var(--jk-primary);
background: var(--jk-surface-muted, rgba(0, 0, 0, 0.03));
border-radius: 6px;
}
.backlog-sprint-setup__title {
margin: 0 0 6px;
}
.backlog-sprint-planning { .backlog-sprint-planning {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;