mitai-jinkendo/frontend/src/widgetSystem/BodyHistoryVizConfigEditor.jsx
Lars 20f195aca1
Some checks failed
Deploy Development / deploy (push) Successful in 55s
Build Test / pytest-backend (push) Failing after 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 17s
feat: enhance body_history_viz widget configuration and validation
- Added new configuration options for the `body_history_viz` widget, including visibility settings for various charts and KPIs.
- Implemented default values for the widget's configuration to streamline user experience.
- Enhanced validation logic to ensure proper handling of configuration inputs, including error handling for unknown keys and visibility requirements.
- Updated tests to cover new configuration scenarios and validation rules for the `body_history_viz` widget.
- Bumped application version to reflect these changes.
2026-04-22 08:51:06 +02:00

92 lines
3.8 KiB
JavaScript

import { BODY_HISTORY_VIZ_WIDGET_DEFAULTS, normalizeBodyHistoryVizConfig } from './bodyHistoryVizConfig'
const CHART_TOGGLES = [
{ key: 'show_weight_chart', label: 'Gewichts-Chart' },
{ key: 'show_body_fat_chart', label: 'Körperfett (Caliper)' },
{ key: 'show_proportion_chart', label: 'Silhouette & Proportion' },
{ key: 'show_circumference_index_chart', label: 'Umfänge — Index' },
{ key: 'show_circumference_lines_chart', label: 'Umfänge — Linien (Fallback)' },
]
const OTHER_TOGGLES = [
{ key: 'show_goals_strip', label: 'Körper-Ziele (Strip)' },
{ key: 'show_intro_blurb', label: 'Hinweistext unter Zielen' },
{ key: 'show_layer_meta', label: 'Layer-2a-Hinweis (Meta)' },
{ key: 'show_kpis', label: 'KPI-Kacheln' },
]
/**
* @param {{ config: Record<string, unknown>, onChange: (next: Record<string, unknown>) => void }} props
*/
export default function BodyHistoryVizConfigEditor({ config, onChange }) {
const merged = normalizeBodyHistoryVizConfig(config)
const patch = (partial) => {
const next = { ...merged, ...partial }
const def = BODY_HISTORY_VIZ_WIDGET_DEFAULTS
const stored = {}
for (const k of Object.keys(def)) {
if (next[k] !== def[k]) stored[k] = next[k]
}
onChange(stored)
}
const setBool = (key, checked) => {
patch({ [key]: checked })
}
return (
<div style={{ marginTop: 10, marginLeft: 28 }}>
<div style={{ fontSize: 12, color: 'var(--text2)', marginBottom: 8, lineHeight: 1.5 }}>
<strong>Körper (Verlauf-Bundle):</strong> welche Blöcke auf der Übersicht erscheinen. Unbelegte Felder = schlanker
Standard (nur KPI kompakt + Gewicht).
</div>
<div style={{ fontSize: 12, color: 'var(--text2)', marginBottom: 6 }}>KPI-Umfang</div>
<label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, marginBottom: 10, cursor: 'pointer' }}>
<input
type="radio"
name="body_hist_kpi_detail"
checked={merged.kpi_detail === 'compact'}
onChange={() => patch({ kpi_detail: 'compact' })}
/>
<span>Kompakt (Gewicht, KF%, Magermasse)</span>
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, marginBottom: 12, cursor: 'pointer' }}>
<input
type="radio"
name="body_hist_kpi_detail"
checked={merged.kpi_detail === 'full'}
onChange={() => patch({ kpi_detail: 'full' })}
/>
<span>Voll (wie Verlauf alle Kacheln)</span>
</label>
<div style={{ fontSize: 12, color: 'var(--text2)', marginBottom: 6 }}>Bereiche</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
{OTHER_TOGGLES.map(({ key, label }) => (
<label key={key} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, cursor: 'pointer' }}>
<input type="checkbox" checked={merged[key]} onChange={(e) => setBool(key, e.target.checked)} />
<span>{label}</span>
</label>
))}
</div>
<div style={{ fontSize: 12, color: 'var(--text2)', margin: '12px 0 6px' }}>Charts</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
{CHART_TOGGLES.map(({ key, label }) => (
<label key={key} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, cursor: 'pointer' }}>
<input type="checkbox" checked={merged[key]} onChange={(e) => setBool(key, e.target.checked)} />
<span>{label}</span>
</label>
))}
</div>
<button
type="button"
className="btn btn-secondary"
style={{ marginTop: 10, fontSize: 12, padding: '6px 12px' }}
onClick={() => onChange({})}
>
Auf schlanken Standard zurück
</button>
</div>
)
}