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

View File

@ -19,13 +19,13 @@ describe('widgetRegistry', () => {
'kairo.action.read', 'kairo.action.read',
'kairo.initiative.read', 'kairo.initiative.read',
'kairo.workspace.read', 'kairo.workspace.read',
'kairo.attention.read',
]) ])
expect(all.length).toBe(6) expect(all.length).toBe(6)
const readOnly = getWidgetsForArea('workspace', ['kairo.initiative.read']) 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.my_open_actions')).toBe(false)
expect(readOnly.some((w) => w.key === 'kairo.active_initiatives')).toBe(true) 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', title: 'Aufmerksamkeit',
description: 'Regelbasierte Attention-Items', description: 'Regelbasierte Attention-Items',
area: 'workspace', area: 'workspace',
requiredCapability: 'kairo.attention.read', requiredCapability: 'kairo.workspace.read',
defaultOrder: 8, defaultOrder: 8,
component: AttentionWidget, component: AttentionWidget,
}, },

View File

@ -1,8 +1,17 @@
import { useEffect, useState } from 'react'
import { useSession } from '../context/SessionContext.jsx' import { useSession } from '../context/SessionContext.jsx'
import { WidgetCard } from '../components/WidgetCard.jsx' import { WidgetCard } from '../components/WidgetCard.jsx'
export function TenantContextWidget() { export function TenantContextWidget() {
const { context, me } = useSession() 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 ( return (
<WidgetCard title="Kontext" subtitle="Aktiver Mandant und Actor"> <WidgetCard title="Kontext" subtitle="Aktiver Mandant und Actor">
@ -28,6 +37,14 @@ export function TenantContextWidget() {
<dd>{context.actor.type}</dd> <dd>{context.actor.type}</dd>
</div> </div>
)} )}
{version && (
<div>
<dt>App-Version</dt>
<dd className="muted">
{version.version} · Schema {version.schema}
</dd>
</div>
)}
</dl> </dl>
)} )}
</WidgetCard> </WidgetCard>