Kairo-Jinkendo/frontend/src/config/methodUiDefaults.js
Lars b7d88ce24a
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Test Suite / pytest-backend (push) Successful in 4m54s
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 13s
feat(A1): Minimum Usability Slice — CadenceInstance, Today-Übung, Gate-Fortschritt
PO No-Go A1 adressieren: Tages-Tracking mit Messnotiz, Work-UI für Übung, Next-Action-Link, Gate-Fortschritt in Kontrolle, Eingang aus A1-Nav entfernt.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 12:32:23 +02:00

183 lines
6.1 KiB
JavaScript

/**
* AP1.9d — Methoden-Default-UI (DEPRECATED AP2.3b — Fallback bis Resolver live).
* Bevorzugt: GET /api/initiatives/:id/operating-context + resolveOperatingProfile.js
*
* @typedef {{ key: string, to: string, label: string, mode: 'plan'|'work'|'control' }} ProcessStep
* @typedef {{
* archetypeKey: string | null,
* methodKey: string | null,
* hasActiveSprint: boolean,
* }} MethodUiInput
* @typedef {{
* processSteps: ProcessStep[],
* planDefaultRoute: string,
* workDefaultRoute: string,
* controlDefaultRoute: string,
* planOutlineKeys: string[] | null,
* workNavKeys: string[] | null,
* }} MethodUiDefaults
*/
import { PLAN_OUTLINE_NODES } from '../plan/planOutlineNodes.js'
import { WORK_NAV_ITEMS } from './workNav.js'
const PRODUCT_PROCESS = [
{ key: 'inbox', to: '/plan/inbox', label: 'Eingang', mode: 'plan' },
{ key: 'sprint-plan', to: '/plan/sprint', label: 'Sprint planen', mode: 'plan' },
{ key: 'sprint-work', to: '/work/sprint', label: 'Ausführen', mode: 'work' },
{ key: 'control', to: '/control/status', label: 'Kontrolle', mode: 'control' },
]
const LINEAR_PROCESS = [
{ key: 'gates', to: '/plan/gates', label: 'Zielzustände', mode: 'plan' },
{ key: 'work', to: '/work/today', label: 'Ausführen', mode: 'work' },
{ key: 'control', to: '/control/status', label: 'Kontrolle', mode: 'control' },
]
const MATURITY_PROCESS = [
{ key: 'gates', to: '/plan/gates', label: 'Stufen', mode: 'plan' },
{ key: 'journey', to: '/control/journey', label: 'Rhythmen', mode: 'control' },
{ key: 'work', to: '/work/today', label: 'Ausführen', mode: 'work' },
{ key: 'control', to: '/control/status', label: 'Kontrolle', mode: 'control' },
]
const PROGRAM_PROCESS = [
{ key: 'gates', to: '/plan/gates', label: 'Phasen', mode: 'plan' },
{ key: 'inbox', to: '/plan/inbox', label: 'Eingang', mode: 'plan' },
{ key: 'work', to: '/work/today', label: 'Ausführen', mode: 'work' },
{ key: 'control', to: '/control/status', label: 'Kontrolle', mode: 'control' },
]
const ARCHETYPE_UI = {
'initiative.product': {
processSteps: PRODUCT_PROCESS,
planDefaultRoute: '/plan/inbox',
workDefaultRoute: '/work/sprint',
controlDefaultRoute: '/control/status',
planOutlineKeys: ['profile', 'inbox', 'work', 'sprint', 'gates'],
workNavKeys: ['sprint', 'today', 'mine'],
},
'initiative.linear_project': {
processSteps: LINEAR_PROCESS,
planDefaultRoute: '/plan/gates',
workDefaultRoute: '/work/today',
controlDefaultRoute: '/control/status',
planOutlineKeys: ['profile', 'structure', 'gates', 'inbox', 'work'],
workNavKeys: ['today', 'mine', 'sprint'],
},
'initiative.maturity_journey': {
processSteps: MATURITY_PROCESS,
planDefaultRoute: '/plan/gates',
workDefaultRoute: '/work/today',
controlDefaultRoute: '/control/status',
planOutlineKeys: ['profile', 'gates', 'work'],
workNavKeys: ['today', 'mine', 'sprint'],
},
'initiative.program': {
processSteps: PROGRAM_PROCESS,
planDefaultRoute: '/plan/gates',
workDefaultRoute: '/work/today',
controlDefaultRoute: '/control/status',
planOutlineKeys: ['profile', 'structure', 'gates', 'inbox', 'sprint', 'work'],
workNavKeys: ['today', 'mine', 'sprint'],
},
}
const FALLBACK_DEFAULTS = {
processSteps: [],
planDefaultRoute: '/plan/profile',
workDefaultRoute: '/work/today',
controlDefaultRoute: '/control/status',
planOutlineKeys: null,
workNavKeys: null,
}
/**
* @param {MethodUiInput} input
* @returns {MethodUiDefaults}
*/
export function resolveMethodUiDefaults(input = {}) {
const archetypeKey = input.archetypeKey || null
const base = ARCHETYPE_UI[archetypeKey] || FALLBACK_DEFAULTS
let workDefaultRoute = base.workDefaultRoute
if (archetypeKey === 'initiative.product' && !input.hasActiveSprint) {
workDefaultRoute = '/work/today'
}
return {
processSteps: base.processSteps,
planDefaultRoute: base.planDefaultRoute,
workDefaultRoute,
controlDefaultRoute: base.controlDefaultRoute,
planOutlineKeys: base.planOutlineKeys,
workNavKeys: base.workNavKeys,
}
}
/**
* @param {MethodUiInput} input
* @returns {ProcessStep[]}
*/
export function resolveProcessSteps(input = {}) {
return resolveMethodUiDefaults(input).processSteps
}
/**
* @param {MethodUiInput} input
* @returns {typeof PLAN_OUTLINE_NODES}
*/
export function resolvePlanOutlineNodes(input = {}) {
const { planOutlineKeys } = resolveMethodUiDefaults(input)
if (!planOutlineKeys) return PLAN_OUTLINE_NODES
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),
)
}
/**
* @param {MethodUiInput} input
* @returns {typeof WORK_NAV_ITEMS}
*/
export function resolveWorkNavItems(input = {}) {
const { workNavKeys } = resolveMethodUiDefaults(input)
if (!workNavKeys) return WORK_NAV_ITEMS
const order = new Map(workNavKeys.map((key, index) => [key, index]))
return WORK_NAV_ITEMS.filter((item) => order.has(item.key)).sort(
(a, b) => (order.get(a.key) ?? 0) - (order.get(b.key) ?? 0),
)
}
/**
* Aktiven Prozessschritt aus Modus + Pfad ableiten.
* @param {string} pathname
* @param {ProcessStep[]} steps
*/
export function resolveActiveProcessStepKey(pathname, steps) {
const path = (pathname || '').replace(/\/$/, '') || pathname
if (!steps.length) return null
if (path.startsWith('/control/')) {
const journey = steps.find((s) => s.key === 'journey')
if (journey && path.startsWith('/control/journey')) return 'journey'
return steps.find((s) => s.mode === 'control')?.key ?? null
}
if (path.startsWith('/work/')) {
if (path.startsWith('/work/sprint')) {
return steps.find((s) => s.key === 'sprint-work')?.key ?? null
}
return steps.find((s) => s.key === 'work')?.key ?? null
}
if (path.startsWith('/plan/')) {
if (path.startsWith('/plan/inbox')) return 'inbox'
if (path.startsWith('/plan/sprint')) return 'sprint-plan'
if (path.startsWith('/plan/gates')) return 'gates'
}
if (path.startsWith('/actions/')) {
return steps.find((s) => s.mode === 'work')?.key ?? null
}
return null
}