All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m8s
- Bumped APP_VERSION to 0.8.131 and updated the changelog to reflect recent changes. - Added the TrainingPlanningUnitFormModal component to the TrainingPlanningPage for enhanced training unit management. - Refactored frameworkLineageText utility function for better code organization and reusability in the training planning context. - Updated BASELINE_SNAPSHOT documentation to include new metrics and logging details for k6 health checks.
116 lines
3.7 KiB
JavaScript
116 lines
3.7 KiB
JavaScript
/** Reine Hilfen für Trainingsplanung (Kalender, Sichtbarkeits-Kurztext, Trainer-Zuordnung). */
|
||
|
||
export function trainingVisibilityShortDE(visibility) {
|
||
const v = String(visibility || '').trim().toLowerCase()
|
||
if (v === 'official') return 'Öffentliche Bibliothek'
|
||
if (v === 'club') return 'Verein'
|
||
if (v === 'private') return 'Privat'
|
||
return visibility ? String(visibility) : ''
|
||
}
|
||
|
||
export function addDaysIsoDate(isoDay, daysDelta) {
|
||
const d = new Date(`${isoDay}T12:00:00`)
|
||
d.setDate(d.getDate() + daysDelta)
|
||
return d.toISOString().slice(0, 10)
|
||
}
|
||
|
||
export function pad2(n) {
|
||
return String(n).padStart(2, '0')
|
||
}
|
||
|
||
export function toIsoLocal(d) {
|
||
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`
|
||
}
|
||
|
||
/** Montag = erster Wochentag (ISO-Woche UI) */
|
||
export function mondayIndex(d) {
|
||
return (d.getDay() + 6) % 7
|
||
}
|
||
|
||
/** Kalendarische Monatsansicht: erster und letzter Tag des sichtbaren Rasters (Mo–So) */
|
||
export function getCalendarGridRange(ym) {
|
||
const parts = (ym || '').split('-').map(Number)
|
||
const y = parts[0]
|
||
const m = parts[1]
|
||
if (!y || !m || m < 1 || m > 12) {
|
||
const t = new Date()
|
||
return { gridStart: toIsoLocal(t), gridEnd: toIsoLocal(t) }
|
||
}
|
||
const first = new Date(y, m - 1, 1)
|
||
const last = new Date(y, m, 0)
|
||
const gridStart = new Date(first)
|
||
gridStart.setDate(first.getDate() - mondayIndex(first))
|
||
const lastMon = mondayIndex(last)
|
||
const gridEnd = new Date(last)
|
||
gridEnd.setDate(last.getDate() + (6 - lastMon))
|
||
return { gridStart: toIsoLocal(gridStart), gridEnd: toIsoLocal(gridEnd) }
|
||
}
|
||
|
||
export function shiftCalendarMonth(ym, delta) {
|
||
const parts = (ym || '').split('-').map(Number)
|
||
const y = parts[0] || new Date().getFullYear()
|
||
const m = parts[1] || 1
|
||
const d = new Date(y, m - 1 + delta, 1)
|
||
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}`
|
||
}
|
||
|
||
export function enumerateIsoDays(fromIso, toIso) {
|
||
const out = []
|
||
const cur = new Date(`${fromIso}T12:00:00`)
|
||
const end = new Date(`${toIso}T12:00:00`)
|
||
while (cur <= end) {
|
||
out.push(toIsoLocal(cur))
|
||
cur.setDate(cur.getDate() + 1)
|
||
}
|
||
return out
|
||
}
|
||
|
||
export const WEEKDAYS_DE = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']
|
||
|
||
export function toNumList(arr) {
|
||
if (!Array.isArray(arr)) return []
|
||
const out = []
|
||
for (const x of arr) {
|
||
const n = Number(x)
|
||
if (Number.isFinite(n) && n >= 1) out.push(n)
|
||
}
|
||
return out
|
||
}
|
||
|
||
export const sessionAssignDefaults = () => ({
|
||
lead_trainer_profile_id: '',
|
||
session_assistants_inherit: true,
|
||
session_assistant_profile_ids: [],
|
||
})
|
||
|
||
/** Co_trainer_ids aus TrainingGroups (Liste/JSON) → Zahlenliste */
|
||
export function normalizeGroupCoTrainerIds(raw) {
|
||
if (raw == null) return []
|
||
const arr = Array.isArray(raw) ? raw : []
|
||
const out = []
|
||
for (const x of arr) {
|
||
const n = Number(x)
|
||
if (Number.isFinite(n) && n >= 1) out.push(n)
|
||
}
|
||
return out
|
||
}
|
||
|
||
/** Mitgliederverzeichnis-Einträge ohne effektiven Leitungsträger als Co‑Option */
|
||
export function filterDirectoryExcludingLead(directory, excludeLeadPid) {
|
||
const ex =
|
||
excludeLeadPid != null && excludeLeadPid !== '' && Number.isFinite(Number(excludeLeadPid))
|
||
? Number(excludeLeadPid)
|
||
: null
|
||
if (ex == null) return directory
|
||
return directory.filter((m) => Number(m.id) !== ex)
|
||
}
|
||
|
||
/** Kurztexte für Rahmen-Herkunft (Listen + Formular-Modal). */
|
||
export function frameworkLineageText(unit) {
|
||
const fpTitle = (unit.origin_framework_program_title || '').trim() || 'Rahmenprogramm'
|
||
const st = (unit.origin_framework_slot_title || '').trim()
|
||
const idx = unit.origin_framework_slot_sort_order
|
||
const slotBit = st || (typeof idx === 'number' ? `Session ${idx + 1}` : 'Session')
|
||
return { fpTitle, slotBit, fpId: unit.origin_framework_program_id }
|
||
}
|