166 lines
4.2 KiB
JavaScript
166 lines
4.2 KiB
JavaScript
|
|
/**
|
|||
|
|
* Geführtes method_profile für Kombinationsübungen — Felder nach method_archetype.
|
|||
|
|
* Unbekannte JSON-Schlüssel bleiben beim Zusammenführen erhalten (Erweiterbarkeit).
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const INT_MAX = 86400
|
|||
|
|
|
|||
|
|
function parseProfileJson(raw) {
|
|||
|
|
if (typeof raw !== 'string' || !raw.trim()) return { ok: true, obj: {} }
|
|||
|
|
try {
|
|||
|
|
const p = JSON.parse(raw)
|
|||
|
|
if (!p || typeof p !== 'object' || Array.isArray(p)) {
|
|||
|
|
return { ok: false, error: 'Ablaufprofil muss ein JSON-Objekt sein.' }
|
|||
|
|
}
|
|||
|
|
return { ok: true, obj: { ...p } }
|
|||
|
|
} catch {
|
|||
|
|
return { ok: false, error: 'Ablaufprofil (JSON): Syntax ungültig.' }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Pro Archetyp: UI-Feldbeschreibungen (Werte werden in method_profile geschrieben) */
|
|||
|
|
export const METHOD_PROFILE_GUI_FIELDS = Object.freeze({
|
|||
|
|
sequence_linear: [
|
|||
|
|
{
|
|||
|
|
key: 'hint_step_duration_sec',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Orientierung: Sekunden je Station/Schritt (optional)',
|
|||
|
|
min: 5,
|
|||
|
|
max: INT_MAX,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'block_intro_sec',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Einführung / Demon am Block Gesamt (Sek., optional)',
|
|||
|
|
min: 0,
|
|||
|
|
max: INT_MAX,
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
circuit_rotate_time: [
|
|||
|
|
{
|
|||
|
|
key: 'work_seconds',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Arbeitszeit pro Station (Sek.)',
|
|||
|
|
min: 5,
|
|||
|
|
max: INT_MAX,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'transition_seconds',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Wechsel / Rotation (Sek., optional)',
|
|||
|
|
min: 0,
|
|||
|
|
max: INT_MAX,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'rest_seconds',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Pause zwischen Runden oder Stationen-Folgen (Sek., optional)',
|
|||
|
|
min: 0,
|
|||
|
|
max: INT_MAX,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'rounds',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Runden (optional, wenn alle Station je Runde angefahren werden)',
|
|||
|
|
min: 1,
|
|||
|
|
max: 999,
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
circuit_all_parallel: [
|
|||
|
|
{
|
|||
|
|
key: 'explain_before_seconds',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Zeitfenster Vorab‑Erklärung aller Stationen (Sek., optional)',
|
|||
|
|
min: 0,
|
|||
|
|
max: INT_MAX,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'simultaneous_start',
|
|||
|
|
kind: 'bool',
|
|||
|
|
label: 'Alle Stationen starten zusammen nach Erklärung',
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
station_parcour: [
|
|||
|
|
{
|
|||
|
|
key: 'allow_free_visit_order',
|
|||
|
|
kind: 'bool',
|
|||
|
|
label: 'Reihenfolge der Besuche frei (Parcours / Abhaken-Logik später im Coach)',
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
pair_superset: [
|
|||
|
|
{
|
|||
|
|
key: 'switch_seconds',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Orientierung: Wechselpause A↔B (Sek., optional)',
|
|||
|
|
min: 0,
|
|||
|
|
max: INT_MAX,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'work_seconds_per_side',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Arbeit pro Rolle oder Seite (Sek., optional)',
|
|||
|
|
min: 5,
|
|||
|
|
max: INT_MAX,
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
time_domain_interval: [
|
|||
|
|
{
|
|||
|
|
key: 'work_seconds',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Intervall: Belastungszeit (Sek.)',
|
|||
|
|
min: 5,
|
|||
|
|
max: INT_MAX,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'rest_seconds',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Intervall: Erholungszeit (Sek., optional)',
|
|||
|
|
min: 0,
|
|||
|
|
max: INT_MAX,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'interval_rounds',
|
|||
|
|
kind: 'int',
|
|||
|
|
label: 'Anzahl Wiederholungen / Runden der Domäne (optional)',
|
|||
|
|
min: 1,
|
|||
|
|
max: 999,
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
free_method_block: [],
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Aktualisiert method_profile unter Beibehaltung nicht-GUI Schlüssel.
|
|||
|
|
*/
|
|||
|
|
export function updateProfileGuided(archetype, rawJson, key, parsedValue, kind) {
|
|||
|
|
const arch = typeof archetype === 'string' ? archetype.trim() : ''
|
|||
|
|
const parsed = parseProfileJson(rawJson)
|
|||
|
|
if (!parsed.ok) return parsed
|
|||
|
|
const next = { ...parsed.obj }
|
|||
|
|
|
|||
|
|
if (kind === 'bool') {
|
|||
|
|
if (parsedValue) next[key] = true
|
|||
|
|
else delete next[key]
|
|||
|
|
} else if (kind === 'int') {
|
|||
|
|
if (parsedValue === null || parsedValue === undefined || parsedValue === '') {
|
|||
|
|
delete next[key]
|
|||
|
|
} else {
|
|||
|
|
const n = typeof parsedValue === 'number' ? parsedValue : parseInt(String(parsedValue), 10)
|
|||
|
|
if (!Number.isFinite(n)) delete next[key]
|
|||
|
|
else next[key] = n
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const outJson = JSON.stringify(next)
|
|||
|
|
return { ok: true, obj: next, json: outJson === '{}' ? '{}' : outJson }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function setFullProfileRawJson(rawEditable) {
|
|||
|
|
const parsed = parseProfileJson(rawEditable)
|
|||
|
|
if (!parsed.ok) return parsed
|
|||
|
|
const j = JSON.stringify(parsed.obj)
|
|||
|
|
return { ok: true, obj: parsed.obj, json: j === '{}' ? '{}' : j }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export { parseProfileJson, INT_MAX }
|