PageSourceSearch

https://8xhpiqce6vpx.manus.space/app.js

js 8xhpiqce6vpx.manus.space collected 2026-10-03 04:11:34 UTC 15,778 bytes, 409 lines download raw bytes

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.