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
- 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.
104 lines
3.9 KiB
JavaScript
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',
|
|
}
|
|
}
|