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 */}
-