shinkan-jinkendo/frontend/src/utils/planningUnitRoutes.js
Lars cb868373f4
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Test Suite / pytest-backend (push) Successful in 41s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 34s
Test Suite / playwright-tests (push) Successful in 1m16s
Test Suite / pytest-backend (pull_request) Successful in 36s
Test Suite / lint-backend (pull_request) Successful in 0s
Test Suite / build-frontend (pull_request) Successful in 13s
Test Suite / k6 /health Baseline (pull_request) Successful in 34s
Test Suite / playwright-tests (pull_request) Successful in 1m13s
Enhance PlanningLayout and TrainingUnitEditPage with unsaved changes handling
- Updated PlanningLayout to conditionally render the PlanningRouteNav based on the current path, improving navigation for planning unit editors.
- Enhanced TrainingUnitEditPage with unsaved changes detection, integrating a prompt for users to confirm before leaving the page with unsaved changes.
- Introduced utility functions for creating a stable snapshot of form data to facilitate dirty-checking, ensuring better user experience during form editing.
- Added tests for the new utility functions to validate their behavior in various scenarios.
2026-05-19 14:39:46 +02:00

104 lines
3.9 KiB
JavaScript

/**
* Zentrale Routen & Rückkehr-Kontext Trainingsplanung ↔ Einheiten-Editor.
* Alle Navigations-URLs für Kalender-Einheiten hier pflegen (Drift-Schutz).
*/
export const PLANNING_HUB_PATH = '/planning'
export function buildPlanUnitEditPath(unitId) {
const id = Number(unitId)
if (!Number.isFinite(id) || id < 1) return PLANNING_HUB_PATH
return `/planning/units/${id}/edit`
}
/**
* @param {{ groupId?: string|number, plannedDate?: string, templateId?: string|number, mode?: string }} opts
*/
export function buildPlanUnitNewPath(opts = {}) {
const params = new URLSearchParams()
if (opts.groupId != null && opts.groupId !== '') params.set('group', String(opts.groupId))
if (opts.plannedDate) params.set('date', String(opts.plannedDate).slice(0, 10))
if (opts.templateId != null && opts.templateId !== '') params.set('template', String(opts.templateId))
if (opts.mode === 'debrief') params.set('mode', 'debrief')
const q = params.toString()
return q ? `/planning/units/new?${q}` : '/planning/units/new'
}
/** @param {import('react-router-dom').URLSearchParams|string|null|undefined} raw */
export function legacyPlanningUnitDeepLinkTarget(raw) {
const params =
raw instanceof URLSearchParams ? raw : new URLSearchParams(typeof raw === 'string' ? raw : '')
const uid = params.get('unit')
if (!uid) return null
const idNum = parseInt(uid, 10)
if (!Number.isFinite(idNum) || idNum < 1) return null
const debrief = params.get('debrief')
const base = buildPlanUnitEditPath(idNum)
if (debrief === '1' || debrief === 'true') {
return `${base}?mode=debrief`
}
return base
}
/**
* @param {{
* selectedGroupId?: string,
* planView?: string,
* calendarMonthStr?: string,
* startDate?: string,
* endDate?: string,
* planScope?: string,
* assignedToMeOnly?: boolean,
* }} hubState
*/
export function buildPlanningHubReturnState(hubState = {}) {
return {
v: 1,
selectedGroupId: hubState.selectedGroupId != null ? String(hubState.selectedGroupId) : '',
planView: hubState.planView || 'list',
calendarMonthStr: hubState.calendarMonthStr || '',
startDate: hubState.startDate || '',
endDate: hubState.endDate || '',
planScope: hubState.planScope || 'group',
assignedToMeOnly: Boolean(hubState.assignedToMeOnly),
}
}
/** @param {string} pathname */
export function isPlanningUnitEditorPath(pathname) {
if (!pathname) return false
if (pathname === '/planning/units/new') return true
return /^\/planning\/units\/\d+\/edit$/.test(pathname)
}
/** @param {ReturnType<typeof buildPlanningHubReturnState>|null|undefined} state */
export function planningHubPathFromReturnState(state) {
if (!state || state.v !== 1) return PLANNING_HUB_PATH
const params = new URLSearchParams()
if (state.selectedGroupId) params.set('group', state.selectedGroupId)
if (state.planView && state.planView !== 'list') params.set('view', state.planView)
if (state.planView === 'calendar' && state.calendarMonthStr) {
params.set('month', state.calendarMonthStr)
}
if (state.startDate) params.set('start', state.startDate)
if (state.endDate) params.set('end', state.endDate)
if (state.planScope && state.planScope !== 'group') params.set('scope', state.planScope)
if (state.assignedToMeOnly) params.set('mine', '1')
const q = params.toString()
return q ? `${PLANNING_HUB_PATH}?${q}` : PLANNING_HUB_PATH
}
/** Hub-Query beim Mount auf State-Felder mappen. */
export function parsePlanningHubQuery(searchParams) {
const p = searchParams instanceof URLSearchParams ? searchParams : new URLSearchParams(searchParams || '')
return {
selectedGroupId: p.get('group') || '',
planView: p.get('view') === 'calendar' ? 'calendar' : 'list',
calendarMonthStr: p.get('month') || '',
startDate: p.get('start') || '',
endDate: p.get('end') || '',
planScope: p.get('scope') === 'club' ? 'club' : 'group',
assignedToMeOnly: p.get('mine') === '1',
}
}