fix(AP0.8): UI-Sichtbarkeit Attention und OM-Sektionen verbessern
All checks were successful
Deploy Development / deploy (push) Successful in 45s
Test Suite / pytest-backend (push) Successful in 57s
Test Suite / lint-backend (push) Successful in 2s
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 12s

Attention-Widget an workspace.read koppeln, Vorhaben-Detail-Sektionen bei initiative.read anzeigen, App-Version im Kontext-Widget, Session-Loading-Race behoben.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Lars 2026-07-05 15:15:27 +02:00
parent fe3bc4f057
commit c9101db1e1
4 changed files with 29 additions and 12 deletions

View File

@ -41,7 +41,7 @@ import { useSession } from '../context/SessionContext.jsx'
export function InitiativeDetailPage() {
const { id } = useParams()
const { context } = useSession()
const { context, loading: sessionLoading } = useSession()
const { capabilities } = useCapabilities()
const {
actors,
@ -64,6 +64,7 @@ export function InitiativeDetailPage() {
const [hideDone, setHideDone] = useState(true)
const load = useCallback(async () => {
if (sessionLoading) return
setLoading(true)
setError(null)
try {
@ -75,13 +76,12 @@ export function InitiativeDetailPage() {
setActions(actionData)
const loads = []
if (capabilities.has('kairo.blocker.read')) {
const canReadOm =
capabilities.has('kairo.initiative.read') ||
capabilities.has('kairo.blocker.read')
if (canReadOm) {
loads.push(listInitiativeBlockers(id).then(setBlockers).catch(() => setBlockers([])))
}
if (capabilities.has('kairo.backlog.read')) {
loads.push(listInitiativeBacklog(id).then(setBacklogItems).catch(() => setBacklogItems([])))
}
if (capabilities.has('kairo.milestone.read')) {
loads.push(listInitiativeMilestones(id).then(setMilestones).catch(() => setMilestones([])))
}
await Promise.all(loads)
@ -90,7 +90,7 @@ export function InitiativeDetailPage() {
} finally {
setLoading(false)
}
}, [id, capabilities])
}, [id, capabilities, sessionLoading])
useEffect(() => {
load()
@ -405,7 +405,7 @@ export function InitiativeDetailPage() {
</ul>
</section>
{capabilities.has('kairo.milestone.read') && (
{capabilities.has('kairo.initiative.read') && (
<MilestonesSection
milestones={milestones}
canManage={capabilities.has('kairo.milestone.manage')}
@ -416,7 +416,7 @@ export function InitiativeDetailPage() {
/>
)}
{capabilities.has('kairo.backlog.read') && (
{capabilities.has('kairo.initiative.read') && (
<BacklogSection
items={backlogItems}
canManage={capabilities.has('kairo.backlog.manage')}
@ -428,7 +428,7 @@ export function InitiativeDetailPage() {
/>
)}
{capabilities.has('kairo.blocker.read') && (
{capabilities.has('kairo.initiative.read') && (
<BlockersSection
blockers={blockers}
canManage={capabilities.has('kairo.blocker.manage')}

View File

@ -19,13 +19,13 @@ describe('widgetRegistry', () => {
'kairo.action.read',
'kairo.initiative.read',
'kairo.workspace.read',
'kairo.attention.read',
])
expect(all.length).toBe(6)
const readOnly = getWidgetsForArea('workspace', ['kairo.initiative.read'])
expect(readOnly.some((w) => w.key === 'kairo.my_open_actions')).toBe(false)
expect(readOnly.some((w) => w.key === 'kairo.active_initiatives')).toBe(true)
expect(readOnly.some((w) => w.key === 'kairo.attention')).toBe(false)
})
})

View File

@ -42,7 +42,7 @@ export const WIDGETS = [
title: 'Aufmerksamkeit',
description: 'Regelbasierte Attention-Items',
area: 'workspace',
requiredCapability: 'kairo.attention.read',
requiredCapability: 'kairo.workspace.read',
defaultOrder: 8,
component: AttentionWidget,
},

View File

@ -1,8 +1,17 @@
import { useEffect, useState } from 'react'
import { useSession } from '../context/SessionContext.jsx'
import { WidgetCard } from '../components/WidgetCard.jsx'
export function TenantContextWidget() {
const { context, me } = useSession()
const [version, setVersion] = useState(null)
useEffect(() => {
fetch('/api/version')
.then((r) => r.json())
.then((data) => setVersion(data))
.catch(() => setVersion(null))
}, [])
return (
<WidgetCard title="Kontext" subtitle="Aktiver Mandant und Actor">
@ -28,6 +37,14 @@ export function TenantContextWidget() {
<dd>{context.actor.type}</dd>
</div>
)}
{version && (
<div>
<dt>App-Version</dt>
<dd className="muted">
{version.version} · Schema {version.schema}
</dd>
</div>
)}
</dl>
)}
</WidgetCard>