feat(product): Plan-Arbeit fuer Direct-AP-Pfad (ADP P1)
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Test Suite / pytest-backend (push) Successful in 3m45s
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 19s
Test Suite / playwright-smoke (push) Successful in 12s
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Test Suite / pytest-backend (push) Successful in 3m45s
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 19s
Test Suite / playwright-smoke (push) Successful in 12s
Product-Outline enthaelt work; planOutlineKeys-Reihenfolge steuert Sidebar; Product-spezifischer Lead auf Plan-Arbeit.
This commit is contained in:
parent
f7d65dde01
commit
dc6f25b673
|
|
@ -83,7 +83,7 @@ INITIATIVE_UI_PROFILES: dict[str, dict[str, Any]] = {
|
|||
"workDefaultRoute": "/work/sprint",
|
||||
"workDefaultRouteWithoutActiveWorkCycle": "/work/today",
|
||||
"controlDefaultRoute": "/control/status",
|
||||
"planOutlineKeys": ["profile", "inbox", "sprint", "gates"],
|
||||
"planOutlineKeys": ["profile", "inbox", "work", "sprint", "gates"],
|
||||
"workNavKeys": ["sprint", "today", "mine"],
|
||||
"dataSlices": [
|
||||
"backlog",
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ const ARCHETYPE_UI = {
|
|||
planDefaultRoute: '/plan/inbox',
|
||||
workDefaultRoute: '/work/sprint',
|
||||
controlDefaultRoute: '/control/status',
|
||||
planOutlineKeys: ['profile', 'inbox', 'sprint', 'gates'],
|
||||
planOutlineKeys: ['profile', 'inbox', 'work', 'sprint', 'gates'],
|
||||
workNavKeys: ['sprint', 'today', 'mine'],
|
||||
},
|
||||
'initiative.linear_project': {
|
||||
|
|
@ -130,8 +130,10 @@ export function resolveProcessSteps(input = {}) {
|
|||
export function resolvePlanOutlineNodes(input = {}) {
|
||||
const { planOutlineKeys } = resolveMethodUiDefaults(input)
|
||||
if (!planOutlineKeys) return PLAN_OUTLINE_NODES
|
||||
const allowed = new Set(planOutlineKeys)
|
||||
return PLAN_OUTLINE_NODES.filter((node) => allowed.has(node.key))
|
||||
const order = new Map(planOutlineKeys.map((key, index) => [key, index]))
|
||||
return PLAN_OUTLINE_NODES.filter((node) => order.has(node.key)).sort(
|
||||
(a, b) => (order.get(a.key) ?? 0) - (order.get(b.key) ?? 0),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ describe('methodUiDefaults', () => {
|
|||
})
|
||||
expect(ui.planDefaultRoute).toBe('/plan/inbox')
|
||||
expect(ui.workDefaultRoute).toBe('/work/sprint')
|
||||
expect(ui.planOutlineKeys).toEqual(['profile', 'inbox', 'sprint', 'gates'])
|
||||
expect(ui.planOutlineKeys).toEqual(['profile', 'inbox', 'work', 'sprint', 'gates'])
|
||||
expect(resolveWorkNavItems({ archetypeKey: 'initiative.product' }).map((i) => i.key)).toEqual([
|
||||
'sprint',
|
||||
'today',
|
||||
|
|
@ -41,6 +41,12 @@ describe('methodUiDefaults', () => {
|
|||
])
|
||||
})
|
||||
|
||||
it('product plan outline includes work for direct AP path', () => {
|
||||
expect(
|
||||
resolvePlanOutlineNodes({ archetypeKey: 'initiative.product' }).map((n) => n.key),
|
||||
).toEqual(['profile', 'inbox', 'work', 'sprint', 'gates'])
|
||||
})
|
||||
|
||||
it('linear archetype defaults to gates and filters plan outline', () => {
|
||||
const ui = resolveMethodUiDefaults({ archetypeKey: 'initiative.linear_project' })
|
||||
expect(ui.planDefaultRoute).toBe('/plan/gates')
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ function PlanWorkInner() {
|
|||
const { initiativeId, projectId: scopeProjectId, hrefWithScope } = useProgramScope()
|
||||
const [executionGraph, setExecutionGraph] = useState(null)
|
||||
const {
|
||||
initiative,
|
||||
actions,
|
||||
projects,
|
||||
roadmapItems,
|
||||
|
|
@ -52,6 +53,11 @@ function PlanWorkInner() {
|
|||
return <LoadingState message="Lade Arbeitspakete …" />
|
||||
}
|
||||
|
||||
const isProduct = initiative?.archetype_key === 'initiative.product'
|
||||
const sectionLead = isProduct
|
||||
? 'Arbeitspakete direkt anlegen (Pfad B) oder committetes Ist — ohne Eingang-Umweg. Sprint-fokussiert unter Ausführen → Sprint.'
|
||||
: 'Gesamt-Ist am Vorhaben — Sprint-fokussierte Arbeit unter Ausführen → Sprint.'
|
||||
|
||||
return (
|
||||
<>
|
||||
{error && <p className="error">{error}</p>}
|
||||
|
|
@ -76,7 +82,7 @@ function PlanWorkInner() {
|
|||
busy={formBusy}
|
||||
executionGraph={executionGraph}
|
||||
sectionTitle="Alle Arbeitspakete"
|
||||
sectionLead="Gesamt-Ist am Vorhaben — Sprint-fokussierte Arbeit unter Ausführen → Sprint."
|
||||
sectionLead={sectionLead}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -145,8 +145,10 @@ export function resolveOperatingProfileFromInput(input = {}) {
|
|||
export function resolvePlanOutlineNodesFromProfile(input = {}) {
|
||||
const { planOutlineKeys } = resolveOperatingProfileFromInput(input)
|
||||
if (!planOutlineKeys) return PLAN_OUTLINE_NODES
|
||||
const allowed = new Set(planOutlineKeys)
|
||||
return PLAN_OUTLINE_NODES.filter((node) => allowed.has(node.key))
|
||||
const order = new Map(planOutlineKeys.map((key, index) => [key, index]))
|
||||
return PLAN_OUTLINE_NODES.filter((node) => order.has(node.key)).sort(
|
||||
(a, b) => (order.get(a.key) ?? 0) - (order.get(b.key) ?? 0),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ const PRODUCT_CONTEXT = {
|
|||
workDefaultRoute: '/work/sprint',
|
||||
workDefaultRouteWithoutActiveWorkCycle: '/work/today',
|
||||
controlDefaultRoute: '/control/status',
|
||||
planOutlineKeys: ['profile', 'inbox', 'sprint', 'gates'],
|
||||
planOutlineKeys: ['profile', 'inbox', 'work', 'sprint', 'gates'],
|
||||
workNavKeys: ['sprint', 'today', 'mine'],
|
||||
},
|
||||
data_slices: ['backlog', 'actions', 'roadmap', 'work_cycles', 'projects'],
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user