feat(dashboard): filter AP2 courses strictly by user account profession

This commit is contained in:
Alex Mueller 2026-09-15 08:53:06 +02:00
parent b0a5edaebb
commit 1cf27fbd52
3 changed files with 248 additions and 61 deletions

View file

@ -114,12 +114,12 @@
} }
}, },
async register(name, email, password, role = 'solo', rememberMe = false) { async register(name, email, password, role = 'solo', rememberMe = false, profession = '') {
try { try {
const res = await fetch('/api/auth/register', { const res = await fetch('/api/auth/register', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, email, password, role }) body: JSON.stringify({ name, email, password, role, profession })
}); });
const data = await res.json().catch(() => ({})); const data = await res.json().catch(() => ({}));
if (!res.ok) { if (!res.ok) {
@ -577,11 +577,26 @@
<label style="display: block; font-size: 0.8rem; font-weight: 700; color: #0f172a; margin-bottom: 4px;">Deine Rolle / Lernziel:</label> <label style="display: block; font-size: 0.8rem; font-weight: 700; color: #0f172a; margin-bottom: 4px;">Deine Rolle / Lernziel:</label>
<select id="auth-select-role" style="width: 100%; padding: 9px 12px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 0.88rem; outline: none; background: white; box-sizing: border-box;"> <select id="auth-select-role" style="width: 100%; padding: 9px 12px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 0.88rem; outline: none; background: white; box-sizing: border-box;">
<option value="solo">🎓 Solo-Selbstlerner & Quereinsteiger</option> <option value="solo">🎓 Solo-Selbstlerner & Quereinsteiger</option>
<option value="student">💼 Auszubildender Fachinformatiker (FIAE / FISI)</option> <option value="student">💼 Auszubildender IT-Beruf</option>
<option value="teacher">👨‍🏫 Lehrkraft / Dozent / Ausbilder</option> <option value="teacher">👨‍🏫 Lehrkraft / Dozent / Ausbilder</option>
</select> </select>
</div> </div>
<div id="auth-field-profession" style="display: none;">
<label style="display: block; font-size: 0.8rem; font-weight: 700; color: #0f172a; margin-bottom: 4px;">Dein IT-Ausbildungsberuf (optional):</label>
<select id="auth-select-profession" style="width: 100%; padding: 9px 12px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 0.88rem; outline: none; background: white; box-sizing: border-box;">
<option value="">Keine Angabe / Quereinsteiger</option>
<option value="FISI">🖥️ Fachinformatiker Systemintegration (FISI)</option>
<option value="FIAE">💻 Fachinformatiker Anwendungsentwicklung (FIAE)</option>
<option value="FIDP">📊 Fachinformatiker Daten- &amp; Prozessanalyse (FIDP)</option>
<option value="FIDV">🌐 Fachinformatiker Digitale Vernetzung (FIDV)</option>
<option value="ITSE">⚡ IT-System-Elektroniker/in (ITSE)</option>
<option value="ITSM">💼 Kaufleute für IT-System-Management (ITSM)</option>
<option value="KDM">📈 Kaufleute für Digitalisierungsmanagement (KDM)</option>
</select>
<span style="display: block; font-size: 0.74rem; color: #64748b; margin-top: 3px;">Schaltet deinen spezifischen IHK AP2-Prüfungskurs auf dem Dashboard frei.</span>
</div>
<!-- Session-Einstellung (Standard: nicht angehakt für Login-Zwang nach Schließen des Browsers) --> <!-- Session-Einstellung (Standard: nicht angehakt für Login-Zwang nach Schließen des Browsers) -->
<div style="display: flex; align-items: center; gap: 8px; margin-top: 2px;"> <div style="display: flex; align-items: center; gap: 8px; margin-top: 2px;">
<input type="checkbox" id="auth-remember-me" style="width: 16px; height: 16px; cursor: pointer;"> <input type="checkbox" id="auth-remember-me" style="width: 16px; height: 16px; cursor: pointer;">
@ -618,6 +633,7 @@
const tabReg = document.getElementById('auth-tab-register'); const tabReg = document.getElementById('auth-tab-register');
const nameField = document.getElementById('auth-field-name'); const nameField = document.getElementById('auth-field-name');
const roleField = document.getElementById('auth-field-role'); const roleField = document.getElementById('auth-field-role');
const profField = document.getElementById('auth-field-profession');
const submitBtn = document.getElementById('auth-submit-btn'); const submitBtn = document.getElementById('auth-submit-btn');
const errorBox = document.getElementById('auth-error-box'); const errorBox = document.getElementById('auth-error-box');
const form = document.getElementById('auth-form'); const form = document.getElementById('auth-form');
@ -634,8 +650,9 @@
tabLog.style.color = '#64748b'; tabLog.style.color = '#64748b';
tabLog.style.boxShadow = 'none'; tabLog.style.boxShadow = 'none';
nameField.style.display = 'block'; if (nameField) nameField.style.display = 'block';
roleField.style.display = 'block'; if (roleField) roleField.style.display = 'block';
if (profField) profField.style.display = 'block';
submitBtn.innerText = 'Jetzt kostenlos registrieren →'; submitBtn.innerText = 'Jetzt kostenlos registrieren →';
submitBtn.style.background = '#0284c7'; submitBtn.style.background = '#0284c7';
} else { } else {
@ -648,8 +665,9 @@
tabReg.style.boxShadow = 'none'; tabReg.style.boxShadow = 'none';
} }
nameField.style.display = 'none'; if (nameField) nameField.style.display = 'none';
roleField.style.display = 'none'; if (roleField) roleField.style.display = 'none';
if (profField) profField.style.display = 'none';
submitBtn.innerText = 'Anmelden →'; submitBtn.innerText = 'Anmelden →';
submitBtn.style.background = '#059669'; submitBtn.style.background = '#059669';
} }
@ -678,6 +696,7 @@
const password = document.getElementById('auth-input-password').value; const password = document.getElementById('auth-input-password').value;
const name = document.getElementById('auth-input-name')?.value.trim() || email.split('@')[0]; const name = document.getElementById('auth-input-name')?.value.trim() || email.split('@')[0];
const role = document.getElementById('auth-select-role')?.value || 'solo'; const role = document.getElementById('auth-select-role')?.value || 'solo';
const profession = document.getElementById('auth-select-profession')?.value || '';
const rememberMe = document.getElementById('auth-remember-me')?.checked || false; const rememberMe = document.getElementById('auth-remember-me')?.checked || false;
submitBtn.disabled = true; submitBtn.disabled = true;
@ -685,7 +704,7 @@
let result; let result;
if (isRegister) { if (isRegister) {
result = await window.AUTH.register(name, email, password, role, rememberMe); result = await window.AUTH.register(name, email, password, role, rememberMe, profession);
} else { } else {
result = await window.AUTH.login(email, password, rememberMe); result = await window.AUTH.login(email, password, rememberMe);
} }

View file

@ -257,6 +257,7 @@
<div id="nav-profile-pill" style="display: flex; align-items: center; gap: 6px; background: rgba(255, 255, 255, 0.08); padding: 4px 10px; border-radius: 99px; border: 1px solid rgba(255, 255, 255, 0.12);"> <div id="nav-profile-pill" style="display: flex; align-items: center; gap: 6px; background: rgba(255, 255, 255, 0.08); padding: 4px 10px; border-radius: 99px; border: 1px solid rgba(255, 255, 255, 0.12);">
<span id="nav-auth-user" style="font-size: 0.82rem; font-weight: 700; color: #38bdf8;">👤 Lädt...</span> <span id="nav-auth-user" style="font-size: 0.82rem; font-weight: 700; color: #38bdf8;">👤 Lädt...</span>
<span id="nav-auth-role" style="font-size: 0.68rem; font-weight: 800; background: rgba(56, 189, 248, 0.15); color: #7dd3fc; padding: 2px 6px; border-radius: 99px; text-transform: uppercase;"></span> <span id="nav-auth-role" style="font-size: 0.68rem; font-weight: 800; background: rgba(56, 189, 248, 0.15); color: #7dd3fc; padding: 2px 6px; border-radius: 99px; text-transform: uppercase;"></span>
<span id="nav-auth-profession" style="display: none; font-size: 0.68rem; font-weight: 800; background: rgba(16, 185, 129, 0.25); color: #34d399; padding: 2px 7px; border-radius: 99px; text-transform: uppercase; border: 1px solid rgba(16, 185, 129, 0.4);" title="Aktiver IT-Ausbildungsberuf"></span>
</div> </div>
<button type="button" class="btn" id="nav-btn-logout" style="padding: 6px 12px; font-size: 0.82rem; background: rgba(239, 68, 68, 0.15); color: #f87171; border: 1px solid rgba(239, 68, 68, 0.3);"> <button type="button" class="btn" id="nav-btn-logout" style="padding: 6px 12px; font-size: 0.82rem; background: rgba(239, 68, 68, 0.15); color: #f87171; border: 1px solid rgba(239, 68, 68, 0.3);">
<span class="btn-text-desktop">Abmelden</span> <span class="btn-text-desktop">Abmelden</span>
@ -374,16 +375,16 @@
<!-- Category Filter Tabs --> <!-- Category Filter Tabs -->
<div class="portal-filter-tabs" style="margin-bottom: 20px;"> <div class="portal-filter-tabs" style="margin-bottom: 20px;">
<button type="button" class="portal-tab-pill active" onclick="filterCourses('all', this)">🌟 Alle Fachbereiche (16)</button> <button type="button" class="portal-tab-pill active" id="tab-pill-all" onclick="filterCourses('all', this)">🌟 Alle Fachbereiche</button>
<button type="button" class="portal-tab-pill" onclick="filterCourses('ihk', this)">🎓 IHK Prüfung (2)</button> <button type="button" class="portal-tab-pill" id="tab-pill-ihk" onclick="filterCourses('ihk', this)">🎓 IHK Prüfung</button>
<button type="button" class="portal-tab-pill" onclick="filterCourses('devops', this)">🐧 DevOps, Linux &amp; AD (5)</button> <button type="button" class="portal-tab-pill" id="tab-pill-devops" onclick="filterCourses('devops', this)">🐧 DevOps, Linux &amp; AD</button>
<button type="button" class="portal-tab-pill" onclick="filterCourses('coding', this)">💻 Backend &amp; Systems (6)</button> <button type="button" class="portal-tab-pill" id="tab-pill-coding" onclick="filterCourses('coding', this)">💻 Backend &amp; Systems</button>
<button type="button" class="portal-tab-pill" onclick="filterCourses('data', this)">🗄️ Daten &amp; Web (3)</button> <button type="button" class="portal-tab-pill" id="tab-pill-data" onclick="filterCourses('data', this)">🗄️ Daten &amp; Web</button>
</div> </div>
<!-- AUSBILDUNGSPLAN & KMK-LERNFELDER BEREICH --> <!-- AUSBILDUNGSPLAN & KMK-LERNFELDER BEREICH -->
<div class="apprenticeship-panel" id="azubi-panel"> <div class="apprenticeship-panel" id="azubi-panel">
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px; margin-bottom: 16px;"> <div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px; margin-bottom: 12px;">
<div style="display: flex; align-items: center; gap: 12px;"> <div style="display: flex; align-items: center; gap: 12px;">
<div style="width: 42px; height: 42px; border-radius: 10px; background: linear-gradient(135deg, #0284c7, #1d4ed8); display: flex; align-items: center; justify-content: center; font-size: 1.4rem; color: white;"> <div style="width: 42px; height: 42px; border-radius: 10px; background: linear-gradient(135deg, #0284c7, #1d4ed8); display: flex; align-items: center; justify-content: center; font-size: 1.4rem; color: white;">
🎓 🎓
@ -395,20 +396,28 @@
</div> </div>
<!-- Profession Dropdown & Save Button --> <!-- Profession Dropdown & Save Button -->
<div style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;"> <div style="display: flex; flex-direction: column; align-items: flex-end; gap: 6px;">
<label for="azubi-prof-select" style="font-size: 0.85rem; font-weight: 700; color: #475569;">Mein IT-Beruf:</label> <div style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
<select id="azubi-prof-select" onchange="onProfessionChanged()" style="padding: 7px 12px; font-size: 0.86rem; font-weight: 600; border-radius: 8px; border: 1px solid #cbd5e1; background: #f8fafc; color: #0f172a; cursor: pointer;"> <label for="azubi-prof-select" style="font-size: 0.85rem; font-weight: 700; color: #475569;">Mein IT-Beruf:</label>
<option value="FISI">🖥️ FISI - Systemintegration</option> <select id="azubi-prof-select" onchange="onProfessionChanged()" style="padding: 7px 12px; font-size: 0.86rem; font-weight: 600; border-radius: 8px; border: 1px solid #cbd5e1; background: #f8fafc; color: #0f172a; cursor: pointer;">
<option value="FIAE">💻 FIAE - Anwendungsentwicklung</option> <option value="">-- Kein Ausbildungsberuf gewählt --</option>
<option value="FIDP">📊 FIDP - Daten- &amp; Prozessanalyse</option> <option value="FISI">🖥️ FISI - Systemintegration</option>
<option value="FIDV">🌐 FIDV - Digitale Vernetzung</option> <option value="FIAE">💻 FIAE - Anwendungsentwicklung</option>
<option value="ITSE">⚡ ITSE - IT-System-Elektronik</option> <option value="FIDP">📊 FIDP - Daten- &amp; Prozessanalyse</option>
<option value="ITSM">💼 ITSM - IT-System-Management</option> <option value="FIDV">🌐 FIDV - Digitale Vernetzung</option>
<option value="KDM">📈 KDM - Digitalisierungsmanagement</option> <option value="ITSE">⚡ ITSE - IT-System-Elektronik</option>
</select> <option value="ITSM">💼 ITSM - IT-System-Management</option>
<button type="button" class="btn" id="btn-save-apprenticeship" onclick="saveApprenticeshipChoice()" style="background: #0284c7; color: white; padding: 7px 14px; font-size: 0.82rem; font-weight: 700; border-radius: 8px; border: none; cursor: pointer;" title="Beruf und Lehrjahr in deinem Profil speichern"> <option value="KDM">📈 KDM - Digitalisierungsmanagement</option>
💾 Speichern <option value="ALL">🌟 Alle Ausbildungsberufe anzeigen (Dozent / Admin)</option>
</button> </select>
<button type="button" class="btn" id="btn-save-apprenticeship" onclick="saveApprenticeshipChoice()" style="background: #0284c7; color: white; padding: 7px 14px; font-size: 0.82rem; font-weight: 700; border-radius: 8px; border: none; cursor: pointer;" title="Beruf in deinem Account speichern">
💾 Im Account speichern
</button>
</div>
<!-- Account Status Info Badge -->
<div id="azubi-prof-account-badge" style="font-size: 0.78rem; display: flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 6px; background: #f1f5f9; border: 1px solid #e2e8f0; color: #475569;">
<span id="azubi-prof-account-msg">Lade Berufsstatus...</span>
</div>
</div> </div>
</div> </div>
@ -1189,7 +1198,7 @@
</div> </div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 18px; margin-top: 16px;"> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 18px; margin-top: 16px;">
<div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);"> <div class="ihk-quick-card" data-ihk-profession="common" style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);">
<h4 style="margin: 0 0 6px 0; color: #1e3a8a; font-size: 1.05rem;">📘 AP1: IT-Arbeitsplatz (LF 1–8)</h4> <h4 style="margin: 0 0 6px 0; color: #1e3a8a; font-size: 1.05rem;">📘 AP1: IT-Arbeitsplatz (LF 1–8)</h4>
<p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Gemeinsame Kernqualifikationen für alle IT-Berufe: Verträge, Ergonomie, Subnetting, BSI, BPMN 2.0, Belege &amp; Netzplan.</p> <p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Gemeinsame Kernqualifikationen für alle IT-Berufe: Verträge, Ergonomie, Subnetting, BSI, BPMN 2.0, Belege &amp; Netzplan.</p>
<a href="workspace.html?course=ihk_ap1&chapter=01_lf1_unternehmen_und_arbeitsplatz" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px;"> <a href="workspace.html?course=ihk_ap1&chapter=01_lf1_unternehmen_und_arbeitsplatz" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px;">
@ -1197,7 +1206,7 @@
</a> </a>
</div> </div>
<div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);"> <div class="ihk-quick-card" data-ihk-profession="FISI" style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);">
<h4 style="margin: 0 0 6px 0; color: #991b1b; font-size: 1.05rem;">📕 AP2: Systemintegration (FISI)</h4> <h4 style="margin: 0 0 6px 0; color: #991b1b; font-size: 1.05rem;">📕 AP2: Systemintegration (FISI)</h4>
<p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">VLAN &amp; Routing, VRT-Symbole, Active Directory, RAID 0-10, Backup 3-2-1-1-0, Enterprise SQL &amp; 40h-Projektleitfaden.</p> <p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">VLAN &amp; Routing, VRT-Symbole, Active Directory, RAID 0-10, Backup 3-2-1-1-0, Enterprise SQL &amp; 40h-Projektleitfaden.</p>
<a href="workspace.html?course=ihk_ap2_fisi&chapter=01_lf9_netzwerkinfrastruktur_und_routing" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #991b1b;"> <a href="workspace.html?course=ihk_ap2_fisi&chapter=01_lf9_netzwerkinfrastruktur_und_routing" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #991b1b;">
@ -1205,7 +1214,7 @@
</a> </a>
</div> </div>
<div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);"> <div class="ihk-quick-card" data-ihk-profession="FIAE" style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);">
<h4 style="margin: 0 0 6px 0; color: #0369a1; font-size: 1.05rem;">💻 AP2: Anwendungsentwicklung (FIAE)</h4> <h4 style="margin: 0 0 6px 0; color: #0369a1; font-size: 1.05rem;">💻 AP2: Anwendungsentwicklung (FIAE)</h4>
<p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Clean Architecture, GoF-Entwurfsmuster, UML 2.5, Algorithmen (O-Notation), OOP &amp; SOLID, REST/GraphQL &amp; 80h-Projektleitfaden.</p> <p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Clean Architecture, GoF-Entwurfsmuster, UML 2.5, Algorithmen (O-Notation), OOP &amp; SOLID, REST/GraphQL &amp; 80h-Projektleitfaden.</p>
<a href="workspace.html?course=ihk_ap2_fiae&chapter=01_software_architektur_und_design_patterns" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #0369a1;"> <a href="workspace.html?course=ihk_ap2_fiae&chapter=01_software_architektur_und_design_patterns" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #0369a1;">
@ -1213,7 +1222,7 @@
</a> </a>
</div> </div>
<div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);"> <div class="ihk-quick-card" data-ihk-profession="FIDP" style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);">
<h4 style="margin: 0 0 6px 0; color: #0d9488; font-size: 1.05rem;">📊 AP2: Daten- &amp; Prozessanalyse (FIDP)</h4> <h4 style="margin: 0 0 6px 0; color: #0d9488; font-size: 1.05rem;">📊 AP2: Daten- &amp; Prozessanalyse (FIDP)</h4>
<p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Prozessoptimierung, BPMN 2.0, ISO 25012 Datenqualität, ETL/CDC, Star-Schema, Statistik, ML-Evaluation &amp; 40h-Projektleitfaden.</p> <p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Prozessoptimierung, BPMN 2.0, ISO 25012 Datenqualität, ETL/CDC, Star-Schema, Statistik, ML-Evaluation &amp; 40h-Projektleitfaden.</p>
<a href="workspace.html?course=ihk_ap2_fidp&chapter=01_prozessanalyse_und_prozessoptimierung" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #0d9488;"> <a href="workspace.html?course=ihk_ap2_fidp&chapter=01_prozessanalyse_und_prozessoptimierung" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #0d9488;">
@ -1221,7 +1230,7 @@
</a> </a>
</div> </div>
<div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);"> <div class="ihk-quick-card" data-ihk-profession="FIDV" style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);">
<h4 style="margin: 0 0 6px 0; color: #b45309; font-size: 1.05rem;">🏭 AP2: Digitale Vernetzung (FIDV)</h4> <h4 style="margin: 0 0 6px 0; color: #b45309; font-size: 1.05rem;">🏭 AP2: Digitale Vernetzung (FIDV)</h4>
<p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Cyber-Physische Systeme, RAMI 4.0, IO-Link, Profinet IO, TSN, OPC UA/MQTT, 5G-Campus, IEC 62443, Purdue &amp; 40h-Projekt.</p> <p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Cyber-Physische Systeme, RAMI 4.0, IO-Link, Profinet IO, TSN, OPC UA/MQTT, 5G-Campus, IEC 62443, Purdue &amp; 40h-Projekt.</p>
<a href="workspace.html?course=ihk_ap2_fidv&chapter=01_industrie40_und_cyber_physische_systeme" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #b45309;"> <a href="workspace.html?course=ihk_ap2_fidv&chapter=01_industrie40_und_cyber_physische_systeme" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #b45309;">
@ -1229,7 +1238,7 @@
</a> </a>
</div> </div>
<div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);"> <div class="ihk-quick-card" data-ihk-profession="ITSE" style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);">
<h4 style="margin: 0 0 6px 0; color: #d97706; font-size: 1.05rem;">⚡ AP2: IT-System-Elektroniker (ITSE)</h4> <h4 style="margin: 0 0 6px 0; color: #d97706; font-size: 1.05rem;">⚡ AP2: IT-System-Elektroniker (ITSE)</h4>
<p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">VDE 0100/0701, DGUV V3, USV (VFI), LWL-Spleißen &amp; OTDR, EMV, Brandschottung &amp; 40h-Projektleitfaden.</p> <p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">VDE 0100/0701, DGUV V3, USV (VFI), LWL-Spleißen &amp; OTDR, EMV, Brandschottung &amp; 40h-Projektleitfaden.</p>
<a href="workspace.html?course=ihk_ap2_itse&chapter=01_elektrotechnik_und_schutzmassnahmen_vde0100" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #d97706;"> <a href="workspace.html?course=ihk_ap2_itse&chapter=01_elektrotechnik_und_schutzmassnahmen_vde0100" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #d97706;">
@ -1237,7 +1246,7 @@
</a> </a>
</div> </div>
<div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);"> <div class="ihk-quick-card" data-ihk-profession="ITSM" style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);">
<h4 style="margin: 0 0 6px 0; color: #0284c7; font-size: 1.05rem;">💼 AP2: IT-System-Management (ITSM)</h4> <h4 style="margin: 0 0 6px 0; color: #0284c7; font-size: 1.05rem;">💼 AP2: IT-System-Management (ITSM)</h4>
<p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">7P-Marketing, Handelskalkulation, TCO/ROI, EVB-IT Verträge, SLAs, ITIL &amp; 40h-Projektleitfaden.</p> <p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">7P-Marketing, Handelskalkulation, TCO/ROI, EVB-IT Verträge, SLAs, ITIL &amp; 40h-Projektleitfaden.</p>
<a href="workspace.html?course=ihk_ap2_itsm&chapter=01_it_marktanalyse_wettbewerb_und_swot" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #0284c7;"> <a href="workspace.html?course=ihk_ap2_itsm&chapter=01_it_marktanalyse_wettbewerb_und_swot" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #0284c7;">
@ -1245,7 +1254,7 @@
</a> </a>
</div> </div>
<div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);"> <div class="ihk-quick-card" data-ihk-profession="KDM" style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);">
<h4 style="margin: 0 0 6px 0; color: #059669; font-size: 1.05rem;">📈 AP2: Digitalisierungsmanagement (KDM)</h4> <h4 style="margin: 0 0 6px 0; color: #059669; font-size: 1.05rem;">📈 AP2: Digitalisierungsmanagement (KDM)</h4>
<p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Business Model Canvas, Plattformökonomie, BPMN 2.0, GoBD/DMS, XRechnung, DSFA &amp; 40h-Projektleitfaden.</p> <p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Business Model Canvas, Plattformökonomie, BPMN 2.0, GoBD/DMS, XRechnung, DSFA &amp; 40h-Projektleitfaden.</p>
<a href="workspace.html?course=ihk_ap2_kdm&chapter=01_digitale_geschaeftsmodelle_und_bmc" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #059669;"> <a href="workspace.html?course=ihk_ap2_kdm&chapter=01_digitale_geschaeftsmodelle_und_bmc" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #059669;">
@ -1253,13 +1262,25 @@
</a> </a>
</div> </div>
<div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);"> <div class="ihk-quick-card" data-ihk-profession="common" style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: var(--shadow-sm);">
<h4 style="margin: 0 0 6px 0; color: #0f172a; font-size: 1.05rem;">⚡ Subnetting-Drill Master</h4> <h4 style="margin: 0 0 6px 0; color: #0f172a; font-size: 1.05rem;">⚡ Subnetting-Drill Master</h4>
<p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Interaktiver Rechentrainer: Netz-ID, Broadcast-Adresse, Hostbereich &amp; Präfixlängen im Sekundentakt trainieren.</p> <p style="font-size: 0.86rem; color: #64748b; margin: 0 0 14px 0;">Interaktiver Rechentrainer: Netz-ID, Broadcast-Adresse, Hostbereich &amp; Präfixlängen im Sekundentakt trainieren.</p>
<a href="courses/ihk_ap1/11_subnetting_drill_master/index.html" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #0f172a;"> <a href="courses/ihk_ap1/11_subnetting_drill_master/index.html" class="btn-start-ide" style="font-size: 0.84rem; padding: 8px 14px; background: #0f172a;">
<span>▶ Subnetting-Drill öffnen</span> <span>▶ Subnetting-Drill öffnen</span>
</a> </a>
</div> </div>
<!-- Hinweisfeld wenn kein Ausbildungsberuf im Account gewählt ist -->
<div id="ihk-no-prof-hint" style="display: none; grid-column: 1 / -1; background: #f8fafc; border: 2px dashed #94a3b8; border-radius: 12px; padding: 24px; text-align: center;">
<div style="font-size: 2rem; margin-bottom: 8px;">🔒</div>
<h4 style="margin: 0 0 6px 0; color: #1e293b; font-size: 1.05rem; font-weight: 800;">Fachspezifischer AP2-Prüfungskurs gesperrt</h4>
<p style="font-size: 0.88rem; color: #64748b; margin: 0 0 14px 0; max-width: 580px; margin-inline: auto;">
Auf deinem Dashboard werden nur Kurse für deinen im Account hinterlegten Ausbildungsberuf angezeigt. Wähle oben im Ausbildungsplan deinen IT-Beruf aus und klicke auf Speichern, um deinen passenden AP2-Trainer freizuschalten.
</p>
<button type="button" onclick="document.getElementById('azubi-panel').scrollIntoView({behavior:'smooth'})" class="btn" style="background: #0284c7; color: white; padding: 8px 18px; font-weight: 700; border: none; border-radius: 8px; cursor: pointer;">
🎓 IT-Ausbildungsberuf wählen &rarr;
</button>
</div>
</div> </div>
</section> </section>
@ -1481,18 +1502,83 @@
} catch (e) {} } catch (e) {}
} }
// Profession-Mapping: Welche AP2-Kurse gehören zu welchem Ausbildungsberuf?
const AP2_PROFESSION_MAP = {
'ihk_ap2_fisi': 'FISI',
'ihk_ap2_fiae': 'FIAE',
'ihk_ap2_fidp': 'FIDP',
'ihk_ap2_fidv': 'FIDV',
'ihk_ap2_itse': 'ITSE',
'ihk_ap2_itsm': 'ITSM',
'ihk_ap2_kdm': 'KDM'
};
const PROFESSION_NAMES = {
'FISI': 'Fachinformatiker Systemintegration (FISI)',
'FIAE': 'Fachinformatiker Anwendungsentwicklung (FIAE)',
'FIDP': 'Fachinformatiker Daten- & Prozessanalyse (FIDP)',
'FIDV': 'Fachinformatiker Digitale Vernetzung (FIDV)',
'ITSE': 'IT-System-Elektroniker/in (ITSE)',
'ITSM': 'Kaufleute für IT-System-Management (ITSM)',
'KDM': 'Kaufleute für Digitalisierungsmanagement (KDM)',
'ALL': 'Alle 7 IT-Berufe (Dozent / Admin)'
};
// Filter State // Filter State
let currentCategory = 'all'; let currentCategory = 'all';
let currentYear = 'all'; // 'all', 1, 2, 3, or 'assigned' let currentYear = 'all'; // 'all', 1, 2, 3, or 'assigned'
let currentProfession = 'FISI'; let accountProfession = ''; // Im DB-Account hinterlegter Beruf
let currentProfession = ''; // Aktuelle Dropdown-Auswahl im Dashboard
let userAssignedCourses = []; let userAssignedCourses = [];
// Helper: Berufs-Status-Badge im Azubi-Panel aktualisieren
function updateProfessionStatusBadge() {
const msgEl = document.getElementById('azubi-prof-account-msg');
const badgeEl = document.getElementById('azubi-prof-account-badge');
const navProf = document.getElementById('nav-auth-profession');
if (!msgEl || !badgeEl) return;
if (navProf) {
if (currentProfession && currentProfession !== 'ALL') {
navProf.innerText = currentProfession;
navProf.style.display = 'inline-block';
} else if (currentProfession === 'ALL') {
navProf.innerText = 'ALLE BERUFE';
navProf.style.display = 'inline-block';
} else {
navProf.style.display = 'none';
}
}
if (!currentProfession) {
badgeEl.style.background = '#fef2f2';
badgeEl.style.borderColor = '#fecaca';
badgeEl.style.color = '#991b1b';
msgEl.innerHTML = '⚠️ <strong>Kein Ausbildungsberuf im Account angegeben:</strong> Spezifische AP2-Prüfungskurse sind ausgeblendet. Wähle oben deinen Beruf und klicke auf Speichern.';
} else if (currentProfession === 'ALL') {
badgeEl.style.background = '#eff6ff';
badgeEl.style.borderColor = '#bfdbfe';
badgeEl.style.color = '#1e40af';
msgEl.innerHTML = '🌟 <strong>Dozenten- &amp; Admin-Vollansicht:</strong> Alle 7 IT-Ausbildungsberufe sind auf dem Dashboard sichtbar.';
} else if (currentProfession === accountProfession) {
badgeEl.style.background = '#f0fdf4';
badgeEl.style.borderColor = '#bbf7d0';
badgeEl.style.color = '#166534';
msgEl.innerHTML = `✅ <strong>Im Account gespeichert:</strong> ${PROFESSION_NAMES[currentProfession] || currentProfession}. Es wird exklusiv dein passender AP2-Prüfungskurs angezeigt.`;
} else {
badgeEl.style.background = '#fffbeb';
badgeEl.style.borderColor = '#fde68a';
badgeEl.style.color = '#92400e';
msgEl.innerHTML = `⏳ <strong>Vorschau für:</strong> ${PROFESSION_NAMES[currentProfession] || currentProfession} (noch nicht im Account gespeichert). Klicke auf <strong>💾 Im Account speichern</strong>.`;
}
}
// Unified Card Filtering // Unified Card Filtering
function applyAllFilters() { function applyAllFilters() {
const cards = document.querySelectorAll('.portal-card'); const cards = document.querySelectorAll('.portal-card');
const query = (document.getElementById('course-filter-input').value || '').toLowerCase().trim(); const query = (document.getElementById('course-filter-input').value || '').toLowerCase().trim();
const manifestProf = (window.IT_AUSBILDUNGEN && window.IT_AUSBILDUNGEN.getProfession) const manifestProf = (window.IT_AUSBILDUNGEN && window.IT_AUSBILDUNGEN.getProfession && currentProfession && currentProfession !== 'ALL')
? window.IT_AUSBILDUNGEN.getProfession(currentProfession) ? window.IT_AUSBILDUNGEN.getProfession(currentProfession)
: null; : null;
@ -1502,19 +1588,34 @@
} }
let visibleCount = 0; let visibleCount = 0;
let counts = { all: 0, ihk: 0, devops: 0, coding: 0, data: 0 };
cards.forEach(card => { cards.forEach(card => {
const cardCat = card.getAttribute('data-category'); const cardCat = card.getAttribute('data-category');
const cardId = card.getAttribute('data-course-id'); const cardId = card.getAttribute('data-course-id');
const cardTitle = (card.getAttribute('data-title') || '').toLowerCase(); const cardTitle = (card.getAttribute('data-title') || '').toLowerCase();
// 1. Category check // 1. Ausbildungsberuf-Prüfung:
// AP2 Kurse für Ausbildungsberufe sieht man NUR, wenn dieser Beruf im Account / Filter gewählt ist!
const requiredProf = AP2_PROFESSION_MAP[cardId];
let matchesProfession = true;
if (requiredProf) {
if (currentProfession === 'ALL') {
matchesProfession = true;
} else if (currentProfession && currentProfession === requiredProf) {
matchesProfession = true;
} else {
matchesProfession = false;
}
}
// 2. Category check
const matchesCategory = (currentCategory === 'all' || cardCat === currentCategory); const matchesCategory = (currentCategory === 'all' || cardCat === currentCategory);
// 2. Search query check // 3. Search query check
const matchesQuery = (!query || cardTitle.includes(query) || card.innerText.toLowerCase().includes(query)); const matchesQuery = (!query || cardTitle.includes(query) || card.innerText.toLowerCase().includes(query));
// 3. Year / Assigned check // 4. Year / Assigned check
let matchesYearOrAssigned = true; let matchesYearOrAssigned = true;
if (currentYear === 'assigned') { if (currentYear === 'assigned') {
matchesYearOrAssigned = userAssignedCourses.includes(cardId); matchesYearOrAssigned = userAssignedCourses.includes(cardId);
@ -1522,13 +1623,54 @@
matchesYearOrAssigned = yearAllowedCourses.includes(cardId); matchesYearOrAssigned = yearAllowedCourses.includes(cardId);
} }
if (matchesCategory && matchesQuery && matchesYearOrAssigned) { // Zähler für Fachbereichs-Pills nur bei erlaubtem Beruf mitzählen
if (matchesProfession) {
counts.all++;
if (cardCat && counts[cardCat] !== undefined) {
counts[cardCat]++;
}
}
if (matchesProfession && matchesCategory && matchesQuery && matchesYearOrAssigned) {
card.style.display = 'flex'; card.style.display = 'flex';
visibleCount++; visibleCount++;
} else { } else {
card.style.display = 'none'; card.style.display = 'none';
} }
}); });
// Filter auch auf den IHK AP1 & AP2 Schnellwahl-Bereich anwenden
const ihkCards = document.querySelectorAll('.ihk-quick-card');
ihkCards.forEach(card => {
const prof = card.getAttribute('data-ihk-profession');
if (!prof || prof === 'common') {
card.style.display = 'block';
} else if (currentProfession === 'ALL') {
card.style.display = 'block';
} else if (currentProfession && currentProfession === prof) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
const noProfHint = document.getElementById('ihk-no-prof-hint');
if (noProfHint) {
noProfHint.style.display = (!currentProfession || currentProfession === '') ? 'block' : 'none';
}
// Tab Pills Zähler dynamisch aktualisieren
const pillAll = document.getElementById('tab-pill-all');
const pillIhk = document.getElementById('tab-pill-ihk');
const pillDevops = document.getElementById('tab-pill-devops');
const pillCoding = document.getElementById('tab-pill-coding');
const pillData = document.getElementById('tab-pill-data');
if (pillAll) pillAll.innerText = `🌟 Alle Fachbereiche (${counts.all})`;
if (pillIhk) pillIhk.innerText = `🎓 IHK Prüfung (${counts.ihk})`;
if (pillDevops) pillDevops.innerText = `🐧 DevOps, Linux & AD (${counts.devops})`;
if (pillCoding) pillCoding.innerText = `💻 Backend & Systems (${counts.coding})`;
if (pillData) pillData.innerText = `🗄️ Daten & Web (${counts.data})`;
} }
// 3. Live Category Filter // 3. Live Category Filter
@ -1562,6 +1704,7 @@
const select = document.getElementById('azubi-prof-select'); const select = document.getElementById('azubi-prof-select');
if (select) { if (select) {
currentProfession = select.value; currentProfession = select.value;
updateProfessionStatusBadge();
renderLernfelder(currentProfession, currentYear); renderLernfelder(currentProfession, currentYear);
applyAllFilters(); applyAllFilters();
} }
@ -1570,7 +1713,7 @@
// 3e. Save Apprenticeship Choice to Backend // 3e. Save Apprenticeship Choice to Backend
async function saveApprenticeshipChoice() { async function saveApprenticeshipChoice() {
const select = document.getElementById('azubi-prof-select'); const select = document.getElementById('azubi-prof-select');
const prof = select ? select.value : 'FISI'; const prof = select ? select.value : '';
const yr = (typeof currentYear === 'number') ? currentYear : 1; const yr = (typeof currentYear === 'number') ? currentYear : 1;
const btn = document.getElementById('btn-save-apprenticeship'); const btn = document.getElementById('btn-save-apprenticeship');
if (btn) btn.innerText = 'Speichere...'; if (btn) btn.innerText = 'Speichere...';
@ -1587,11 +1730,14 @@
body: JSON.stringify({ profession: prof, training_year: yr }) body: JSON.stringify({ profession: prof, training_year: yr })
}); });
if (res.ok) { if (res.ok) {
accountProfession = prof;
currentProfession = prof;
updateProfessionStatusBadge();
if (btn) { if (btn) {
btn.innerText = '✓ Gespeichert'; btn.innerText = '✓ Im Account gespeichert!';
btn.style.background = '#10b981'; btn.style.background = '#10b981';
setTimeout(() => { setTimeout(() => {
btn.innerText = '💾 Speichern'; btn.innerText = '💾 Im Account speichern';
btn.style.background = '#0284c7'; btn.style.background = '#0284c7';
}, 2000); }, 2000);
} }
@ -1601,15 +1747,20 @@
user.training_year = yr; user.training_year = yr;
localStorage.setItem('auth_user', JSON.stringify(user)); localStorage.setItem('auth_user', JSON.stringify(user));
} }
applyAllFilters();
} else { } else {
if (btn) btn.innerText = 'Fehler'; if (btn) btn.innerText = 'Fehler beim Speichern';
} }
} catch (e) { } catch (e) {
if (btn) btn.innerText = 'Fehler'; if (btn) btn.innerText = 'Fehler beim Speichern';
} }
} else { } else {
accountProfession = prof;
currentProfession = prof;
updateProfessionStatusBadge();
if (btn) btn.innerText = '✓ Lokal gemerkt'; if (btn) btn.innerText = '✓ Lokal gemerkt';
setTimeout(() => { if (btn) btn.innerText = '💾 Speichern'; }, 2000); setTimeout(() => { if (btn) btn.innerText = '💾 Im Account speichern'; }, 2000);
applyAllFilters();
} }
} }
@ -1743,11 +1894,11 @@
roleEl.innerText = user.role === 'admin' ? 'Admin' : (user.role === 'teacher' ? 'Dozent' : 'Schüler'); roleEl.innerText = user.role === 'admin' ? 'Admin' : (user.role === 'teacher' ? 'Dozent' : 'Schüler');
} }
if (user.profession) { accountProfession = user.profession || '';
currentProfession = user.profession; currentProfession = accountProfession || ((user.role === 'admin' || user.role === 'teacher') ? 'ALL' : '');
const profSel = document.getElementById('azubi-prof-select'); const profSel = document.getElementById('azubi-prof-select');
if (profSel) profSel.value = user.profession; if (profSel) profSel.value = currentProfession;
} updateProfessionStatusBadge();
if (user.training_year) { if (user.training_year) {
const yrPill = document.getElementById(`pill-year-${user.training_year}`); const yrPill = document.getElementById(`pill-year-${user.training_year}`);
@ -1776,6 +1927,12 @@
const mTl = document.getElementById('mobile-drawer-teacher-link'); const mTl = document.getElementById('mobile-drawer-teacher-link');
if (mTl) mTl.style.display = 'flex'; if (mTl) mTl.style.display = 'flex';
} }
} else {
accountProfession = '';
currentProfession = '';
const profSel = document.getElementById('azubi-prof-select');
if (profSel) profSel.value = '';
updateProfessionStatusBadge();
} }
// Gelöste Kapitel sammeln (localStorage + API) // Gelöste Kapitel sammeln (localStorage + API)
@ -1827,10 +1984,12 @@
if (resMe.ok) { if (resMe.ok) {
const meData = await resMe.json(); const meData = await resMe.json();
if (meData.user) { if (meData.user) {
if (meData.user.profession) { if (meData.user.profession !== undefined) {
currentProfession = meData.user.profession; accountProfession = meData.user.profession || '';
currentProfession = accountProfession || ((user && (user.role === 'admin' || user.role === 'teacher')) ? 'ALL' : '');
const profSel = document.getElementById('azubi-prof-select'); const profSel = document.getElementById('azubi-prof-select');
if (profSel) profSel.value = currentProfession; if (profSel) profSel.value = currentProfession;
updateProfessionStatusBadge();
} }
if (meData.user.training_year && currentYear === 'all') { if (meData.user.training_year && currentYear === 'all') {
const yrPill = document.getElementById(`pill-year-${meData.user.training_year}`); const yrPill = document.getElementById(`pill-year-${meData.user.training_year}`);
@ -1847,6 +2006,7 @@
if (countTxt) countTxt.innerText = `Dir wurden ${userAssignedCourses.length} Kurse von deinem Ausbilder zugeteilt.`; if (countTxt) countTxt.innerText = `Dir wurden ${userAssignedCourses.length} Kurse von deinem Ausbilder zugeteilt.`;
} }
} }
applyAllFilters();
} }
} }
} catch (e) { } catch (e) {

View file

@ -568,7 +568,7 @@ class PlatformRequestHandler(http.server.SimpleHTTPRequestHandler):
return self.send_json({ return self.send_json({
"classrooms": parsed_classes, "classrooms": parsed_classes,
"assigned_courses": list(all_assigned), "assigned_courses": list(all_assigned),
"profession": (u_row and u_row["profession"]) or (parsed_classes and parsed_classes[0].get("profession")) or "FISI", "profession": (u_row["profession"] if (u_row and u_row["profession"] is not None) else "") or (parsed_classes and parsed_classes[0].get("profession")) or "",
"training_year": (u_row and u_row["training_year"]) or (parsed_classes and parsed_classes[0].get("training_year")) or 1 "training_year": (u_row and u_row["training_year"]) or (parsed_classes and parsed_classes[0].get("training_year")) or 1
}) })
@ -761,19 +761,23 @@ class PlatformRequestHandler(http.server.SimpleHTTPRequestHandler):
if role not in ("student", "teacher", "solo"): if role not in ("student", "teacher", "solo"):
role = "solo" role = "solo"
profession = str(body.get("profession", "")).strip()[:10]
if profession not in ("FISI", "FIAE", "FIDP", "FIDV", "ITSE", "ITSM", "KDM", "ALL"):
profession = ""
pwd_hash, salt = hash_password(password) pwd_hash, salt = hash_password(password)
conn = get_db() conn = get_db()
try: try:
c = conn.cursor() c = conn.cursor()
c.execute("INSERT INTO users (email, password_hash, salt, name, role) VALUES (?, ?, ?, ?, ?)", c.execute("INSERT INTO users (email, password_hash, salt, name, role, profession) VALUES (?, ?, ?, ?, ?, ?)",
(email, pwd_hash, salt, name, role)) (email, pwd_hash, salt, name, role, profession))
user_id = c.lastrowid user_id = c.lastrowid
conn.commit() conn.commit()
token = generate_token(user_id, email, role) token = generate_token(user_id, email, role)
return self.send_json({ return self.send_json({
"message": "Erfolgreich registriert!", "message": "Erfolgreich registriert!",
"token": token, "token": token,
"user": {"id": user_id, "email": email, "name": name, "role": role, "xp": 0, "level": 1} "user": {"id": user_id, "email": email, "name": name, "role": role, "profession": profession, "xp": 0, "level": 1}
}, 201) }, 201)
except sqlite3.IntegrityError: except sqlite3.IntegrityError:
return self.send_json({"error": "Ein Benutzer mit dieser E-Mail existiert bereits"}, 409) return self.send_json({"error": "Ein Benutzer mit dieser E-Mail existiert bereits"}, 409)
@ -815,6 +819,8 @@ class PlatformRequestHandler(http.server.SimpleHTTPRequestHandler):
"email": user["email"], "email": user["email"],
"name": user["name"], "name": user["name"],
"role": user["role"], "role": user["role"],
"profession": user["profession"] if "profession" in user.keys() and user["profession"] else "",
"training_year": user["training_year"] if "training_year" in user.keys() and user["training_year"] else 1,
"xp": user["xp"], "xp": user["xp"],
"level": user["level"], "level": user["level"],
"streak_days": user["streak_days"] "streak_days": user["streak_days"]
@ -982,7 +988,9 @@ class PlatformRequestHandler(http.server.SimpleHTTPRequestHandler):
if not user_token: if not user_token:
return self.send_json({"error": "Nicht authentifiziert"}, 401) return self.send_json({"error": "Nicht authentifiziert"}, 401)
profession = str(body.get("profession", "FISI")).strip()[:10] profession = str(body.get("profession", "")).strip()[:10]
if profession not in ("FISI", "FIAE", "FIDP", "FIDV", "ITSE", "ITSM", "KDM", "ALL", ""):
profession = ""
try: try:
training_year = int(body.get("training_year", 1)) training_year = int(body.get("training_year", 1))
if training_year not in (1, 2, 3): if training_year not in (1, 2, 3):