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

158 lines
4.9 KiB
React
Raw Normal View History

import { useState } from 'react'
import {
BACKLOG_STATUSES,
BACKLOG_STATUS_LABELS,
} from '../constants/status.js'
import { StatusBadge } from './StatusBadge.jsx'
import { PriorityBadge } from './PriorityBadge.jsx'
import { EmptyState } from './EmptyState.jsx'
import { GateSelect, gateTitleById } from './GateSelect.jsx'
export function BacklogSection({
items,
roadmapItems = [],
canManage,
onCreate,
onUpdateGate,
onUpdateStatus,
onConvert,
onDelete,
busy,
}) {
const [title, setTitle] = useState('')
const [gateId, setGateId] = useState('')
const [showForm, setShowForm] = useState(false)
async function handleSubmit(e) {
e.preventDefault()
if (!title.trim()) return
await onCreate({
title: title.trim(),
roadmap_item_id: gateId || undefined,
})
setTitle('')
setGateId('')
setShowForm(false)
}
return (
<section className="card">
<div className="section-header">
<h2>Backlog</h2>
{canManage && (
<button
type="button"
className="btn btn-primary btn-block-mobile"
onClick={() => setShowForm((v) => !v)}
>
{showForm ? 'Abbrechen' : 'Backlog-Item'}
</button>
)}
</div>
{showForm && canManage && (
<form className="inline-form-block" onSubmit={handleSubmit}>
<label>
Titel
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
maxLength={255}
required
/>
</label>
{roadmapItems.length > 0 && (
<label>
Zielzustand (Gate)
<select value={gateId} onChange={(e) => setGateId(e.target.value)}>
<option value="">— keins —</option>
{roadmapItems.map((item) => (
<option key={item.id} value={item.id}>
{item.title}
</option>
))}
</select>
</label>
)}
<button type="submit" className="btn btn-primary" disabled={busy}>
Anlegen
</button>
</form>
)}
{items.length === 0 && <EmptyState message="Backlog ist leer." />}
<ul className="item-list">
{items.map((item) => (
<li key={item.id} className="list-item card-list-item">
<div className="list-item-main">
<strong>{item.title}</strong>
{item.description && (
<p className="list-item-desc">{item.description}</p>
)}
{item.roadmap_item_id && (
<p className="list-item-sub muted">
Gate: {gateTitleById(roadmapItems, item.roadmap_item_id) || item.roadmap_item_id}
</p>
)}
</div>
<div className="list-item-meta action-controls">
<StatusBadge kind="backlog" status={item.status} />
<PriorityBadge priority={item.priority} />
{canManage && item.status !== 'converted' && roadmapItems.length > 0 && (
<select
className="inline-select"
value={item.roadmap_item_id || ''}
onChange={(e) => onUpdateGate(item.id, e.target.value)}
aria-label="Gate-Zuordnung"
>
<option value="">Gate …</option>
{roadmapItems.map((gate) => (
<option key={gate.id} value={gate.id}>
{gate.title}
</option>
))}
</select>
)}
{canManage && item.status !== 'converted' && (
<>
<select
className="inline-select"
value={item.status}
onChange={(e) => onUpdateStatus(item.id, e.target.value)}
aria-label="Backlog-Status"
>
{BACKLOG_STATUSES.filter((s) => s !== 'converted').map((s) => (
<option key={s} value={s}>
{BACKLOG_STATUS_LABELS[s]}
</option>
))}
</select>
{item.status === 'accepted' && (
<button
type="button"
className="btn btn-primary"
onClick={() => onConvert(item.id)}
disabled={busy}
>
In Maßnahme umwandeln
</button>
)}
<button
type="button"
className="btn btn-secondary"
onClick={() => onDelete(item.id)}
>
Löschen
</button>
</>
)}
</div>
</li>
))}
</ul>
</section>
)
}