/** * Einheitlicher Rücksprung-Kontext für PWA-Navigation (siehe NAV_RETURN_CONTEXT_SPEC.md). */ import { buildPlanningHubReturnState, PLANNING_HUB_PATH, planningHubPathFromReturnState, } from './planningUnitRoutes' export { PLANNING_HUB_PATH } export const NAV_RETURN_STATE_KEY = 'appReturn' export const EXERCISES_LIST_PATH = '/exercises' export const TRAINING_MODULES_LIST_PATH = '/planning/training-modules' export const PLAN_TEMPLATES_LIST_PATH = '/planning/plan-templates' export const FRAMEWORK_PROGRAMS_LIST_PATH = '/planning/framework-programs' export const SETTINGS_PATH = '/settings' export const DASHBOARD_PATH = '/' export function buildNavReturnContext(opts) { const path = String(opts?.path || '').trim() const label = String(opts?.label || '').trim() if (!path || !label) return null const ctx = { v: 1, path, label } if (opts?.kind) ctx.kind = opts.kind if (opts?.payload && typeof opts.payload === 'object') { ctx.payload = opts.payload } return ctx } export function buildExercisesListReturnContext() { return buildNavReturnContext({ path: EXERCISES_LIST_PATH, label: 'Zurück zur Übungsliste', kind: 'exerciseList', }) } export function buildTrainingModulesListReturnContext() { return buildNavReturnContext({ path: TRAINING_MODULES_LIST_PATH, label: 'Zurück zur Modul-Bibliothek', kind: 'trainingModulesList', }) } export function buildPlanTemplatesListReturnContext() { return buildNavReturnContext({ path: PLAN_TEMPLATES_LIST_PATH, label: 'Zurück zu Vorlagen', kind: 'planTemplatesList', }) } export function buildFrameworkProgramsListReturnContext() { return buildNavReturnContext({ path: FRAMEWORK_PROGRAMS_LIST_PATH, label: 'Zurück zu Rahmenprogrammen', kind: 'frameworkProgramsList', }) } export function buildSettingsReturnContext() { return buildNavReturnContext({ path: SETTINGS_PATH, label: 'Zurück zu Einstellungen', kind: 'settings', }) } export function buildDashboardReturnContext() { return buildNavReturnContext({ path: DASHBOARD_PATH, label: 'Zurück zur Übersicht', kind: 'dashboard', }) } export const MEDIA_LIBRARY_PATH = '/media' export function buildMediaLibraryReturnContext() { return buildNavReturnContext({ path: MEDIA_LIBRARY_PATH, label: 'Zurück zur Medienbibliothek', kind: 'mediaLibrary', }) } export function buildTrainingRunReturnContext(unitId) { const id = String(unitId || '').trim() if (!id) return null return buildNavReturnContext({ path: `/planning/run/${id}`, label: 'Zurück zum Trainingsablauf', kind: 'trainingRun', payload: { unitId: id }, }) } export function buildPlanningHubFallbackReturnContext() { return buildNavReturnContext({ path: PLANNING_HUB_PATH, label: 'Zurück zur Planung', kind: 'planningHub', }) } /** Router-Link state mit appReturn (optional zusätzlicher State). */ export function linkStateWithAppReturn(returnContext, extraState) { const state = { ...(extraState || {}) } if (returnContext) state[NAV_RETURN_STATE_KEY] = returnContext return Object.keys(state).length > 0 ? state : undefined } /** * Rücksprung zur aktuellen Route (z. B. Einheiten-Editor). */ export function buildCurrentLocationReturnContext(location, label) { const path = `${location?.pathname || ''}${location?.search || ''}`.trim() if (!path) return null return buildNavReturnContext({ path, label: String(label || 'Zurück').trim(), kind: 'currentLocation', }) } /** @param {ReturnType|object} hubState */ export function buildPlanningHubReturnContext(hubState = {}) { const payload = buildPlanningHubReturnState(hubState) return buildNavReturnContext({ path: planningHubPathFromReturnState(payload), label: 'Zurück zur Planung', kind: 'planningHub', payload, }) } /** Legacy planningReturn → appReturn */ export function appReturnFromPlanningReturn(planningReturn) { if (!planningReturn || planningReturn.v !== 1) return null return buildPlanningHubReturnContext(planningReturn) } /** * @param {import('react-router-dom').Location|{ state?: object }|null|undefined} location */ export function readNavReturnFromLocation(location) { const state = location?.state if (!state || typeof state !== 'object') return null const raw = state[NAV_RETURN_STATE_KEY] if (raw?.v === 1 && raw.path && raw.label) return raw if (state.planningReturn) return appReturnFromPlanningReturn(state.planningReturn) return null } /** * @param {import('react-router-dom').Location|{ state?: object }|null|undefined} location * @param {{ path: string, label: string }|null|undefined} fallback */ export function resolveNavReturnTarget(location, fallback) { const ctx = readNavReturnFromLocation(location) if (ctx?.path && ctx?.label) { return { path: ctx.path, label: ctx.label, fromContext: true } } if (fallback?.path && fallback?.label) { return { path: fallback.path, label: fallback.label, fromContext: false } } return null } /** * @param {import('react-router-dom').NavigateFunction} navigate * @param {import('react-router-dom').Location|{ state?: object }|null|undefined} location * @param {{ path?: string, label?: string }|null|undefined} fallback */ export function goNavReturn(navigate, location, fallback) { const target = resolveNavReturnTarget(location, fallback) if (target?.fromContext && target.path) { navigate(target.path) return } if (typeof window !== 'undefined' && window.history.length > 1) { navigate(-1) return } if (fallback?.path) { navigate(fallback.path) return } navigate('/') } /** * @param {import('react-router-dom').NavigateFunction} navigate * @param {string} to * @param {ReturnType|null|undefined} returnContext * @param {object} [options] */ export function navigateWithAppReturn(navigate, to, returnContext, options = {}) { const state = { ...(options.state || {}) } if (returnContext) state[NAV_RETURN_STATE_KEY] = returnContext navigate(to, { ...options, state }) } /** * Bestehenden appReturn (oder Legacy planningReturn) beim Weiterleiten erhalten. */ export function preserveAppReturnOnNavigate(navigate, location, to, options = {}) { const existing = readNavReturnFromLocation(location) const state = { ...(options.state || {}) } if (existing) state[NAV_RETURN_STATE_KEY] = existing navigate(to, { ...options, state }) }