|
| 1 | +document.addEventListener('DOMContentLoaded', () => { |
| 2 | + const contentArea = document.getElementById('content-area'); |
| 3 | + const searchInput = document.getElementById('search-input'); |
| 4 | + const diffFilter = document.getElementById('filter-difficulty'); |
| 5 | + const typeFilter = document.getElementById('filter-type'); |
| 6 | + const tagsContainer = document.getElementById('filter-tags'); |
| 7 | + const resetBtn = document.getElementById('reset-filters'); |
| 8 | + const statPrompts = document.getElementById('stat-prompts'); |
| 9 | + const viewTitle = document.getElementById('current-view-title'); |
| 10 | + const bookmarkCount = document.getElementById('bookmark-count'); |
| 11 | + const showBookmarksBtn = document.getElementById('show-bookmarks-btn'); |
| 12 | + |
| 13 | + let bookmarks = JSON.parse(localStorage.getItem('aelBookmarks')) || []; |
| 14 | + let showingOnlyBookmarks = false; |
| 15 | + let activeTags = new Set(); |
| 16 | + |
| 17 | + // Extract unique types & tags |
| 18 | + const types = [...new Set(window.academyData.map(d => d.type))]; |
| 19 | + types.forEach(type => { |
| 20 | + const opt = document.createElement('option'); |
| 21 | + opt.value = type; |
| 22 | + opt.innerText = type; |
| 23 | + typeFilter.appendChild(opt); |
| 24 | + }); |
| 25 | + |
| 26 | + const allTags = [...new Set(window.academyData.flatMap(d => d.tags))]; |
| 27 | + allTags.forEach(tag => { |
| 28 | + const btn = document.createElement('button'); |
| 29 | + btn.className = 'tag-btn'; |
| 30 | + btn.innerText = tag; |
| 31 | + btn.onclick = () => { |
| 32 | + if (activeTags.has(tag)) { |
| 33 | + activeTags.delete(tag); |
| 34 | + btn.classList.remove('active'); |
| 35 | + } else { |
| 36 | + activeTags.add(tag); |
| 37 | + btn.classList.add('active'); |
| 38 | + } |
| 39 | + renderFilters(); |
| 40 | + }; |
| 41 | + tagsContainer.appendChild(btn); |
| 42 | + }); |
| 43 | + |
| 44 | + function updateBookmarks() { |
| 45 | + bookmarkCount.innerText = bookmarks.length; |
| 46 | + localStorage.setItem('aelBookmarks', JSON.stringify(bookmarks)); |
| 47 | + } |
| 48 | + updateBookmarks(); |
| 49 | + |
| 50 | + function renderFilters() { |
| 51 | + const query = searchInput.value.toLowerCase(); |
| 52 | + const diff = diffFilter.value; |
| 53 | + const type = typeFilter.value; |
| 54 | + |
| 55 | + let filtered = window.academyData.filter(q => { |
| 56 | + const matchQuery = q.question.toLowerCase().includes(query) || q.detailedAnswer.toLowerCase().includes(query); |
| 57 | + const matchDiff = diff === 'All' || q.difficulty === diff; |
| 58 | + const matchType = type === 'All' || q.type === type; |
| 59 | + const matchTags = activeTags.size === 0 || q.tags.some(t => activeTags.has(t)); |
| 60 | + const matchBook = showingOnlyBookmarks ? bookmarks.includes(q.id) : true; |
| 61 | + |
| 62 | + return matchQuery && matchDiff && matchType && matchTags && matchBook; |
| 63 | + }); |
| 64 | + |
| 65 | + statPrompts.innerText = filtered.length; |
| 66 | + renderCards(filtered); |
| 67 | + } |
| 68 | + |
| 69 | + function renderCards(items) { |
| 70 | + contentArea.innerHTML = ''; |
| 71 | + if (items.length === 0) { |
| 72 | + contentArea.innerHTML = '<p style="color:var(--text-dim)">No questions found matching your criteria.</p>'; |
| 73 | + return; |
| 74 | + } |
| 75 | + |
| 76 | + items.forEach((item, index) => { |
| 77 | + const card = document.createElement('div'); |
| 78 | + card.className = 'qa-card'; |
| 79 | + card.id = item.id; |
| 80 | + |
| 81 | + const isBookmarked = bookmarks.includes(item.id); |
| 82 | + |
| 83 | + card.innerHTML = ` |
| 84 | + <div class="qa-header" onclick="toggleCard('${item.id}')"> |
| 85 | + <div class="qa-title-wrapper"> |
| 86 | + <div class="qa-question">Q: ${item.question}</div> |
| 87 | + <div class="qa-meta-tags"> |
| 88 | + <span class="meta-tag meta-diff">${item.difficulty}</span> |
| 89 | + <span class="meta-tag meta-type">${item.type}</span> |
| 90 | + <span class="meta-tag meta-time">⏱ ${item.estimatedReadingTime}</span> |
| 91 | + <span class="meta-tag" style="background:rgba(255,255,255,0.1)">ID: ${item.id}</span> |
| 92 | + </div> |
| 93 | + </div> |
| 94 | + <div class="qa-actions"> |
| 95 | + <button class="action-btn ${isBookmarked ? 'active-bookmark' : ''}" onclick="toggleBookmark(event, '${item.id}')" title="Bookmark">🔖</button> |
| 96 | + <button class="action-btn" onclick="copyURL(event, '${item.id}')" title="Copy URL Anchor">🔗</button> |
| 97 | + <button class="action-btn" onclick="toggleCard('${item.id}')" title="Expand/Collapse">🔽</button> |
| 98 | + </div> |
| 99 | + </div> |
| 100 | + <div class="qa-body"> |
| 101 | + <div class="qa-section s-answer"> |
| 102 | + <h4>✅ Detailed Answer</h4> |
| 103 | + <p>${item.detailedAnswer}</p> |
| 104 | + </div> |
| 105 | + |
| 106 | + <div style="display:flex; gap:24px; margin-bottom:24px; flex-wrap:wrap;"> |
| 107 | + <div class="qa-section s-correct" style="flex:1; min-width:300px; margin-bottom:0;"> |
| 108 | + <h4>🎯 Why Correct</h4> |
| 109 | + <p>${item.whyCorrect}</p> |
| 110 | + </div> |
| 111 | + <div class="qa-section s-incorrect" style="flex:1; min-width:300px; margin-bottom:0;"> |
| 112 | + <h4>❌ Why Incorrect</h4> |
| 113 | + <p>${item.whyIncorrect}</p> |
| 114 | + </div> |
| 115 | + </div> |
| 116 | +
|
| 117 | + <div class="qa-section s-example"> |
| 118 | + <h4>🌍 Real-World Example</h4> |
| 119 | + <div class="code-box">${item.realWorldExample}</div> |
| 120 | + </div> |
| 121 | +
|
| 122 | + <div style="display:flex; gap:24px; margin-bottom:24px; flex-wrap:wrap;"> |
| 123 | + <div class="qa-section s-mistakes" style="flex:1; min-width:300px; margin-bottom:0;"> |
| 124 | + <h4>⚠️ Common Mistakes</h4> |
| 125 | + <p>${item.commonMistakes}</p> |
| 126 | + </div> |
| 127 | + <div class="qa-section s-best" style="flex:1; min-width:300px; margin-bottom:0;"> |
| 128 | + <h4>⭐ Best Practices</h4> |
| 129 | + <p>${item.bestPractices}</p> |
| 130 | + </div> |
| 131 | + </div> |
| 132 | +
|
| 133 | + <div class="qa-section s-rels"> |
| 134 | + <h4>🔗 Relationships & Curriculum</h4> |
| 135 | + <div class="rel-box"> |
| 136 | + <strong>Learning Outcome:</strong> ${item.relationships.learningOutcome}<br> |
| 137 | + <strong>Exercise:</strong> ${item.relationships.exercise}<br> |
| 138 | + <strong>Challenge:</strong> ${item.relationships.challenge}<br> |
| 139 | + <strong>Interview:</strong> ${item.relationships.interview}<br> |
| 140 | + <strong>Source:</strong> ${item.source} |
| 141 | + </div> |
| 142 | + </div> |
| 143 | + |
| 144 | + <div class="card-footer-nav"> |
| 145 | + ${index > 0 ? `<button class="nav-arrow" onclick="scrollToId('${items[index-1].id}')">⬅ Previous Question</button>` : '<span></span>'} |
| 146 | + ${index < items.length - 1 ? `<button class="nav-arrow" onclick="scrollToId('${items[index+1].id}')">Next Question ➡</button>` : '<span></span>'} |
| 147 | + </div> |
| 148 | + </div> |
| 149 | + `; |
| 150 | + contentArea.appendChild(card); |
| 151 | + }); |
| 152 | + |
| 153 | + checkHash(); |
| 154 | + } |
| 155 | + |
| 156 | + // Interactions |
| 157 | + window.toggleCard = (id) => { |
| 158 | + const card = document.getElementById(id); |
| 159 | + if(card) card.classList.toggle('expanded'); |
| 160 | + }; |
| 161 | + |
| 162 | + window.toggleBookmark = (e, id) => { |
| 163 | + e.stopPropagation(); |
| 164 | + const idx = bookmarks.indexOf(id); |
| 165 | + if(idx > -1) bookmarks.splice(idx, 1); |
| 166 | + else bookmarks.push(id); |
| 167 | + |
| 168 | + updateBookmarks(); |
| 169 | + if(showingOnlyBookmarks) renderFilters(); // re-render if in bookmark view |
| 170 | + else { |
| 171 | + e.currentTarget.classList.toggle('active-bookmark'); |
| 172 | + } |
| 173 | + }; |
| 174 | + |
| 175 | + window.copyURL = (e, id) => { |
| 176 | + e.stopPropagation(); |
| 177 | + const url = window.location.href.split('#')[0] + '#' + id; |
| 178 | + navigator.clipboard.writeText(url); |
| 179 | + const originalText = e.currentTarget.innerHTML; |
| 180 | + e.currentTarget.innerHTML = '✅'; |
| 181 | + setTimeout(() => e.currentTarget.innerHTML = originalText, 1500); |
| 182 | + }; |
| 183 | + |
| 184 | + window.scrollToId = (id) => { |
| 185 | + const el = document.getElementById(id); |
| 186 | + if(el) { |
| 187 | + el.scrollIntoView({behavior: 'smooth'}); |
| 188 | + el.classList.add('highlight'); |
| 189 | + el.classList.add('expanded'); |
| 190 | + setTimeout(() => el.classList.remove('highlight'), 2000); |
| 191 | + } |
| 192 | + }; |
| 193 | + |
| 194 | + // Listeners |
| 195 | + searchInput.addEventListener('input', renderFilters); |
| 196 | + diffFilter.addEventListener('change', renderFilters); |
| 197 | + typeFilter.addEventListener('change', renderFilters); |
| 198 | + |
| 199 | + resetBtn.addEventListener('click', () => { |
| 200 | + searchInput.value = ''; |
| 201 | + diffFilter.value = 'All'; |
| 202 | + typeFilter.value = 'All'; |
| 203 | + activeTags.clear(); |
| 204 | + document.querySelectorAll('.tag-btn').forEach(b => b.classList.remove('active')); |
| 205 | + showingOnlyBookmarks = false; |
| 206 | + viewTitle.innerText = "All Questions"; |
| 207 | + renderFilters(); |
| 208 | + }); |
| 209 | + |
| 210 | + showBookmarksBtn.addEventListener('click', () => { |
| 211 | + showingOnlyBookmarks = !showingOnlyBookmarks; |
| 212 | + viewTitle.innerText = showingOnlyBookmarks ? "Bookmarked Questions" : "All Questions"; |
| 213 | + renderFilters(); |
| 214 | + }); |
| 215 | + |
| 216 | + document.getElementById('expand-all').addEventListener('click', () => { |
| 217 | + document.querySelectorAll('.qa-card').forEach(c => c.classList.add('expanded')); |
| 218 | + }); |
| 219 | + document.getElementById('collapse-all').addEventListener('click', () => { |
| 220 | + document.querySelectorAll('.qa-card').forEach(c => c.classList.remove('expanded')); |
| 221 | + }); |
| 222 | + |
| 223 | + // Check URL Hash on load |
| 224 | + function checkHash() { |
| 225 | + if(window.location.hash) { |
| 226 | + const id = window.location.hash.substring(1); |
| 227 | + setTimeout(() => scrollToId(id), 500); |
| 228 | + } |
| 229 | + } |
| 230 | + |
| 231 | + // Init |
| 232 | + renderFilters(); |
| 233 | +}); |
0 commit comments