// finder.js – Logik für den Step-basierten Finder im Tab 2 mit YAML-Ausgabe let steps = []; // Alle geladenen Schritte aus structure.yml let answers = []; // User-Antworten let selectedBundesland = ''; // Aktuelles Bundesland let currentPath = []; // Aktueller Pfad durch den Entscheidungsbaum // Lädt Finderstruktur aus YML und speichert sie global async function loadFinderStructure() { try { const response = await fetch('Steuerungsdateien/finder.yml'); const text = await response.text(); const data = jsyaml.load(text); steps = data.steps; console.log('[DEBUG] Finderstruktur geladen:', steps); resetFinder(); renderBundeslandStep(); // Zeige Empfehlungsbereich beim Finder-Tab an showFinderRecommendation(); } catch (err) { console.error("Fehler beim Laden der structure.yml:", err); } } // Setzt den Finder zurück function resetFinder() { answers = []; selectedBundesland = ''; currentPath = ['step1']; document.getElementById('result').style.display = 'none'; document.getElementById('dynamic-steps').innerHTML = ''; } // Baut das initiale Bundesland-Auswahlfeld mit Arrow und Styling function renderBundeslandStep() { const step1 = steps.find(s => s.id === 'step1'); if (!step1) return; const container = document.getElementById('dynamic-steps'); container.innerHTML = ''; // Reset const div = document.createElement('div'); div.className = 'step active'; div.id = 'step1'; const label = document.createElement('label'); label.textContent = step1.frage; div.appendChild(label); // Erstelle den wrapper mit Klasse für bessere Auswählbarkeit const wrapper = document.createElement('div'); wrapper.className = 'select-wrapper'; // NEU wrapper.style.position = 'relative'; wrapper.style.display = 'inline-block'; // NEU const select = document.createElement('select'); select.className = 'select purple auto-width'; select.id = 'bundesland-steps'; select.name = 'bundesland'; const defaultOpt = document.createElement('option'); defaultOpt.textContent = 'Bitte wählen'; defaultOpt.value = ''; select.appendChild(defaultOpt); step1.options.forEach(opt => { const option = document.createElement('option'); option.value = opt.value; option.textContent = opt.label; select.appendChild(option); }); // Vorauswahl des Bundeslandes, falls vorhanden if (selectedBundesland) { select.value = selectedBundesland; } select.addEventListener('change', (e) => { const val = e.target.value; if (val !== '') { // Wenn Bundesland geändert wird, Pfad neu berechnen und alle nachfolgenden Steps entfernen recalculatePathFrom('step1', val); onBundeslandSelected(val); } }); const arrow = document.createElement('div'); arrow.className = 'select-arrow'; wrapper.appendChild(select); wrapper.appendChild(arrow); div.appendChild(wrapper); const info = document.createElement('div'); info.className = 'info'; info.textContent = step1.info; div.appendChild(info); container.appendChild(div); setTimeout(() => { resizeSelect(select); }, 0); } // Wird aufgerufen, wenn das Bundesland gewählt wurde (step1) function onBundeslandSelected(value) { selectedBundesland = value; answers[0] = value; document.getElementById('result').style.display = 'none'; // Alle nachfolgenden Steps entfernen removeStepsAfter('step1'); // Step 2 rendern const step2 = steps.find(s => s.id === 'step2'); if (step2) renderStep(step2); } // Rendert einen Step (Frage + Optionen) function renderStep(step) { const container = document.getElementById('dynamic-steps'); // Prüfen, ob dieser Step bereits existiert let stepElement = document.getElementById(step.id); if (stepElement) { // Falls der Step bereits existiert, nur Info anzeigen const info = stepElement.querySelector('.info'); if (info) info.style.display = 'block'; // Falls der Step bereits existiert und eine Antwort hat, markiere den Button const stepIndex = steps.findIndex(s => s.id === step.id); const selectedValue = answers[stepIndex]; if (selectedValue) { const buttons = stepElement.querySelectorAll('button'); buttons.forEach(btn => { if (btn.dataset.value === selectedValue) { btn.classList.add('selected'); } else { btn.classList.remove('selected'); } }); } stepElement.scrollIntoView({ behavior: 'smooth' }); return; } // Neuen Step erstellen const div = document.createElement('div'); div.className = 'step active'; div.id = step.id; const frage = document.createElement('label'); frage.textContent = step.frage; div.appendChild(frage); const buttonGroup = document.createElement('div'); buttonGroup.className = 'button-group'; step.options.forEach(opt => { const btn = document.createElement('button'); btn.type = 'button'; btn.className = 'button'; btn.textContent = opt.label; btn.dataset.value = opt.value; // Wenn bereits eine Antwort ausgewählt ist, markieren const stepIndex = steps.findIndex(s => s.id === step.id); if (answers[stepIndex] === opt.value) { btn.classList.add('selected'); } btn.onclick = () => { // Vorher ausgewählte Buttons deselektieren buttonGroup.querySelectorAll('button').forEach(b => b.classList.remove('selected')); btn.classList.add('selected'); // Antwort speichern const stepIndex = steps.findIndex(s => s.id === step.id); answers[stepIndex] = opt.value; // Info ausblenden div.querySelector('.info').style.display = 'none'; // Pfad neu berechnen und alle nachfolgenden Steps entfernen recalculatePathFrom(step.id, opt.value); removeStepsAfter(step.id); // Abhängig von der Option weitergehen oder Ergebnis anzeigen if (opt.result) { // Am Ende des Pfades - Ergebnis anzeigen const code = buildResultCode(); loadResult(code); } else if (opt.next) { // Nächsten Step anzeigen const nextStep = steps.find(s => s.id === opt.next); if (nextStep) renderStep(nextStep); } }; buttonGroup.appendChild(btn); }); div.appendChild(buttonGroup); const info = document.createElement('div'); info.className = 'info'; info.textContent = step.info; div.appendChild(info); container.appendChild(div); div.scrollIntoView({ behavior: 'smooth' }); // Step zum aktiven Pfad hinzufügen if (!currentPath.includes(step.id)) { currentPath.push(step.id); } } // Berechnet den Pfad ausgehend von einem bestimmten Step neu function recalculatePathFrom(stepId, selectedValue) { const stepIndex = steps.findIndex(s => s.id === stepId); // Aktualisiere den aktuellen Pfad (entferne alle nach dem geänderten Step) currentPath = currentPath.slice(0, currentPath.indexOf(stepId) + 1); // Aktualisiere die Antworten (behalte nur Antworten bis zum aktuellen Step) answers = answers.slice(0, stepIndex + 1); // Setze die ausgewählte Antwort answers[stepIndex] = selectedValue; console.log('[DEBUG] Neuer Pfad berechnet ab', stepId, 'Pfad:', currentPath, 'Antworten:', answers); // Ergebnis ausblenden, da sich der Pfad geändert hat document.getElementById('result').style.display = 'none'; } // Entfernt alle Steps nach dem angegebenen Step aus dem DOM function removeStepsAfter(stepId) { const container = document.getElementById('dynamic-steps'); const stepElements = container.querySelectorAll('.step'); let removeFlag = false; stepElements.forEach(el => { if (removeFlag) { container.removeChild(el); } if (el.id === stepId) { removeFlag = true; } }); // Ergebnis ausblenden document.getElementById('result').style.display = 'none'; } // Generiert den Code für das Ergebnis aus den Antworten function buildResultCode() { // Bundesland-Code mapping - korrigiert für die tatsächlichen Verzeichnisnamen const bundeslandMapping = { 'BW': '1_BW', // Baden-Württemberg 'BY': '2_BY', // Bayern 'BE': '3_BE', // Berlin 'BB': '4_BB', // Brandenburg 'HB': '5_HB', // Bremen 'HH': '6_HH', // Hamburg 'HE': '7_HE', // Hessen 'MV': '8_MV', // Mecklenburg-Vorpommern 'NI': '9_NI', // Niedersachsen 'NW': '10_NW', // Nordrhein-Westfalen 'RP': '11_RP', // Rheinland-Pfalz 'SL': '12_SL', // Saarland 'SN': '13_SN', // Sachsen 'ST': '14_ST', // Sachsen-Anhalt 'SH': '15_SH', // Schleswig-Holstein 'TH': '16_TH' // Thüringen }; console.log('[DEBUG] Alle Antworten vor Filterung:', answers); // Stelle sicher, dass answers ein Array ist und alle Elemente definiert sind const cleanAnswers = answers.filter(answer => answer !== undefined && answer !== null && answer !== ''); console.log('[DEBUG] Bereinigte Antworten:', cleanAnswers); if (cleanAnswers.length === 0) { console.error('[ERROR] Keine gültigen Antworten gefunden'); return 'error/no-answers'; } const bundesland = bundeslandMapping[cleanAnswers[0]] || cleanAnswers[0]; // Spezielle Behandlung für HS (Hochschulstudium) let restCode; if (cleanAnswers[1] === 'HS') { // Bei HS: HS-BA-1F oder HS-MA-2F restCode = cleanAnswers.slice(1).join('-'); } else { // Bei LA: LA-BA-1F oder LA-MA-2F restCode = cleanAnswers.slice(1).join('-'); } const code = `${bundesland}/${restCode}`; console.log('[DEBUG] Finaler Code:', code); return code; } // Ergebnis anzeigen am Ende des Flows async function loadResult(code) { // 1. Ermittle den Pfad zur .md-Datei-Liste (z.B. Bundeslaender/1_BW/BA-2F.md) // 2. Lade die Datei mit den Pfaden zu den eigentlichen Texten // 3. Lade alle .md-Dateien in der Reihenfolge, parse sie und zeige sie als
an let codeFile = `Bundeslaender/${code}.md`; const resultContainer = document.getElementById('result'); const mainSection = resultContainer.querySelector('.main-section'); mainSection.innerHTML = ''; // Hilfsfunktion: Markdown-Abschnitt parsen und als Article rendern function renderMarkdownArticle(mdText) { // Splitte in Abschnitte nach ## const sections = mdText.split(/\n(?=## )/); return sections.map(section => { let lines = section.trim().split('\n'); if (!lines[0].startsWith('##')) return ''; let headline = lines[0].replace(/^## /, ''); let marginal = ''; let textStart = 1; // Prüfe zweite Zeile auf Link if (lines[1] && lines[1].startsWith('http')) { const linkLine = lines[1]; // Prüfe ob es ein Markdown-Link mit Text ist: [Text](URL) const markdownLinkMatch = linkLine.match(/^\[([^\]]+)\]\(([^)]+)\)$/); if (markdownLinkMatch) { // Markdown-Link gefunden: [Text](URL) const linkText = markdownLinkMatch[1]; const linkUrl = markdownLinkMatch[2]; marginal = `
${linkText}
`; } else { // Nur URL ohne Text - Fallback verwenden marginal = `
Externer Link
`; } textStart = 2; } let text = lines.slice(textStart).join('\n'); return `

${headline}

${marked.parse(text)}
`; }).join(''); } // Lade die Datei mit den Pfaden zu den eigentlichen Texten try { const pfadRes = await fetch(codeFile); if (!pfadRes.ok) throw new Error('Datei nicht gefunden: ' + codeFile); const pfadText = await pfadRes.text(); const mdFiles = pfadText.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#')); let allArticles = ''; for (const mdFile of mdFiles) { try { // Entferne führenden Slash falls vorhanden const cleanPath = mdFile.startsWith('/') ? mdFile.substring(1) : mdFile; // Füge Bundeslaender-repo/ hinzu, falls nicht bereits vorhanden const fullPath = cleanPath.startsWith('Bundeslaender-repo/') ? cleanPath : `Bundeslaender-repo/${cleanPath}`; const mdRes = await fetch(fullPath); if (!mdRes.ok) throw new Error('Datei nicht gefunden: ' + fullPath); const mdText = await mdRes.text(); allArticles += renderMarkdownArticle(mdText); } catch (err) { allArticles += `

Datei nicht gefunden

${mdFile}
`; } } mainSection.innerHTML = allArticles; resultContainer.style.display = 'block'; resultContainer.scrollIntoView({ behavior: 'smooth' }); } catch (err) { mainSection.innerHTML = `

Fehler

${err.message}
`; resultContainer.style.display = 'block'; } } // Lädt und zeigt die zusätzlichen Abschnitte an function loadAdditionalSections(sections, resultContainer) { if (!resultContainer) { console.error('[ERROR] Kein gültiger Container für zusätzliche Abschnitte'); return; } // Trennlinie hinzufügen vor den zusätzlichen Abschnitten const divider = document.createElement('hr'); divider.className = 'section-divider'; resultContainer.appendChild(divider); // Jeden zusätzlichen Abschnitt laden sections.forEach((sectionCode, index) => { const path = `data/finder/texte/${sectionCode}.yml`; console.log(`[DEBUG] Lade zusätzlichen Abschnitt ${index + 1} von:`, path); fetch(path) .then(res => res.ok ? res.text() : null) .then(txt => txt ? jsyaml.load(txt) : null) .then(data => { if (!data) { console.warn(`[WARN] Zusätzlicher Abschnitt ${sectionCode} konnte nicht geladen werden.`); return; } // Zusätzlichen Abschnitt hinzufügen appendAdditionalSection(data, index + 1, sectionCode, resultContainer); }) .catch(err => { console.error(`[ERROR] Fehler beim Laden des zusätzlichen Abschnitts ${sectionCode}:`, err); }); }); } // Fügt einen zusätzlichen Abschnitt zum DOM hinzu function appendAdditionalSection(data, index, sectionCode, resultContainer) { if (!resultContainer) { console.error('[ERROR] Kein gültiger Container zum Anhängen des zusätzlichen Abschnitts'); return; } // Neuen Abschnitt erstellen const section = document.createElement('div'); section.className = 'additional-section'; section.id = `section-${sectionCode}`; section.innerHTML = `

${data.Headline || `Zusätzliche Information ${index}`}

${data.Text || '...'}
${data.Tags || ''}
${data.Marginal || ''}
`; resultContainer.appendChild(section); } // Fügt einen Reset-Button hinzu (optional) function addResetButton() { const container = document.getElementById('dynamic-steps').parentElement; let resetBtn = document.getElementById('reset-finder'); if (!resetBtn) { resetBtn = document.createElement('button'); resetBtn.id = 'reset-finder'; resetBtn.className = 'button secondary'; resetBtn.textContent = 'Neu starten'; resetBtn.onclick = () => { resetFinder(); renderBundeslandStep(); if (typeof showTab === 'function') showTab('tab2'); }; container.appendChild(resetBtn); } } // Zeigt den Empfehlungsbereich für den Finder-Tab an function showFinderRecommendation() { const recommendation = document.querySelector('.recommendation'); if (recommendation) { recommendation.style.display = 'block'; } } // Initialisierung beim Seitenladen window.addEventListener('DOMContentLoaded', () => { loadFinderStructure(); addResetButton(); // Optional observeSelectChanges(); });