fix: Sprint-AP Bearbeiten, Sprint-Verschiebung und klarere Aktionsleiste.
Some checks failed
Test Suite / pytest-backend (push) Waiting to run
Test Suite / lint-backend (push) Waiting to run
Test Suite / compose-smoke (push) Waiting to run
Test Suite / k6 /api/health Baseline (push) Blocked by required conditions
Test Suite / playwright-smoke (push) Blocked by required conditions
Deploy Development / deploy (push) Has been cancelled
Some checks failed
Test Suite / pytest-backend (push) Waiting to run
Test Suite / lint-backend (push) Waiting to run
Test Suite / compose-smoke (push) Waiting to run
Test Suite / k6 /api/health Baseline (push) Blocked by required conditions
Test Suite / playwright-smoke (push) Blocked by required conditions
Deploy Development / deploy (push) Has been cancelled
Behebt editingActionId-Bug in Plan/Work-Sprint; Schnellzuweisung AP zu anderem Sprint ohne Voll-Reload. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
04d1dbf1f9
commit
ee1e1c0671
|
|
@ -3,6 +3,7 @@ import { ACTION_STATUS_LABELS, PRIORITY_LABELS } from '../constants/status.js'
|
||||||
import { ActorSelect } from './ActorSelect.jsx'
|
import { ActorSelect } from './ActorSelect.jsx'
|
||||||
import { GateSelect } from './GateSelect.jsx'
|
import { GateSelect } from './GateSelect.jsx'
|
||||||
import { flattenLeafProjectOptions, getLeafProjects } from '../utils/projectTree.js'
|
import { flattenLeafProjectOptions, getLeafProjects } from '../utils/projectTree.js'
|
||||||
|
import { PLANNING_SPRINT_STATUSES } from '../utils/workCycleActions.js'
|
||||||
|
|
||||||
function isoToLocalInput(iso) {
|
function isoToLocalInput(iso) {
|
||||||
if (!iso) return ''
|
if (!iso) return ''
|
||||||
|
|
@ -47,7 +48,9 @@ export function ActionForm({
|
||||||
const defaultActors = initial.assigned_actor_ids || []
|
const defaultActors = initial.assigned_actor_ids || []
|
||||||
const leafProjects = getLeafProjects(projects)
|
const leafProjects = getLeafProjects(projects)
|
||||||
const projectOptions = flattenLeafProjectOptions(projects)
|
const projectOptions = flattenLeafProjectOptions(projects)
|
||||||
const cycleOptions = workCycles.filter((c) => c.status !== 'completed')
|
const cycleOptions = workCycles.filter(
|
||||||
|
(c) => PLANNING_SPRINT_STATUSES.has(c.status) || c.id === initial.work_cycle_id,
|
||||||
|
)
|
||||||
const defaultCycleId =
|
const defaultCycleId =
|
||||||
initial.work_cycle_id || (activeWorkCycle?.id && !initial.id ? activeWorkCycle.id : '')
|
initial.work_cycle_id || (activeWorkCycle?.id && !initial.id ? activeWorkCycle.id : '')
|
||||||
|
|
||||||
|
|
@ -108,13 +111,19 @@ export function ActionForm({
|
||||||
/>
|
/>
|
||||||
{cycleOptions.length > 0 && (
|
{cycleOptions.length > 0 && (
|
||||||
<label>
|
<label>
|
||||||
Sprint (optional)
|
Sprint
|
||||||
<select name="work_cycle_id" defaultValue={defaultCycleId}>
|
<select name="work_cycle_id" defaultValue={defaultCycleId}>
|
||||||
<option value="">— kein Sprint —</option>
|
<option value="">— kein Sprint —</option>
|
||||||
{cycleOptions.map((cycle) => (
|
{cycleOptions.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)'
|
||||||
|
: cycle.status === 'reached'
|
||||||
|
? ' (abgeschlossen)'
|
||||||
|
: ''}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { PriorityBadge } from './PriorityBadge.jsx'
|
||||||
import { ActionForm } from './ActionForm.jsx'
|
import { ActionForm } from './ActionForm.jsx'
|
||||||
import { gateTitleById } from './GateSelect.jsx'
|
import { gateTitleById } from './GateSelect.jsx'
|
||||||
import { actionTitleById } from '../utils/actionReferences.js'
|
import { actionTitleById } from '../utils/actionReferences.js'
|
||||||
|
import { SprintTransferSelect } from './SprintTransferSelect.jsx'
|
||||||
|
|
||||||
function formatDue(iso) {
|
function formatDue(iso) {
|
||||||
if (!iso) return null
|
if (!iso) return null
|
||||||
|
|
@ -45,6 +46,7 @@ export function ActionHubCard({
|
||||||
onQuickStatus,
|
onQuickStatus,
|
||||||
onCreateBlocker,
|
onCreateBlocker,
|
||||||
onUnplanToBacklog,
|
onUnplanToBacklog,
|
||||||
|
onMoveToSprint,
|
||||||
canManage,
|
canManage,
|
||||||
canManageBlocker,
|
canManageBlocker,
|
||||||
formBusy,
|
formBusy,
|
||||||
|
|
@ -123,6 +125,14 @@ export function ActionHubCard({
|
||||||
{action.assigned_actor_ids?.length > 0 && (
|
{action.assigned_actor_ids?.length > 0 && (
|
||||||
<span>{action.assigned_actor_ids.length} zugewiesen</span>
|
<span>{action.assigned_actor_ids.length} zugewiesen</span>
|
||||||
)}
|
)}
|
||||||
|
{action.work_cycle_id && (
|
||||||
|
<span>
|
||||||
|
Sprint:{' '}
|
||||||
|
{workCycles.find((c) => c.id === action.work_cycle_id)?.title ||
|
||||||
|
activeWorkCycle?.title ||
|
||||||
|
'…'}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{due && <span>Fällig: {due}</span>}
|
{due && <span>Fällig: {due}</span>}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
@ -169,6 +179,7 @@ export function ActionHubCard({
|
||||||
|
|
||||||
{canManage && (
|
{canManage && (
|
||||||
<footer className="action-hub-card-actions">
|
<footer className="action-hub-card-actions">
|
||||||
|
<div className="action-hub-card-actions__primary">
|
||||||
<select
|
<select
|
||||||
className="inline-select"
|
className="inline-select"
|
||||||
value={action.status}
|
value={action.status}
|
||||||
|
|
@ -181,6 +192,16 @@ export function ActionHubCard({
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
{typeof onMoveToSprint === 'function' && (
|
||||||
|
<SprintTransferSelect
|
||||||
|
action={action}
|
||||||
|
workCycles={workCycles}
|
||||||
|
busy={formBusy}
|
||||||
|
onMove={onMoveToSprint}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="action-hub-card-actions__secondary">
|
||||||
{canManageBlocker && action.status !== 'done' && (
|
{canManageBlocker && action.status !== 'done' && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -216,6 +237,7 @@ export function ActionHubCard({
|
||||||
Details
|
Details
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
)}
|
)}
|
||||||
</article>
|
</article>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ export function InitiativeActionsHub({
|
||||||
onQuickStatus,
|
onQuickStatus,
|
||||||
onCreateBlockerForAction,
|
onCreateBlockerForAction,
|
||||||
onUnplanToBacklog,
|
onUnplanToBacklog,
|
||||||
|
onMoveToSprint,
|
||||||
canManage,
|
canManage,
|
||||||
canManageBlocker,
|
canManageBlocker,
|
||||||
formBusy,
|
formBusy,
|
||||||
|
|
@ -32,6 +33,7 @@ export function InitiativeActionsHub({
|
||||||
onReloadActors,
|
onReloadActors,
|
||||||
sectionTitle = 'Arbeitspakete',
|
sectionTitle = 'Arbeitspakete',
|
||||||
sectionLead = 'Committete operative Einheit — Aufgaben im Detail. Blocker, Nachweise und Reviews hängen am Arbeitspaket.',
|
sectionLead = 'Committete operative Einheit — Aufgaben im Detail. Blocker, Nachweise und Reviews hängen am Arbeitspaket.',
|
||||||
|
allActions = [],
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="card initiative-actions-hub">
|
<section className="card initiative-actions-hub">
|
||||||
|
|
@ -98,6 +100,7 @@ export function InitiativeActionsHub({
|
||||||
onQuickStatus={(status) => onQuickStatus(action, status)}
|
onQuickStatus={(status) => onQuickStatus(action, status)}
|
||||||
onCreateBlocker={() => onCreateBlockerForAction(action.id)}
|
onCreateBlocker={() => onCreateBlockerForAction(action.id)}
|
||||||
onUnplanToBacklog={onUnplanToBacklog}
|
onUnplanToBacklog={onUnplanToBacklog}
|
||||||
|
onMoveToSprint={onMoveToSprint}
|
||||||
canManage={canManage}
|
canManage={canManage}
|
||||||
canManageBlocker={canManageBlocker}
|
canManageBlocker={canManageBlocker}
|
||||||
formBusy={formBusy}
|
formBusy={formBusy}
|
||||||
|
|
@ -111,7 +114,7 @@ export function InitiativeActionsHub({
|
||||||
roadmapItems={roadmapItems}
|
roadmapItems={roadmapItems}
|
||||||
workCycles={workCycles}
|
workCycles={workCycles}
|
||||||
activeWorkCycle={activeWorkCycle}
|
activeWorkCycle={activeWorkCycle}
|
||||||
allActions={actions}
|
allActions={allActions.length ? allActions : actions}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
46
frontend/src/components/SprintTransferSelect.jsx
Normal file
46
frontend/src/components/SprintTransferSelect.jsx
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { listCarryoverTargetSprints } from '../utils/workCycleActions.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schnellzuweisung: AP in einen anderen Sprint verschieben.
|
||||||
|
*/
|
||||||
|
export function SprintTransferSelect({
|
||||||
|
action,
|
||||||
|
workCycles = [],
|
||||||
|
busy = false,
|
||||||
|
onMove,
|
||||||
|
className = '',
|
||||||
|
}) {
|
||||||
|
if (!action?.work_cycle_id || action.status === 'done' || action.status === 'discarded') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const targets = listCarryoverTargetSprints(workCycles, action.work_cycle_id)
|
||||||
|
if (targets.length === 0) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<label className={`sprint-transfer-select ${className}`.trim()}>
|
||||||
|
<span className="sr-only">In anderen Sprint verschieben</span>
|
||||||
|
<select
|
||||||
|
className="inline-select"
|
||||||
|
value=""
|
||||||
|
disabled={busy}
|
||||||
|
aria-label={`${action.title} in anderen Sprint verschieben`}
|
||||||
|
onChange={(e) => {
|
||||||
|
const targetId = e.target.value
|
||||||
|
if (targetId && typeof onMove === 'function') {
|
||||||
|
onMove(action, targetId)
|
||||||
|
}
|
||||||
|
e.target.value = ''
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="">→ Sprint …</option>
|
||||||
|
{targets.map((cycle) => (
|
||||||
|
<option key={cycle.id} value={cycle.id}>
|
||||||
|
{cycle.title}
|
||||||
|
{cycle.status === 'active' ? ' (aktiv)' : cycle.status === 'planned' ? ' (geplant)' : ''}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -118,7 +118,7 @@ export function InitiativeOperationsProvider({ children, initiativeId: initiativ
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const [showActionForm, setShowActionForm] = useState(false)
|
const [showActionForm, setShowActionForm] = useState(false)
|
||||||
const [editingAction, setEditingAction] = useState(null)
|
const [editingActionId, setEditingActionId] = useState(null)
|
||||||
const [formBusy, setFormBusy] = useState(false)
|
const [formBusy, setFormBusy] = useState(false)
|
||||||
const [hideDone, setHideDone] = useState(true)
|
const [hideDone, setHideDone] = useState(true)
|
||||||
const [selectedWorkCycleId, setSelectedWorkCycleId] = useState('')
|
const [selectedWorkCycleId, setSelectedWorkCycleId] = useState('')
|
||||||
|
|
@ -364,7 +364,7 @@ export function InitiativeOperationsProvider({ children, initiativeId: initiativ
|
||||||
if (capabilities.has('kairo.action.manage')) {
|
if (capabilities.has('kairo.action.manage')) {
|
||||||
await setActionAssignments(actionId, payload.assigned_actor_ids || [])
|
await setActionAssignments(actionId, payload.assigned_actor_ids || [])
|
||||||
}
|
}
|
||||||
setEditingAction(null)
|
setEditingActionId(null)
|
||||||
await load()
|
await load()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err.message)
|
setError(err.message)
|
||||||
|
|
@ -373,6 +373,20 @@ export function InitiativeOperationsProvider({ children, initiativeId: initiativ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleMoveActionToSprint(action, targetCycleId) {
|
||||||
|
if (!action?.id || !targetCycleId || action.work_cycle_id === targetCycleId) return
|
||||||
|
setFormBusy(true)
|
||||||
|
setError(null)
|
||||||
|
try {
|
||||||
|
const updated = await updateAction(action.id, { work_cycle_id: targetCycleId })
|
||||||
|
setActions((prev) => prev.map((a) => (a.id === action.id ? updated : a)))
|
||||||
|
} catch (err) {
|
||||||
|
setError(err.message)
|
||||||
|
} finally {
|
||||||
|
setFormBusy(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleQuickStatus(action, status) {
|
async function handleQuickStatus(action, status) {
|
||||||
if (!capabilities.has('kairo.action.manage')) return
|
if (!capabilities.has('kairo.action.manage')) return
|
||||||
try {
|
try {
|
||||||
|
|
@ -966,8 +980,8 @@ export function InitiativeOperationsProvider({ children, initiativeId: initiativ
|
||||||
error,
|
error,
|
||||||
showActionForm,
|
showActionForm,
|
||||||
setShowActionForm,
|
setShowActionForm,
|
||||||
editingAction,
|
editingActionId,
|
||||||
setEditingAction,
|
setEditingActionId,
|
||||||
formBusy,
|
formBusy,
|
||||||
hideDone,
|
hideDone,
|
||||||
setHideDone,
|
setHideDone,
|
||||||
|
|
@ -983,6 +997,7 @@ export function InitiativeOperationsProvider({ children, initiativeId: initiativ
|
||||||
handleMethodProfileChange,
|
handleMethodProfileChange,
|
||||||
handleCreateAction,
|
handleCreateAction,
|
||||||
handleUpdateAction,
|
handleUpdateAction,
|
||||||
|
handleMoveActionToSprint,
|
||||||
handleQuickStatus,
|
handleQuickStatus,
|
||||||
handleCreateBlocker,
|
handleCreateBlocker,
|
||||||
handleCreateBlockerForAction,
|
handleCreateBlockerForAction,
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ export function InitiativeExecutionPage() {
|
||||||
setHideDone,
|
setHideDone,
|
||||||
showActionForm,
|
showActionForm,
|
||||||
setShowActionForm,
|
setShowActionForm,
|
||||||
editingAction,
|
editingActionId,
|
||||||
setEditingAction,
|
setEditingActionId,
|
||||||
capabilities,
|
capabilities,
|
||||||
formBusy,
|
formBusy,
|
||||||
unlinkedBlockers,
|
unlinkedBlockers,
|
||||||
|
|
@ -63,11 +63,11 @@ export function InitiativeExecutionPage() {
|
||||||
showForm={showActionForm}
|
showForm={showActionForm}
|
||||||
onToggleForm={() => {
|
onToggleForm={() => {
|
||||||
setShowActionForm((v) => !v)
|
setShowActionForm((v) => !v)
|
||||||
setEditingAction(null)
|
setEditingActionId(null)
|
||||||
}}
|
}}
|
||||||
editingActionId={editingAction}
|
editingActionId={editingActionId}
|
||||||
onEditAction={setEditingAction}
|
onEditAction={setEditingActionId}
|
||||||
onCancelEdit={() => setEditingAction(null)}
|
onCancelEdit={() => setEditingActionId(null)}
|
||||||
onCreateAction={ops.handleCreateAction}
|
onCreateAction={ops.handleCreateAction}
|
||||||
onUpdateAction={ops.handleUpdateAction}
|
onUpdateAction={ops.handleUpdateAction}
|
||||||
onQuickStatus={ops.handleQuickStatus}
|
onQuickStatus={ops.handleQuickStatus}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ function PlanSprintInner() {
|
||||||
formBusy,
|
formBusy,
|
||||||
showActionForm,
|
showActionForm,
|
||||||
setShowActionForm,
|
setShowActionForm,
|
||||||
editingAction,
|
editingActionId,
|
||||||
setEditingAction,
|
setEditingActionId,
|
||||||
actionContextById,
|
actionContextById,
|
||||||
actors,
|
actors,
|
||||||
actorsLoading,
|
actorsLoading,
|
||||||
|
|
@ -45,6 +45,7 @@ function PlanSprintInner() {
|
||||||
handleUpdateAction,
|
handleUpdateAction,
|
||||||
handleQuickStatus,
|
handleQuickStatus,
|
||||||
handleCreateBlockerForAction,
|
handleCreateBlockerForAction,
|
||||||
|
handleMoveActionToSprint,
|
||||||
handleUnplanAction,
|
handleUnplanAction,
|
||||||
} = ops
|
} = ops
|
||||||
|
|
||||||
|
|
@ -102,14 +103,15 @@ function PlanSprintInner() {
|
||||||
onHideDoneChange={setHideDone}
|
onHideDoneChange={setHideDone}
|
||||||
showForm={showActionForm}
|
showForm={showActionForm}
|
||||||
onToggleForm={() => setShowActionForm((v) => !v)}
|
onToggleForm={() => setShowActionForm((v) => !v)}
|
||||||
editingActionId={editingAction?.id}
|
editingActionId={editingActionId}
|
||||||
onEditAction={setEditingAction}
|
onEditAction={setEditingActionId}
|
||||||
onCancelEdit={() => setEditingAction(null)}
|
onCancelEdit={() => setEditingActionId(null)}
|
||||||
onCreateAction={createSprintAction}
|
onCreateAction={createSprintAction}
|
||||||
onUpdateAction={handleUpdateAction}
|
onUpdateAction={handleUpdateAction}
|
||||||
onQuickStatus={handleQuickStatus}
|
onQuickStatus={handleQuickStatus}
|
||||||
onCreateBlockerForAction={handleCreateBlockerForAction}
|
onCreateBlockerForAction={handleCreateBlockerForAction}
|
||||||
onUnplanToBacklog={handleUnplanAction}
|
onUnplanToBacklog={handleUnplanAction}
|
||||||
|
onMoveToSprint={handleMoveActionToSprint}
|
||||||
canManage={capabilities.has('kairo.action.manage')}
|
canManage={capabilities.has('kairo.action.manage')}
|
||||||
canManageBlocker={capabilities.has('kairo.blocker.manage')}
|
canManageBlocker={capabilities.has('kairo.blocker.manage')}
|
||||||
formBusy={formBusy}
|
formBusy={formBusy}
|
||||||
|
|
@ -120,6 +122,7 @@ function PlanSprintInner() {
|
||||||
onReloadActors={reloadActors}
|
onReloadActors={reloadActors}
|
||||||
workCycles={workCycles}
|
workCycles={workCycles}
|
||||||
activeWorkCycle={selectedWorkCycle}
|
activeWorkCycle={selectedWorkCycle}
|
||||||
|
allActions={actions}
|
||||||
sectionTitle={`Sprint-Backlog — ${selectedWorkCycle.title}`}
|
sectionTitle={`Sprint-Backlog — ${selectedWorkCycle.title}`}
|
||||||
sectionLead={sprintLead}
|
sectionLead={sprintLead}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ function WorkSprintInner() {
|
||||||
formBusy,
|
formBusy,
|
||||||
showActionForm,
|
showActionForm,
|
||||||
setShowActionForm,
|
setShowActionForm,
|
||||||
editingAction,
|
editingActionId,
|
||||||
setEditingAction,
|
setEditingActionId,
|
||||||
actionContextById,
|
actionContextById,
|
||||||
actors,
|
actors,
|
||||||
actorsLoading,
|
actorsLoading,
|
||||||
|
|
@ -38,6 +38,8 @@ function WorkSprintInner() {
|
||||||
handleUpdateAction,
|
handleUpdateAction,
|
||||||
handleQuickStatus,
|
handleQuickStatus,
|
||||||
handleCreateBlockerForAction,
|
handleCreateBlockerForAction,
|
||||||
|
handleMoveActionToSprint,
|
||||||
|
handleUnplanAction,
|
||||||
initiative,
|
initiative,
|
||||||
steeringSnapshot,
|
steeringSnapshot,
|
||||||
steeringSnapshotLoading,
|
steeringSnapshotLoading,
|
||||||
|
|
@ -101,13 +103,15 @@ function WorkSprintInner() {
|
||||||
onHideDoneChange={setHideDone}
|
onHideDoneChange={setHideDone}
|
||||||
showForm={showActionForm}
|
showForm={showActionForm}
|
||||||
onToggleForm={() => setShowActionForm((v) => !v)}
|
onToggleForm={() => setShowActionForm((v) => !v)}
|
||||||
editingActionId={editingAction?.id}
|
editingActionId={editingActionId}
|
||||||
onEditAction={setEditingAction}
|
onEditAction={setEditingActionId}
|
||||||
onCancelEdit={() => setEditingAction(null)}
|
onCancelEdit={() => setEditingActionId(null)}
|
||||||
onCreateAction={handleCreateAction}
|
onCreateAction={handleCreateAction}
|
||||||
onUpdateAction={handleUpdateAction}
|
onUpdateAction={handleUpdateAction}
|
||||||
onQuickStatus={handleQuickStatus}
|
onQuickStatus={handleQuickStatus}
|
||||||
onCreateBlockerForAction={handleCreateBlockerForAction}
|
onCreateBlockerForAction={handleCreateBlockerForAction}
|
||||||
|
onMoveToSprint={handleMoveActionToSprint}
|
||||||
|
onUnplanToBacklog={handleUnplanAction}
|
||||||
canManage={capabilities.has('kairo.action.manage')}
|
canManage={capabilities.has('kairo.action.manage')}
|
||||||
canManageBlocker={capabilities.has('kairo.blocker.manage')}
|
canManageBlocker={capabilities.has('kairo.blocker.manage')}
|
||||||
formBusy={formBusy}
|
formBusy={formBusy}
|
||||||
|
|
@ -159,13 +163,15 @@ function WorkSprintInner() {
|
||||||
onHideDoneChange={setHideDone}
|
onHideDoneChange={setHideDone}
|
||||||
showForm={showActionForm}
|
showForm={showActionForm}
|
||||||
onToggleForm={() => setShowActionForm((v) => !v)}
|
onToggleForm={() => setShowActionForm((v) => !v)}
|
||||||
editingActionId={editingAction?.id}
|
editingActionId={editingActionId}
|
||||||
onEditAction={setEditingAction}
|
onEditAction={setEditingActionId}
|
||||||
onCancelEdit={() => setEditingAction(null)}
|
onCancelEdit={() => setEditingActionId(null)}
|
||||||
onCreateAction={createAction}
|
onCreateAction={createAction}
|
||||||
onUpdateAction={handleUpdateAction}
|
onUpdateAction={handleUpdateAction}
|
||||||
onQuickStatus={handleQuickStatus}
|
onQuickStatus={handleQuickStatus}
|
||||||
onCreateBlockerForAction={handleCreateBlockerForAction}
|
onCreateBlockerForAction={handleCreateBlockerForAction}
|
||||||
|
onMoveToSprint={handleMoveActionToSprint}
|
||||||
|
onUnplanToBacklog={handleUnplanAction}
|
||||||
canManage={capabilities.has('kairo.action.manage')}
|
canManage={capabilities.has('kairo.action.manage')}
|
||||||
canManageBlocker={capabilities.has('kairo.blocker.manage')}
|
canManageBlocker={capabilities.has('kairo.blocker.manage')}
|
||||||
formBusy={formBusy}
|
formBusy={formBusy}
|
||||||
|
|
@ -176,9 +182,14 @@ function WorkSprintInner() {
|
||||||
onReloadActors={reloadActors}
|
onReloadActors={reloadActors}
|
||||||
workCycles={ops.workCycles}
|
workCycles={ops.workCycles}
|
||||||
activeWorkCycle={activeWorkCycle}
|
activeWorkCycle={activeWorkCycle}
|
||||||
|
allActions={ops.actions}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!activeWorkCycle && !isProductArchetype && (
|
||||||
|
<EmptyState message="Sprint-Ausführung ist für Product-Vorhaben — wähle ein passendes Vorhaben im Scope." />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -999,13 +999,42 @@
|
||||||
|
|
||||||
.action-hub-card-actions {
|
.action-hub-card-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-direction: column;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
margin-top: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
padding-top: 0.65rem;
|
padding-top: 0.65rem;
|
||||||
border-top: 1px solid var(--jk-border, #e5e7eb);
|
border-top: 1px solid var(--jk-border, #e5e7eb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-hub-card-actions__primary,
|
||||||
|
.action-hub-card-actions__secondary {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-hub-card-actions__primary {
|
||||||
|
padding-bottom: 0.35rem;
|
||||||
|
border-bottom: 1px dashed var(--jk-border, #e5e7eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sprint-transfer-select .inline-select {
|
||||||
|
min-width: 9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-sm {
|
.btn-sm {
|
||||||
padding: 0.25rem 0.55rem;
|
padding: 0.25rem 0.55rem;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user