1// ì ì ë³ì 2let currentSection = 'dashboard'; 3 4// íì´ì§ ë¡ë ì ì´ê¸°í 5document.addEventListener('DOMContentLoaded', function() { 6 // ì´ê¸° ëìë³´ë íì 7 showSection('dashboard'); 8 9 // ì´ê¸° í¸ë ë ë°ì´í° ë¡ë 10 loadTrends(); 11}); 12 13// ì¹ì íì/ì¨ê¹ í¨ì 14function showSection(sectionId) { 15 // 모ë ì¹ì ì¨ê¹ 16 document.querySelectorAll('.section').forEach(section => { 17 section.classList.add('hidden'); 18 }); 19 20 // 모ë ë¤ë¹ê²ì´ì ë§í¬ìì active í´ëì¤ ì ê±° 21 document.querySelectorAll('.nav-link').forEach(link => { 22 link.classList.remove('nav-active'); 23 }); 24 25 // ì íë ì¹ì íì 26 const targetSection = document.getElementById(sectionId); 27 if (targetSection) { 28 targetSection.classList.remove('hidden'); 29 targetSection.classList.add('fade-in'); 30 currentSection = sectionId; 31 } 32 33 // í´ë¹ ë¤ë¹ê²ì´ì ë§í¬ì active í´ëì¤ ì¶ê° 34 const navLinks = document.querySelectorAll('.nav-link'); 35 const sectionNames = ['dashboard', 'materials', 'examples', 'trends']; 36 const index = sectionNames.indexOf(sectionId); 37 if (index !== -1 && navLinks[index]) { 38 navLinks[index].classList.add('nav-active'); 39 } 40} 41 42// íìµ ìë£ ê²ì í¨ì 43async function searchMaterials() { 44 const subject = document.getElementById('material-subject').value; 45 const topic = document.getElementById('material-topic').value; 46 const difficulty = document.getElementById('material-difficulty').value; 47 48 if (!subject || !topic) { 49 alert('과목과 주ì 를 ì ë ¥í´ì£¼ì¸ì.'); 50 return; 51 } 52 53 // ë¡ë© íì 54 showLoading('materials'); 55 56 try { 57 const response = await fetch('/api/materials/search', { 58 method: 'POST', 59 headers: { 60 'Content-Type': 'application/json', 61 }, 62 body: JSON.stringify({ 63 subject: subject, 64 topic: topic, 65 difficulty_level: difficulty 66 }) 67 }); 68 69 const data = await response.json(); 70 71 if (data.success) { 72 displayMaterials(data.materials); 73 } else { 74 showError('materials', data.error || 'ìë£ ê²ì ì¤ ì¤ë¥ê° ë°ìíìµëë¤.'); 75 } 76 } catch (error) { 77 showError('materials', 'ë¤í¸ìí¬ ì¤ë¥ê° ë°ìíìµëë¤.'); 78 } finally { 79 hideLoading('materials'); 80 } 81} 82 83// ìì ìì± í¨ì 84async function generateExamples() { 85 const subject = document.getElementById('example-subject').value; 86 const topic = document.getElementById('example-topic').value; 87 const difficulty = document.getElementById('example-difficulty').value; 88 const type = document.getElementById('example-type').value; 89 const count = document.getElementById('example-count').value; 90 91 if (!subject || !topic) { 92 alert('과목과 주ì 를 ì ë ¥í´ì£¼ì¸ì.'); 93 return; 94 } 95 96 // ë¡ë© íì 97 showLoading('examples'); 98 99 try { 100 const response = await fetch('/api/examples/generate', { 101 method: 'POST', 102 headers: { 103 'Content-Type': 'application/json', 104 }, 105 body: JSON.stringify({ 106 subject: subject, 107 topic: topic, 108 difficulty_level: difficulty, 109 problem_type: type, 110 count: parseInt(count) 111 }) 112 }); 113 114 const data = await response.json(); 115 116 if (data.success) { 117 displayExamples(data.examples); 118 } else { 119 showError('examples', data.error || 'ìì ìì± ì¤ ì¤ë¥ê° ë°ìíìµëë¤.'); 120 } 121 } catch (error) { 122 showError('examples', 'ë¤í¸ìí¬ ì¤ë¥ê° ë°ìíìµëë¤.'); 123 } finally { 124 hideLoading('examples'); 125 } 126} 127 128// êµì¡ í¸ë ë ë¡ë í¨ì 129async function loadTrends() { 130 const category = document.getElementById('trend-category')?.value || ''; 131 132 // ë¡ë© íì 133 showLoading('trends'); 134 135 try { 136 const url = category ? `/api/trends?category=${encodeURIComponent(category)}` : '/api/trends'; 137 const response = await fetch(url); 138 const data = await response.json(); 139 140 if (data.success) { 141 displayTrends(data.trends); 142 } else { 143 showError('trends', data.error || 'í¸ë ë ì ë³´ ë¡ë ì¤ ì¤ë¥ê° ë°ìíìµëë¤.'); 144 } 145 } catch (error) { 146 showError('trends', 'ë¤í¸ìí¬ ì¤ë¥ê° ë°ìíìµëë¤.'); 147 } finally { 148 hideLoading('trends'); 149 } 150} 151 152// ê°ì ê³í ìì± í¨ì 153async function generatePlan() { 154 const title = document.getElementById('plan-title').value; 155 const subject = document.getElementById('plan-subject').value; 156 const audience = document.getElementById('plan-audience').value; 157 const duration = document.getElementById('plan-duration').value; 158 159 if (!title || !subject) { 160 alert('ê°ì ì 목과 과목ì ì ë ¥í´ì£¼ì¸ì.'); 161 return; 162 } 163 164 // ë¡ë© íì 165 showLoading('plans'); 166 167 try { 168 const response = await fetch('/api/plans/generate', { 169 method: 'POST', 170 headers: { 171 'Content-Type': 'application/json', 172 }, 173 body: JSON.stringify({ 174 title: title, 175 subject: subject, 176 target_audience: audience, 177 duration: parseInt(duration) 178 }) 179 }); 180 181 const data = await response.json(); 182 183 if (data.success) { 184 displayPlan(data.plan); 185 } else { 186 showError('plans', data.error || 'ê°ì ê³í ìì± ì¤ ì¤ë¥ê° ë°ìíìµëë¤.'); 187 } 188 } catch (error) { 189 showError('plans', 'ë¤í¸ìí¬ ì¤ë¥ê° ë°ìíìµëë¤.'); 190 } finally { 191 hideLoading('plans'); 192 } 193} 194 195// íìµ ìë£ íì í¨ì 196function displayMaterials(materials) { 197 const resultsDiv = document.getElementById('materials-results'); 198 199 if (materials.length === 0) { 200 resultsDiv.innerHTML = '<p class="text-gray-600 text-center py-8">
200ê²ì ê²°ê³¼ê° ììµëë¤.</p>'; 201 return; 202 } 203 204 let html = '<div class="grid gap-6">'; 205 206 materials.forEach(material => { 207 html += ` 208 <div class="bg-gray-50 rounded-lg p-6 border-l-4 border-blue-500"> 209 <div class="flex justify-between items-start mb-3"> 210 <h3 class="text-lg font-semibold text-gray-800">${material.title}</h3> 211 <span class="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full">${material.difficulty_level}</span> 212 </div> 213 <div class="flex items-center space-x-4 text-sm text-gray-600 mb-3"> 214 <span><i class="fas fa-book mr-1"></i>${material.subject}</span> 215 <span><i class="fas fa-tag mr-1"></i>${material.topic}</span> 216 <span><i class="fas fa-file-alt mr-1"></i>${material.material_type}</span> 217 </div> 218 <p class="text-gray-700 mb-3">${material.content}</p> 219 ${material.source_url ? `<a href="${material.source_url}" target="_blank" class="text-blue-500 hover:text-blue-700 text-sm"><i class="fas fa-external-link-alt mr-1"></i>ì문 보기</a>` : ''} 220 </div> 221 `; 222 }); 223 224 html += '</div>'; 225 resultsDiv.innerHTML = html; 226} 227 228// ìì íì í¨ì 229function displayExamples(examples) { 230 const resultsDiv = document.getElementById('examples-results'); 231 232 if (examples.length === 0) { 233 resultsDiv.innerHTML = '<p class="text-gray-600 text-center py-8">ìì±ë ìì ê° ììµëë¤.</p>'; 234 return; 235 } 236 237 let html = '<div class="space-y-6">'; 238 239 examples.forEach((example, index) => { 240 html += ` 241 <div class="bg-gray-50 rounded-lg p-6 border-l-4 border-green-500"> 242 <div class="flex justify-between items-start mb-3"> 243 <h3 class="text-lg font-semibold text-gray-800">문ì ${index + 1}</h3> 244 <div class="flex space-x-2"> 245 <span class="px-2 py-1 bg-green-100 text-green-800 text-xs rounded-full">${example.difficulty_level}</span> 246 <span class="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full">${example.problem_type}</span> 247 </div> 248 </div> 249 <div class="mb-4"> 250 <h4 class="font-medium text-gray-800 mb-2">문ì :</h4> 251 <p class="text-gray-700 bg-white p-3 rounded border">${example.question}</p> 252 </div> 253 ${example.choices && example.choices.length > 0 ? ` 254 <div class="mb-4"> 255 <h4 class="font-medium text-gray-800 mb-2">보기:</h4> 256 <div class="bg-white p-3 rounded border"> 257 ${example.choices.map((choice, idx) => ` 258 <div class="mb-1"> 259 <span class="font-medium">${String.fromCharCode(65 + idx)}.</span> ${choice} 260 </div> 261 `).join('')} 262 </div> 263 </div> 264 ` : ''} 265 <div class="mb-4"> 266 <h4 class="font-medium text-gray-800 mb-2">ì ëµ:</h4> 267 <p class="text-gray-700 bg-white p-3 rounded border">${example.answer}</p> 268 </div> 269 ${example.explanation ? ` 270 <div class="mb-4"> 271 <h4 class="font-medium text-gray-800 mb-2">í´ì¤:</h4> 272 <p class="text-gray-700 bg-white p-3 rounded border">${example.explanation}</p> 273 </div> 274 ` : ''} 275 ${example.learning_objective ? ` 276 <div class="text-sm text-gray-600"> 277 <i class="fas fa-target mr-1"></i>íìµ ëª©í: ${example.learning_objective} 278 </div> 279 ` : ''} 280 </div> 281 `; 282 }); 283 284 html += '</div>'; 285 resultsDiv.innerHTML = html; 286} 287 288// í¸ë ë íì í¨ì 289function displayTrends(trends) { 290 const resultsDiv = document.getElementById('trends-results'); 291 292 if (trends.length === 0) { 293 resultsDiv.innerHTML = '<p class="text-gray-600 text-center py-8">í¸ë ë ì ë³´ê° ììµëë¤.</p>'; 294 return; 295 } 296 297 let html = '<div class="grid gap-6">'; 298 299 trends.forEach(trend => { 300 const impactColor = trend.impact_level === 'ëì' ? 'red' : trend.impact_level === 'ì¤ê°' ? 'yellow' : 'green'; 301 const relevanceWidth = Math.round(trend.relevance_score * 100); 302 303 html += ` 304 <div class="bg-gray-50 rounded-lg p-6 border-l-4 border-purple-500"> 305 <div class="flex justify-between items-start mb-3"> 306 <h3 class="text-lg font-semibold text-gray-800">${trend.title}</h3>
307 <span class="px-2 py-1 bg-${impactColor}-100 text-${impactColor}-800 text-xs rounded-full">${trend.impact_level}</span> 308 </div> 309 <div class="flex items-center space-x-4 text-sm text-gray-600 mb-3"> 310 <span><i class="fas fa-folder mr-1"></i>${trend.category}</span> 311 <span><i class="fas fa-building mr-1"></i>${trend.source}</span> 312 </div> 313 <p class="text-gray-700 mb-4">${trend.description}</p> 314 <div class="mb-3"> 315 <div class="flex justify-between text-sm text-gray-600 mb-1"> 316 <span>ê´ë ¨ì±</span> 317 <span>${relevanceWidth}%</span> 318 </div> 319 <div class="w-full bg-gray-200 rounded-full h-2"> 320 <div class="bg-purple-500 h-2 rounded-full" style="width: ${relevanceWidth}%"></div> 321 </div> 322 </div> 323 ${trend.source_url ? `<a href="${trend.source_url}" target="_blank" class="text-purple-500 hover:text-purple-700 text-sm"><i class="fas fa-external-link-alt mr-1"></i>ìì¸í 보기</a>` : ''} 324 </div> 325 `; 326 }); 327 328 html += '</div>'; 329 resultsDiv.innerHTML = html; 330} 331 332// ê°ì ê³í íì í¨ì 333function displayPlan(plan) { 334 const resultsDiv = document.getElementById('plans-results'); 335 336 const html = ` 337 <div class="bg-gray-50 rounded-lg p-6 border-l-4 border-orange-500"> 338 <div class="mb-6"> 339 <h3 class="text-xl font-bold text-gray-800 mb-2">${plan.title}</h3> 340 <div class="flex items-center space-x-4 text-sm text-gray-600"> 341 <span><i class="fas fa-book mr-1"></i>${plan.subject}</span> 342 <span><i class="fas fa-users mr-1"></i>${plan.target_audience}</span> 343 <span><i class="fas fa-clock mr-1"></i>${plan.duration}ë¶</span> 344 </div> 345 </div> 346 347 <div class="grid md:grid-cols-2 gap-6"> 348 <div> 349 <h4 class="font-semibold text-gray-800 mb-3"><i class="fas fa-bullseye mr-2"></i>íìµ ëª©í</h4> 350 <div class="bg-white p-4 rounded border"> 351 <pre class="whitespace-pre-wrap text-gray-700">${plan.learning_objectives}</pre> 352 </div> 353 </div> 354 355 <div> 356 <h4 class="font-semibold text-gray-800 mb-3"><i class="fas fa-list-ol mr-2"></i>ìì 구ì±</h4> 357 <div class="bg-white p-4 rounded border"> 358 <pre class="whitespace-pre-wrap text-gray-700">${plan.content_outline}</pre> 359 </div> 360 </div> 361 362 <div> 363 <h4 class="font-semibold text-gray-800 mb-3"><i class="fas fa-tools mr-2"></i>íì ìë£</h4> 364 <div class="bg-white p-4 rounded border"> 365 <pre class="whitespace-pre-wrap text-gray-700">${plan.required_materials}</pre> 366 </div> 367 </div> 368 369 <div> 370 <h4 class="font-semibold text-gray-800 mb-3"><i class="fas fa-chart-bar mr-2"></i>íê° ë°©ë²</h4> 371 <div class="bg-white p-4 rounded border"> 372 <pre class="whitespace-pre-wrap text-gray-700">${plan.assessment_method}</pre> 373 </div> 374 </div> 375 </div> 376 </div> 377 `; 378 379 resultsDiv.innerHTML = html; 380} 381 382// ë¡ë© íì í¨ì 383function showLoading(section) { 384 const loadingDiv = document.getElementById(`${section}-loading`); 385 const resultsDiv = document.getElementById(`${section}-results`); 386 387 if (loadingDiv) loadingDiv.classList.add('active'); 388 if (resultsDiv) resultsDiv.innerHTML = ''; 389} 390 391// ë¡ë© ì¨ê¹ í¨ì 392function hideLoading(section) { 393 const loadingDiv = document.getElementById(`${section}-loading`); 394 if (loadingDiv) loadingDiv.classList.remove('active'); 395} 396 397// ì¤ë¥ íì í¨ì 398function showError(section, message) { 399 const resultsDiv = document.getElementById(`${section}-results`); 400 if (resultsDiv) { 401 resultsDiv.innerHTML = ` 402 <div class="bg-red-50 border border-red-200 rounded-lg p-4 text-center"> 403 <i class="fas fa-exclamation-triangle text-red-500 text-2xl mb-2"></i> 404 <p class="text-red-700">${message}</p> 405 </div> 406 `; 407 } 408} 409
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.