@@ -16,15 +16,17 @@
const drawers = [ 'junior' , 'senior' , 'principal' ] . map ( level => {
const cases = catalog . map ( ( hunt , i ) => ( { hunt , i } ) ) . filter ( entry => entry . hunt . difficulty === level ) ;
if ( ! cases . length ) return '' ;
return ` <div class="bh-cabinet-level"> ${ difficultyNames [ level ] } </div> ${ cases . map ( ( { hunt , i } ) => ` < button class = "bh-drawer ${i === selectedCase ? 'active' : ''}" data - select = "${i}" $ { i === selectedCase ? 'aria-pressed="true"' : 'aria-pressed="false"' } > < span class = "bh-drawer-handle" > < / s p a n > < s p a n c l a s s = " b h - c a s e - n o " > $ { e s c ( h u n t . e x a m S c o p e ) } · $ { e s c ( h u n t . d i f f i c u l t y ) . t o U p p e r C a s e ( ) } < / s p a n > < s t r o n g > $ { e s c ( h u n t . t i t l e ) } < / s t r o n g > < s m a l l > $ { h u n t . b u g C o u n t } B u g $ { h u n t . b u g C o u n t = = = 1 ? ' ' : ' s ' } · $ { h u n t . f i l e C o u n t } $ { h u n t . f i l e C o u n t = = = 1 ? ' D a t e i ' : ' D a t e i e n ' } < / s m a l l > < / b u t t o n > ` ) . j o i n ( ' ' ) } ` ;
return ` <div class="bh-cabinet-level"> ${ difficultyNames [ level ] } </div> ${ cases . map ( ( { hunt , i } ) => ` < button class = "bh-drawer ${hunt.solved ? 'solved' : ''} ${i === selectedCase ? 'active' : ''}" data - select = "${i}" $ { i === selectedCase ? 'aria-pressed="true"' : 'aria-pressed="false"' } > < span class = "bh-drawer-handle" > < / s p a n > $ { h u n t . s o l v e d ? ' < s p a n c l a s s = " b h - s o l v e d - s t a m p " > G E L Ö S T < / s p a n > ' : ' ' } < s p a n c l a s s = " b h - c a s e - n o " > $ { e s c ( h u n t . e x a m S c o p e ) } · $ { e s c ( h u n t . d i f f i c u l t y ) . t o U p p e r C a s e ( ) } < / s p a n > < s t r o n g > $ { e s c ( h u n t . t i t l e ) } < / s t r o n g > < s m a l l > $ { h u n t . b u g C o u n t } B u g $ { h u n t . b u g C o u n t = = = 1 ? ' ' : ' s ' } · $ { h u n t . f i l e C o u n t } $ { h u n t . f i l e C o u n t = = = 1 ? ' D a t e i ' : ' D a t e i e n ' } < / s m a l l > < / b u t t o n > ` ) . j o i n ( ' ' ) } ` ;
} ) . join ( '' ) ;
view . innerHTML = ` <section class="bh-hero"><span class="bh-stamp">VERTRAULICH</span><span class="bh-kicker">DEBUG-FALLAKTEN</span><h1>Welche Spur verfolgst du?</h1><p>Öffne eine Akte, lies die Beweise und sichere deinen Befund Schritt für Schritt. Jede Akte bleibt so lange offen, wie du sie brauchst.</p></section><section class="bh-archive"><div class="bh-archive-head"><div><span class="bh-kicker">AKTENSCHRANK · ${ catalog . length } AKTEN</span><h2>Fallarchiv</h2></div><p>Nach Schwierigkeit sortiert: Grundlagen, Praxis, Komplexfälle.</p></div><div class="bh-cabinet" aria-label="Fallakten nach Schwierigkeit"><div class="bh-cabinet-label">BUGHUNTER · ARCHIV</div> ${ drawers } </div><article class="bh-case-detail"><span class="bh-stamp">AUSGEWÄHLTE AKTE</span><span class="bh-kicker"> ${ esc ( selectedHunt . examScope ) } · ${ esc ( selectedHunt . difficulty ) . toUpperCase ( ) } · ${ esc ( selectedHunt . language ) } </span><h2> ${ esc ( selectedHunt . title ) } </h2><p> ${ esc ( selectedHunt . description ) } </p><div class="bh-tags"><span> ${ selectedHunt . fileCount } ${ selectedHunt . fileCount === 1 ? 'Datei' : 'Dateien' } </span><span> ${ selectedHunt . bugCount } Bug ${ selectedHunt . bugCount === 1 ? '' : 's' } </span></div><button class="bh-btn primary" data-start=" ${ esc ( selectedHunt . slug ) } ">AKTE ÖFFNEN</button></article></section> ` ;
view . innerHTML = ` <section class="bh-hero"><span class="bh-stamp">VERTRAULICH</span><span class="bh-kicker">DEBUG-FALLAKTEN</span><h1>Welche Spur verfolgst du?</h1><p>Öffne eine Akte, lies die Beweise und sichere deinen Befund Schritt für Schritt. Jede Akte bleibt so lange offen, wie du sie brauchst.</p></section><section class="bh-archive"><div class="bh-archive-head"><div><span class="bh-kicker">AKTENSCHRANK · ${ catalog . length } AKTEN</span><h2>Fallarchiv</h2></div><p>Nach Schwierigkeit sortiert: Grundlagen, Praxis, Komplexfälle.</p></div><div class="bh-cabinet" aria-label="Fallakten nach Schwierigkeit"><div class="bh-cabinet-label">BUGHUNTER · ARCHIV</div> ${ drawers } </div><article class="bh-case-detail"> ${ selectedHunt . solved ? '<span class="bh-solved-stamp bh-solved-stamp-detail">GELÖST</span>' : '' } <span class="bh-stamp">AUSGEWÄHLTE AKTE</span><span class="bh-kicker"> ${ esc ( selectedHunt . examScope ) } · ${ esc ( selectedHunt . difficulty ) . toUpperCase ( ) } · ${ esc ( selectedHunt . language ) } </span><h2> ${ esc ( selectedHunt . title ) } </h2><p> ${ esc ( selectedHunt . description ) } </p><div class="bh-tags"><span> ${ selectedHunt . fileCount } ${ selectedHunt . fileCount === 1 ? 'Datei' : 'Dateien' } </span><span> ${ selectedHunt . bugCount } Bug ${ selectedHunt . bugCount === 1 ? '' : 's' } </span></div><div class="bh-actions">< button class="bh-btn primary" data-start=" ${ esc ( selectedHunt . slug ) } "> ${ selectedHunt . solved ? 'ERNEUT SPIELEN' : ' AKTE ÖFFNEN' } </button> ${ selectedHunt . solved ? ` <button class="bh-btn" data-reset=" ${ esc ( selectedHunt . slug ) } ">FALL VARIIEREN</button> ` : '' } </div> </article></section>` ;
view . querySelectorAll ( '[data-select]' ) . forEach ( drawer => drawer . addEventListener ( 'click' , ( ) => { selectedCase = Number ( drawer . dataset . select ) ; renderCatalog ( ) ; } ) ) ;
view . querySelector ( '[data-start]' ) ? . addEventListener ( 'click' , event => begin ( event . currentTarget . dataset . start ) ) ;
view . querySelector ( '[data-reset]' ) ? . addEventListener ( 'click' , event => resetCase ( event . currentTarget . dataset . reset ) ) ;
}
function begin ( slug ) { api ( '/session/' + encodeURIComponent ( slug ) ) . then ( data => { if ( ! data . found ) throw new Error ( 'Fallakte nicht verfügbar.' ) ; session = data ; index = 0 ; handoff = null ; answers = session . bugs . map ( ( ) => ( { locate : null , classify : null , fix : null } ) ) ; started = Date . now ( ) ; renderBriefing ( ) ; } ) . catch ( error ) ; }
function resetCase ( slug ) { post ( '/reset/' + encodeURIComponent ( slug ) , { } ) . then ( ( ) => begin ( slug ) ) . catch ( error ) ; }
function renderBriefing ( ) {
view . innerHTML = ` <section class="bh-briefing"><span class="bh-stamp">NEUE AKTE</span><span class="bh-kicker"> ${ esc ( session . examScope ) } · ${ esc ( session . difficulty ) . toUpperCase ( ) } · ${ esc ( session . language ) } </span><h1> ${ esc ( session . title ) } </h1><p> ${ esc ( session . briefing ) } </p><pre> ${ esc ( session . consoleOutput ) } </pre><div class="bh-rules"><div><b>1</b> Ort markieren</div><div><b>2</b> Fehler einordnen</div><div><b>3</b> sauberen Fix wählen</div></div><button class="bh-btn primary" id="bhStart">ERMITTLUNG STARTEN</button></section> ` ;
view . innerHTML = ` <section class="bh-briefing"><span class="bh-stamp">NEUE AKTE</span><span class="bh-kicker"> ${ esc ( session . examScope ) } · ${ esc ( session . difficulty ) . toUpperCase ( ) } · ${ esc ( session . language ) } ${ session . variant ? ` · VARIANTE ${ session . variant + 1 } ` : '' } </span><h1> ${ esc ( session . title ) } </h1><p> ${ esc ( session . briefing ) } </p><pre> ${ esc ( session . consoleOutput ) } </pre><div class="bh-rules"><div><b>1</b> Ort markieren</div><div><b>2</b> Fehler einordnen</div><div><b>3</b> sauberen Fix wählen</div></div><button class="bh-btn primary" id="bhStart">ERMITTLUNG STARTEN</button></section> ` ;
document . getElementById ( 'bhStart' ) . addEventListener ( 'click' , ( ) => { selectedFile = session . files [ 0 ] ? . filename || '' ; selectedLine = 0 ; selected = '' ; options = [ ] ; renderHunt ( ) ; } ) ;
}
function activePhase ( ) { const s = state ( ) ; return ! s . locate ? . done ? 'locate' : ! s . classify ? . done ? 'classify' : 'fix' ; }
@@ -83,7 +85,7 @@
function finish ( ) { post ( '/finish' , { slug : session . slug , durationSeconds : seconds ( ) , items : answers . map ( ( answer , i ) => ( { bugId : session . bugs [ i ] . bugId , locate : strip ( answer . locate ) , classify : strip ( answer . classify ) , fix : strip ( answer . fix ) } ) ) } ) . then ( renderResult ) . catch ( error ) ; }
function strip ( value ) { return value ? { submission : value . submission , attempts : value . attempts , revealed : ! ! value . revealed } : null ; }
function comboText ( ) { let combo = 0 ; for ( let i = 0 ; i < index ; i ++ ) { const a = answers [ i ] ; const clean = [ 'locate' , 'classify' , 'fix' ] . every ( key => a [ key ] && a [ key ] . attempts === 1 && ! a [ key ] . revealed ) ; combo = clean ? combo + 1 : 0 ; } return ` × ${ combo } ` ; }
function renderResult ( result ) { view . innerHTML = ` <section class="bh-result"><span class="bh-stamp">ERMITTLUNG ABGESCHLOSSEN</span><h1>Fallakten geschlossen.</h1><div class="bh-score"> ${ result . score } </div><p class="bh-stars"> ${ '★' . repeat ( result . stars ) } ${ '☆' . repeat ( 3 - result . stars ) } </p><div class="bh-stats"><div><b> ${ result . baseScore } </b>Basis</div><div><b>+ ${ result . comboBonus } </b>Combo</div><div><b> ${ result . bugsSolved } / ${ result . totalBugs } </b>gelöst</div><div><b>× ${ result . maxCombo } </b>Serie</div><div><b> ${ result . highscore } </b>Highscore</div><div><b> ${ result . plays } </b>Akten</div></div><button class="bh-btn primary" id="bhAgain">WEITERE FÄLLE</button></section> ` ; document . getElementById ( 'bhAgain' ) . addEventListener ( 'click' , loadCatalog ) ; }
function renderResult ( result ) { view . innerHTML = ` <section class="bh-result"> ${ result . cleanSolve ? '<span class="bh-solved-stamp bh-solved-stamp-result">GELÖST</span>' : '' } <span class="bh-stamp">ERMITTLUNG ABGESCHLOSSEN</span><h1> ${ result . cleanSolve ? 'Fall fehlerfrei geschlossen.' : ' Fallakten geschlossen.' } </h1><div class="bh-score"> ${ result . score } </div><p class="bh-stars"> ${ '★' . repeat ( result . stars ) } ${ '☆' . repeat ( 3 - result . stars ) } </p><div class="bh-stats"><div><b> ${ result . baseScore } </b>Basis</div><div><b>+ ${ result . comboBonus } </b>Combo</div><div><b> ${ result . bugsSolved } / ${ result . totalBugs } </b>gelöst</div><div><b>× ${ result . maxCombo } </b>Serie</div><div><b> ${ result . highscore } </b>Highscore</div><div><b> ${ result . plays } </b>Akten</div></div><button class="bh-btn primary" id="bhAgain">WEITERE FÄLLE</button></section> ` ; document . getElementById ( 'bhAgain' ) . addEventListener ( 'click' , loadCatalog ) ; }
function feedback ( message , type ) { const target = document . getElementById ( 'bhFeedback' ) ; if ( target ) { target . textContent = message ; target . className = 'bh-feedback ' + type ; } }
function error ( err ) { view . innerHTML = ` <section class="bh-result"><h1>AKTE NICHT LESBAR</h1><p> ${ esc ( err . message || err ) } </p><button class="bh-btn" id="bhReload">ERNEUT VERSUCHEN</button></section> ` ; document . getElementById ( 'bhReload' ) . addEventListener ( 'click' , loadCatalog ) ; }
loadCatalog ( ) ;