fix: prevent form reset on error, add multi-select for goal/activity/injury
Some checks failed
CI/CD - Build, Push & Deploy / Build & Push Docker Image (push) Has been cancelled
CI/CD - Build, Push & Deploy / Update GitOps Manifest (push) Has been cancelled

- Remove profileNextStep(1) from saveProfile catch block so users stay
  on current step when an error occurs instead of being sent back to step 1
- Convert Goal, Activity Level, and Injury Area fields from single-select
  (radio/dropdown) to multi-select checkboxes with comma-separated storage
- Add validateMultiEnum backend validation for comma-separated enum values
- Update trainer.js filterByInjury and goal checks for multi-value support
- Update dietitian.js TDEE, calorie, and water calculations for multi-values

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 15:49:35 +00:00
parent 8b0e2625f5
commit 7ef6b6f9ee
4 changed files with 94 additions and 62 deletions

View File

@@ -232,6 +232,11 @@
color: var(--gray);
display: block;
}
.hint {
font-size: 0.8rem;
color: var(--gray);
font-weight: 400;
}
/* Checkbox group */
.checkbox-group {
@@ -1159,47 +1164,47 @@
<!-- Step 3: Yasam Tarzi -->
<div id="profileStep3" class="hidden">
<div class="form-group">
<label>Hedef</label>
<div class="radio-group">
<label>Hedef <span class="hint">(birden fazla secilebilir)</span></label>
<div class="checkbox-group">
<label>
<input type="radio" name="profGoal" value="lose_weight">
<input type="checkbox" name="profGoal" value="lose_weight">
<span>Kilo Vermek</span>
</label>
<label>
<input type="radio" name="profGoal" value="gain_weight">
<input type="checkbox" name="profGoal" value="gain_weight">
<span>Kilo Almak</span>
</label>
<label>
<input type="radio" name="profGoal" value="build_muscle">
<input type="checkbox" name="profGoal" value="build_muscle">
<span>Kas Gelistirmek</span>
</label>
<label>
<input type="radio" name="profGoal" value="maintain">
<input type="checkbox" name="profGoal" value="maintain">
<span>Formu Korumak</span>
</label>
</div>
</div>
<div class="form-group">
<label>Aktivite Seviyesi</label>
<div class="radio-group">
<label>Aktivite Seviyesi <span class="hint">(birden fazla secilebilir)</span></label>
<div class="checkbox-group">
<label>
<input type="radio" name="profActivity" value="sedentary">
<input type="checkbox" name="profActivity" value="sedentary">
<span>Hareketsiz<span class="radio-desc">Masa basi is, az hareket</span></span>
</label>
<label>
<input type="radio" name="profActivity" value="light">
<input type="checkbox" name="profActivity" value="light">
<span>Hafif<span class="radio-desc">Haftada 1-2 gun hafif egzersiz</span></span>
</label>
<label>
<input type="radio" name="profActivity" value="moderate">
<input type="checkbox" name="profActivity" value="moderate">
<span>Orta<span class="radio-desc">Haftada 3-5 gun egzersiz</span></span>
</label>
<label>
<input type="radio" name="profActivity" value="active">
<input type="checkbox" name="profActivity" value="active">
<span>Aktif<span class="radio-desc">Haftada 6-7 gun egzersiz</span></span>
</label>
<label>
<input type="radio" name="profActivity" value="very_active">
<input type="checkbox" name="profActivity" value="very_active">
<span>Cok Aktif<span class="radio-desc">Gunde 2 antrenman veya agir fiziksel is</span></span>
</label>
</div>
@@ -1414,18 +1419,17 @@
<div id="injuryFields" class="cond-fields hidden">
<div class="form-row">
<div class="form-group">
<label>Sakatlik Bolgesi</label>
<select id="profInjuryArea">
<option value="none">Seciniz</option>
<option value="knee">Diz</option>
<option value="back">Sirt / Bel</option>
<option value="shoulder">Omuz</option>
<option value="elbow">Dirsek</option>
<option value="wrist">Bilek</option>
<option value="ankle">Ayak Bilegi</option>
<option value="hip">Kalca</option>
<option value="neck">Boyun</option>
</select>
<label>Sakatlik Bolgesi <span class="hint">(birden fazla secilebilir)</span></label>
<div class="checkbox-group">
<label><input type="checkbox" name="profInjuryArea" value="knee"><span>Diz</span></label>
<label><input type="checkbox" name="profInjuryArea" value="back"><span>Sirt / Bel</span></label>
<label><input type="checkbox" name="profInjuryArea" value="shoulder"><span>Omuz</span></label>
<label><input type="checkbox" name="profInjuryArea" value="elbow"><span>Dirsek</span></label>
<label><input type="checkbox" name="profInjuryArea" value="wrist"><span>Bilek</span></label>
<label><input type="checkbox" name="profInjuryArea" value="ankle"><span>Ayak Bilegi</span></label>
<label><input type="checkbox" name="profInjuryArea" value="hip"><span>Kalca</span></label>
<label><input type="checkbox" name="profInjuryArea" value="neck"><span>Boyun</span></label>
</div>
</div>
<div class="form-group">
<label>Sakatlik Siddeti</label>
@@ -2133,8 +2137,14 @@ function populateProfileForm(p) {
setRadioValue('bodyType', p.body_type);
// Step 3
setRadioValue('profGoal', p.goal);
setRadioValue('profActivity', p.activity_level);
if (p.goal) {
var goals = typeof p.goal === 'string' ? p.goal.split(',').map(function(s){return s.trim();}) : [p.goal];
setCheckedValues('profGoal', goals);
}
if (p.activity_level) {
var activities = typeof p.activity_level === 'string' ? p.activity_level.split(',').map(function(s){return s.trim();}) : [p.activity_level];
setCheckedValues('profActivity', activities);
}
if (p.job_type) document.getElementById('profJobType').value = p.job_type;
if (p.sleep_hours) document.getElementById('profSleep').value = p.sleep_hours;
setRadioValue('sleepQuality', p.sleep_quality);
@@ -2170,7 +2180,10 @@ function populateProfileForm(p) {
if (p.has_injury) {
document.getElementById('profHasInjury').checked = true;
toggleInjuryFields();
if (p.injury_area) document.getElementById('profInjuryArea').value = p.injury_area;
if (p.injury_area) {
var areas = typeof p.injury_area === 'string' ? p.injury_area.split(',').map(function(s){return s.trim();}) : [p.injury_area];
setCheckedValues('profInjuryArea', areas);
}
setRadioValue('injurySeverity', p.injury_severity);
if (p.injury_notes) document.getElementById('profInjuryNotes').value = p.injury_notes;
}
@@ -2289,8 +2302,8 @@ async function saveProfile() {
body_type: getRadioValue('bodyType') || null,
// Step 3
goal: getRadioValue('profGoal'),
activity_level: getRadioValue('profActivity'),
goal: getCheckedValues('profGoal').join(','),
activity_level: getCheckedValues('profActivity').join(','),
job_type: document.getElementById('profJobType').value || null,
sleep_hours: parseFloat(document.getElementById('profSleep').value) || null,
sleep_quality: getRadioValue('sleepQuality') || null,
@@ -2310,7 +2323,7 @@ async function saveProfile() {
// Step 5
has_injury: document.getElementById('profHasInjury').checked ? 1 : 0,
injury_area: document.getElementById('profInjuryArea').value || 'none',
injury_area: getCheckedValues('profInjuryArea').join(',') || 'none',
injury_severity: getRadioValue('injurySeverity') || null,
injury_notes: document.getElementById('profInjuryNotes').value || null,
has_disability: document.getElementById('profHasDisability').checked ? 1 : 0,
@@ -2371,7 +2384,7 @@ async function saveProfile() {
} catch (err) {
errEl.textContent = err.message;
errEl.style.display = 'block';
profileNextStep(1);
window.scrollTo({ top: 0, behavior: 'smooth' });
} finally {
btn.disabled = false;
btn.textContent = 'Programimi Olustur';