Kairo-Jinkendo/frontend/src/config/methodUiDefaults.js

181 lines
6.0 KiB
JavaScript
Raw Normal View History

/**
* AP1.9d — Methoden-Default-UI: Archetyp/Methode steuert Prozessleiste,
* Modus-Defaults und sichtbare Nav-Knoten (Anti-Todo-Wand).
*
* @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', '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', 'inbox', '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 allowed = new Set(planOutlineKeys)
return PLAN_OUTLINE_NODES.filter((node) => allowed.has(node.key))
}
/**
* @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
}