/** * Eingabe/Ausgabe geplanter Trainingsdauer (Minuten intern, Anzeige z. B. 1,5 h). */ export function parseDurationInput(raw) { if (raw === '' || raw == null) return null const s = String(raw).trim().toLowerCase().replace(/\s+/g, '') if (!s) return null const hMatch = s.match(/^(\d+(?:[.,]\d+)?)\s*h(?:\s*(\d{1,2}))?$/) if (hMatch) { const h = parseFloat(hMatch[1].replace(',', '.')) const extraMin = hMatch[2] ? parseInt(hMatch[2], 10) : 0 if (!Number.isFinite(h) || h < 0) return null const total = Math.round(h * 60) + (Number.isFinite(extraMin) ? extraMin : 0) return total > 0 ? total : null } if (/^\d+(?:[.,]\d+)?$/.test(s)) { const n = parseFloat(s.replace(',', '.')) if (!Number.isFinite(n) || n <= 0) return null if (s.includes(',') || s.includes('.') || n < 8) { return Math.round(n * 60) } return Math.round(n) } const minMatch = s.match(/^(\d+)\s*min?$/) if (minMatch) { const m = parseInt(minMatch[1], 10) return Number.isFinite(m) && m > 0 ? m : null } return null } export function formatDurationDisplay(minutes, { empty = '—' } = {}) { if (minutes == null || minutes === '') return empty const m = Number(minutes) if (!Number.isFinite(m) || m <= 0) return empty if (m % 60 === 0) return `${m / 60} h` if (m >= 60) { const h = Math.floor(m / 60) const rest = m % 60 return `${h} h ${rest} Min` } return `${m} Min` } /** Formularfeld: Minuten → Anzeige-String (z. B. 90 → "1,5") */ export function minutesToDurationFieldValue(minutes) { if (minutes == null || minutes === '') return '' const m = Number(minutes) if (!Number.isFinite(m) || m <= 0) return '' if (m % 60 === 0) return String(m / 60) if (m >= 60) { const h = m / 60 return Number.isInteger(h) ? String(h) : String(Math.round(h * 10) / 10).replace('.', ',') } return String(m) } export function sumExercisePlannedMinutes(sections) { let sum = 0 for (const sec of sections || []) { for (const it of sec.items || []) { if (it.item_type !== 'exercise') continue const n = parseDurationInput(it.planned_duration_min) if (n != null) sum += n } } return sum } export function sumSectionPlannedMinutes(sections) { let sum = 0 for (const sec of sections || []) { const n = parseDurationInput(sec.planned_duration_min) if (n != null) sum += n } return sum } /** * Anzeige für Session-Dauer (ein Slot oder Min/Max über mehrere Sessions). * @param {number|null|undefined} minMinutes * @param {number|null|undefined} maxMinutes */ export function formatSessionDurationRange(minMinutes, maxMinutes, { empty = '—' } = {}) { const lo = minMinutes != null && minMinutes !== '' ? Number(minMinutes) : null const hi = maxMinutes != null && maxMinutes !== '' ? Number(maxMinutes) : null if (lo != null && Number.isFinite(lo) && lo > 0) { if (hi != null && Number.isFinite(hi) && hi > 0 && hi !== lo) { return `${formatDurationDisplay(lo, { empty: '' })} – ${formatDurationDisplay(hi, { empty: '' })}` } return formatDurationDisplay(lo, { empty }) } if (hi != null && Number.isFinite(hi) && hi > 0) { return formatDurationDisplay(hi, { empty }) } return empty }