From b73c77d811ec609ffaf0b49d13d63498883f7e10 Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 23 Mar 2026 14:18:58 +0100 Subject: [PATCH] feat: improve ProfileBuilder mobile UX and clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - Responsive layout: fields stack vertically, no more cramped grid - Clear labels: 'WAS?', 'BEDINGUNG', 'WICHTIGKEIT' - Weight field only shown when using 'weighted_score' strategy - Weight explanation: '1 = unwichtig, 10 = sehr wichtig' - Success message replaces alert() dialog (auto-dismiss after 2s) - Delete button moved to rule header - Better visual hierarchy with sections User feedback: - Felder lassen sich auf Handy nicht gut bearbeiten - Überschriften nicht eindeutig - Gewicht-Feld Verwirrung - Keine OK-Dialoge Co-Authored-By: Claude Opus 4.6 --- frontend/src/components/ProfileBuilder.jsx | 199 +++++++++++------- frontend/src/pages/AdminTrainingTypesPage.jsx | 9 +- 2 files changed, 124 insertions(+), 84 deletions(-) diff --git a/frontend/src/components/ProfileBuilder.jsx b/frontend/src/components/ProfileBuilder.jsx index dc517d3..27b53f5 100644 --- a/frontend/src/components/ProfileBuilder.jsx +++ b/frontend/src/components/ProfileBuilder.jsx @@ -22,6 +22,7 @@ export default function ProfileBuilder({ trainingType, onSave, onCancel, paramet const [profile, setProfile] = useState(null) const [loading, setLoading] = useState(false) const [expandedSections, setExpandedSections] = useState({ minReq: true }) + const [successMessage, setSuccessMessage] = useState(null) useEffect(() => { // Initialize or load existing profile @@ -146,6 +147,12 @@ export default function ProfileBuilder({ trainingType, onSave, onCancel, paramet setLoading(true) try { await onSave(profile) + setSuccessMessage('✓ Profil gespeichert!') + setTimeout(() => { + setSuccessMessage(null) + }, 2000) + } catch (err) { + // Error is already handled by parent } finally { setLoading(false) } @@ -166,6 +173,21 @@ export default function ProfileBuilder({ trainingType, onSave, onCancel, paramet

+ {/* Success Message */} + {successMessage && ( +
+ {successMessage} +
+ )} + {/* Minimum Requirements */}
param ? op.types.includes(param.data_type) : true ) + const useWeights = minReq.pass_strategy === 'weighted_score' return (
- {/* Labels Row */} -
-
PARAMETER
-
OPERATOR
-
SCHWELLENWERT
-
GEWICHT
-
+
+
+ Regel {idx + 1} +
+
- {/* Input Row */} -
- {/* Parameter */} + {/* Parameter */} +
+ +
- {/* Operator */} - + {/* Operator + Value */} +
+ +
+ - {/* Value */} - {rule.operator === 'between' ? ( -
+ {rule.operator === 'between' ? ( +
+ updateRule(idx, { + value: [parseFloat(e.target.value) || 0, Array.isArray(rule.value) ? rule.value[1] : 0] + })} + style={{ fontSize: '13px', flex: 1 }} + /> + updateRule(idx, { + value: [Array.isArray(rule.value) ? rule.value[0] : 0, parseFloat(e.target.value) || 0] + })} + style={{ fontSize: '13px', flex: 1 }} + /> +
+ ) : ( updateRule(idx, { - value: [parseFloat(e.target.value) || 0, Array.isArray(rule.value) ? rule.value[1] : 0] - })} - style={{ fontSize: '13px', padding: '4px 8px' }} + placeholder="z.B. 90" + value={rule.value} + onChange={(e) => updateRule(idx, { value: parseFloat(e.target.value) || 0 })} + style={{ fontSize: '13px', flex: 1 }} /> - updateRule(idx, { - value: [Array.isArray(rule.value) ? rule.value[0] : 0, parseFloat(e.target.value) || 0] - })} - style={{ fontSize: '13px', padding: '4px 8px' }} - /> -
- ) : ( + )} +
+
+ + {/* Weight - nur bei weighted_score */} + {useWeights && ( +
+ updateRule(idx, { value: parseFloat(e.target.value) || 0 })} - style={{ fontSize: '13px' }} + placeholder="1-10" + min="1" + max="10" + value={rule.weight} + onChange={(e) => updateRule(idx, { weight: parseInt(e.target.value) || 1 })} + style={{ fontSize: '13px', width: '80px' }} /> - )} - - {/* Weight */} - updateRule(idx, { weight: parseInt(e.target.value) || 1 })} - style={{ fontSize: '13px' }} - title="Gewichtung der Regel (1=niedrig, 10=hoch)" - /> - - {/* Delete */} - -
+
+ )} {/* Reason */} - updateRule(idx, { reason: e.target.value })} - style={{ fontSize: '12px' }} - /> +
+ + updateRule(idx, { reason: e.target.value })} + style={{ fontSize: '12px', width: '100%' }} + /> +
{/* Optional Checkbox */} -