RewardApp / js /ui /exercise_ui.js
Mhdeusi's picture
Create exercise_ui.js
a6e97b0 verified
class ExerciseUI {
constructor() {
this.currentExercise = null;
this.answerSubmitted = false;
}
async render(exerciseData) {
this.currentExercise = exerciseData;
this.answerSubmitted = false;
const exerciseContent = document.getElementById('exerciseContent');
const exerciseFeedback = document.getElementById('exerciseFeedback');
if (!exerciseContent) return;
// پاک کردن فیدبک قبلی
if (exerciseFeedback) {
exerciseFeedback.innerHTML = '';
}
let html = `
<div class="exercise-header">
<h4>${this.escapeHtml(exerciseData.exercise.question)}</h4>
${exerciseData.exercise.description ?
`<p class="exercise-description">${this.escapeHtml(exerciseData.exercise.description)}</p>` : ''}
</div>
<div class="exercise-body">
<div class="answer-section">
<label for="exerciseAnswer" class="answer-label">پاسخ خود را بنویسید:</label>
<textarea
id="exerciseAnswer"
rows="6"
placeholder="پاسخ خود را اینجا وارد کنید..."
${this.answerSubmitted ? 'disabled' : ''}
></textarea>
${exerciseData.exercise.hint ?
`<div class="exercise-hint">
<strong>💡 نکته:</strong> ${this.escapeHtml(exerciseData.exercise.hint)}
</div>` : ''}
<div class="exercise-actions">
<button
id="submitExercise"
class="btn btn-primary"
onclick="exerciseUI.submitAnswer()"
${this.answerSubmitted ? 'disabled' : ''}
>
ارسال پاسخ
</button>
<button
class="btn btn-secondary"
onclick="exerciseUI.showSolution()"
>
مشاهده راه‌حل
</button>
</div>
</div>
<div class="exercise-info">
<div class="info-card">
<h5>📋 انتظارات ما</h5>
<ul class="expected-keywords">
${exerciseData.exercise.expected_keywords.map(keyword =>
`<li><code>${this.escapeHtml(keyword)}</code></li>`
).join('')}
</ul>
</div>
<div class="info-card">
<h5>🎯 معیارهای ارزیابی</h5>
<p>پاسخ شما بر اساس تطابق با کلمات کلیدی بالا ارزیابی می‌شود.</p>
<p>حداقل ${Math.round((exerciseData.exercise.match_threshold || 0.6) * 100)}% تطابق مورد نیاز است.</p>
</div>
</div>
</div>
`;
exerciseContent.innerHTML = html;
// تنظیم event listener برای textarea
const textarea = document.getElementById('exerciseAnswer');
if (textarea && !this.answerSubmitted) {
textarea.addEventListener('input', this.debounce(this.autoSaveAnswer.bind(this), 1000));
this.loadSavedAnswer();
}
}
async submitAnswer() {
const answerTextarea = document.getElementById('exerciseAnswer');
const submitButton = document.getElementById('submitExercise');
if (!answerTextarea || !submitButton) return;
const answer = answerTextarea.value.trim();
if (!answer) {
Utils.showNotification('لطفاً پاسخ خود را وارد کنید', 'error');
return;
}
// غیرفعال کردن دکمه و نمایش حالت loading
submitButton.disabled = true;
submitButton.innerHTML = '⏳ در حال بررسی...';
try {
const result = await learningLogic.checkExerciseAnswer(
this.currentExercise.day,
answer
);
this.answerSubmitted = true;
this.showResult(result);
this.clearSavedAnswer();
// ارسال event برای به‌روزرسانی سایر بخش‌ها
document.dispatchEvent(new CustomEvent('exerciseSubmitted', {
detail: {
exercise: this.currentExercise,
result: result
}
}));
} catch (error) {
console.error('Error submitting exercise:', error);
Utils.showNotification('خطا در ارسال پاسخ', 'error');
submitButton.disabled = false;
submitButton.innerHTML = 'ارسال پاسخ';
}
}
showResult(result) {
const exerciseFeedback = document.getElementById('exerciseFeedback');
if (!exerciseFeedback) return;
const answerTextarea = document.getElementById('exerciseAnswer');
const submitButton = document.getElementById('submitExercise');
if (answerTextarea) {
answerTextarea.disabled = true;
}
if (submitButton) {
submitButton.disabled = true;
submitButton.innerHTML = '✅ پاسخ ارسال شده';
}
let html = `
<div class="exercise-result ${result.isCorrect ? 'success' : 'warning'}">
<div class="result-header">
<h4>${result.isCorrect ? '✅ پاسخ صحیح' : '❌ نیاز به بهبود'}</h4>
<div class="reward-badge">${result.reward} امتیاز</div>
</div>
<div class="result-body">
<p><strong>پیام:</strong> ${result.feedback.message}</p>
<div class="keywords-analysis">
<h5>📊 تحلیل کلمات کلیدی:</h5>
<div class="keywords-grid">
<div class="matched-keywords">
<h6>✅ کلمات یافت شده:</h6>
${result.matchedKeywords.length > 0 ?
`<ul>${result.matchedKeywords.map(kw => `<li><code>${this.escapeHtml(kw)}</code></li>`).join('')}</ul>` :
'<p>هیچ کلمه‌ای یافت نشد</p>'
}
</div>
<div class="missing-keywords">
<h6>❌ کلمات مفقوده:</h6>
${result.expectedKeywords.filter(kw => !result.matchedKeywords.includes(kw)).length > 0 ?
`<ul>${result.expectedKeywords.filter(kw => !result.matchedKeywords.includes(kw))
.map(kw => `<li><code>${this.escapeHtml(kw)}</code></li>`).join('')}</ul>` :
'<p>همه کلمات یافت شدند</p>'
}
</div>
</div>
</div>
${!result.isCorrect && result.hint ?
`<div class="improvement-hint">
<strong>💡 راهنمایی بهبود:</strong> ${this.escapeHtml(result.hint)}
</div>` : ''
}
</div>
<div class="result-actions">
<button class="btn btn-outline" onclick="exerciseUI.tryAgain()">
🔄 تلاش مجدد
</button>
<button class="btn btn-primary" onclick="exerciseUI.nextExercise()">
➡️ تمرین بعدی
</button>
</div>
</div>
`;
exerciseFeedback.innerHTML = html;
}
tryAgain() {
this.answerSubmitted = false;
this.render(this.currentExercise);
}
nextExercise() {
// در این نسخه ساده، به روز بعد می‌رویم
// در نسخه‌های آینده می‌توان منطق پیچیده‌تری پیاده‌سازی کرد
const nextDay = this.currentExercise.day + 1;
document.dispatchEvent(new CustomEvent('dayChanged', {
detail: { day: nextDay }
}));
}
showSolution() {
if (!this.currentExercise) return;
// در این نسخه ساده، فقط کلمات کلیدی نمایش داده می‌شوند
// در نسخه‌های آینده می‌توان راه‌حل کامل را نمایش داد
const keywords = this.currentExercise.exercise.expected_keywords;
Utils.showNotification(
`کلمات کلیدی مورد انتظار: ${keywords.join('، ')}`,
'info'
);
}
autoSaveAnswer() {
const answerTextarea = document.getElementById('exerciseAnswer');
if (!answerTextarea || this.answerSubmitted) return;
const answer = answerTextarea.value;
const key = `exercise_${this.currentExercise.day}_answer`;
Utils.saveToLocalStorage(key, answer);
}
loadSavedAnswer() {
const answerTextarea = document.getElementById('exerciseAnswer');
if (!answerTextarea) return;
const key = `exercise_${this.currentExercise.day}_answer`;
const savedAnswer = Utils.loadFromLocalStorage(key);
if (savedAnswer) {
answerTextarea.value = savedAnswer;
}
}
clearSavedAnswer() {
const key = `exercise_${this.currentExercise.day}_answer`;
localStorage.removeItem(key);
}
escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
}
// ایجاد نمونه اصلی
const exerciseUI = new ExerciseUI();