Mhdeusi commited on
Commit
a6e97b0
·
verified ·
1 Parent(s): d9f125a

Create exercise_ui.js

Browse files
Files changed (1) hide show
  1. js/ui/exercise_ui.js +276 -0
js/ui/exercise_ui.js ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class ExerciseUI {
2
+ constructor() {
3
+ this.currentExercise = null;
4
+ this.answerSubmitted = false;
5
+ }
6
+
7
+ async render(exerciseData) {
8
+ this.currentExercise = exerciseData;
9
+ this.answerSubmitted = false;
10
+
11
+ const exerciseContent = document.getElementById('exerciseContent');
12
+ const exerciseFeedback = document.getElementById('exerciseFeedback');
13
+
14
+ if (!exerciseContent) return;
15
+
16
+ // پاک کردن فیدبک قبلی
17
+ if (exerciseFeedback) {
18
+ exerciseFeedback.innerHTML = '';
19
+ }
20
+
21
+ let html = `
22
+ <div class="exercise-header">
23
+ <h4>${this.escapeHtml(exerciseData.exercise.question)}</h4>
24
+ ${exerciseData.exercise.description ?
25
+ `<p class="exercise-description">${this.escapeHtml(exerciseData.exercise.description)}</p>` : ''}
26
+ </div>
27
+
28
+ <div class="exercise-body">
29
+ <div class="answer-section">
30
+ <label for="exerciseAnswer" class="answer-label">پاسخ خود را بنویسید:</label>
31
+ <textarea
32
+ id="exerciseAnswer"
33
+ rows="6"
34
+ placeholder="پاسخ خود را اینجا وارد کنید..."
35
+ ${this.answerSubmitted ? 'disabled' : ''}
36
+ ></textarea>
37
+
38
+ ${exerciseData.exercise.hint ?
39
+ `<div class="exercise-hint">
40
+ <strong>💡 نکته:</strong> ${this.escapeHtml(exerciseData.exercise.hint)}
41
+ </div>` : ''}
42
+
43
+ <div class="exercise-actions">
44
+ <button
45
+ id="submitExercise"
46
+ class="btn btn-primary"
47
+ onclick="exerciseUI.submitAnswer()"
48
+ ${this.answerSubmitted ? 'disabled' : ''}
49
+ >
50
+ ارسال پاسخ
51
+ </button>
52
+ <button
53
+ class="btn btn-secondary"
54
+ onclick="exerciseUI.showSolution()"
55
+ >
56
+ مشاهده راه‌حل
57
+ </button>
58
+ </div>
59
+ </div>
60
+
61
+ <div class="exercise-info">
62
+ <div class="info-card">
63
+ <h5>📋 انتظارات ما</h5>
64
+ <ul class="expected-keywords">
65
+ ${exerciseData.exercise.expected_keywords.map(keyword =>
66
+ `<li><code>${this.escapeHtml(keyword)}</code></li>`
67
+ ).join('')}
68
+ </ul>
69
+ </div>
70
+
71
+ <div class="info-card">
72
+ <h5>🎯 معیارهای ارزیابی</h5>
73
+ <p>پاسخ شما بر اساس تطابق با کلمات کلیدی بالا ارزیابی می‌شود.</p>
74
+ <p>حداقل ${Math.round((exerciseData.exercise.match_threshold || 0.6) * 100)}% تطابق مورد نیاز است.</p>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ `;
79
+
80
+ exerciseContent.innerHTML = html;
81
+
82
+ // تنظیم event listener برای textarea
83
+ const textarea = document.getElementById('exerciseAnswer');
84
+ if (textarea && !this.answerSubmitted) {
85
+ textarea.addEventListener('input', this.debounce(this.autoSaveAnswer.bind(this), 1000));
86
+ this.loadSavedAnswer();
87
+ }
88
+ }
89
+
90
+ async submitAnswer() {
91
+ const answerTextarea = document.getElementById('exerciseAnswer');
92
+ const submitButton = document.getElementById('submitExercise');
93
+
94
+ if (!answerTextarea || !submitButton) return;
95
+
96
+ const answer = answerTextarea.value.trim();
97
+
98
+ if (!answer) {
99
+ Utils.showNotification('لطفاً پاسخ خود را وارد کنید', 'error');
100
+ return;
101
+ }
102
+
103
+ // غیرفعال کردن دکمه و نمایش حالت loading
104
+ submitButton.disabled = true;
105
+ submitButton.innerHTML = '⏳ در حال بررسی...';
106
+
107
+ try {
108
+ const result = await learningLogic.checkExerciseAnswer(
109
+ this.currentExercise.day,
110
+ answer
111
+ );
112
+
113
+ this.answerSubmitted = true;
114
+ this.showResult(result);
115
+ this.clearSavedAnswer();
116
+
117
+ // ارسال event برای به‌روزرسانی سایر بخش‌ها
118
+ document.dispatchEvent(new CustomEvent('exerciseSubmitted', {
119
+ detail: {
120
+ exercise: this.currentExercise,
121
+ result: result
122
+ }
123
+ }));
124
+
125
+ } catch (error) {
126
+ console.error('Error submitting exercise:', error);
127
+ Utils.showNotification('خطا در ارسال پاسخ', 'error');
128
+ submitButton.disabled = false;
129
+ submitButton.innerHTML = 'ارسال پاسخ';
130
+ }
131
+ }
132
+
133
+ showResult(result) {
134
+ const exerciseFeedback = document.getElementById('exerciseFeedback');
135
+ if (!exerciseFeedback) return;
136
+
137
+ const answerTextarea = document.getElementById('exerciseAnswer');
138
+ const submitButton = document.getElementById('submitExercise');
139
+
140
+ if (answerTextarea) {
141
+ answerTextarea.disabled = true;
142
+ }
143
+
144
+ if (submitButton) {
145
+ submitButton.disabled = true;
146
+ submitButton.innerHTML = '✅ پاسخ ارسال شده';
147
+ }
148
+
149
+ let html = `
150
+ <div class="exercise-result ${result.isCorrect ? 'success' : 'warning'}">
151
+ <div class="result-header">
152
+ <h4>${result.isCorrect ? '✅ پاسخ صحیح' : '❌ نیاز به بهبود'}</h4>
153
+ <div class="reward-badge">${result.reward} امتیاز</div>
154
+ </div>
155
+
156
+ <div class="result-body">
157
+ <p><strong>پیام:</strong> ${result.feedback.message}</p>
158
+
159
+ <div class="keywords-analysis">
160
+ <h5>📊 تحلیل کلمات کلیدی:</h5>
161
+ <div class="keywords-grid">
162
+ <div class="matched-keywords">
163
+ <h6>✅ کلمات یافت شده:</h6>
164
+ ${result.matchedKeywords.length > 0 ?
165
+ `<ul>${result.matchedKeywords.map(kw => `<li><code>${this.escapeHtml(kw)}</code></li>`).join('')}</ul>` :
166
+ '<p>هیچ کلمه‌ای یافت نشد</p>'
167
+ }
168
+ </div>
169
+ <div class="missing-keywords">
170
+ <h6>❌ کلمات مفقوده:</h6>
171
+ ${result.expectedKeywords.filter(kw => !result.matchedKeywords.includes(kw)).length > 0 ?
172
+ `<ul>${result.expectedKeywords.filter(kw => !result.matchedKeywords.includes(kw))
173
+ .map(kw => `<li><code>${this.escapeHtml(kw)}</code></li>`).join('')}</ul>` :
174
+ '<p>همه کلمات یافت شدند</p>'
175
+ }
176
+ </div>
177
+ </div>
178
+ </div>
179
+
180
+ ${!result.isCorrect && result.hint ?
181
+ `<div class="improvement-hint">
182
+ <strong>💡 راهنمایی بهبود:</strong> ${this.escapeHtml(result.hint)}
183
+ </div>` : ''
184
+ }
185
+ </div>
186
+
187
+ <div class="result-actions">
188
+ <button class="btn btn-outline" onclick="exerciseUI.tryAgain()">
189
+ 🔄 تلاش مجدد
190
+ </button>
191
+ <button class="btn btn-primary" onclick="exerciseUI.nextExercise()">
192
+ ➡️ تمرین بعدی
193
+ </button>
194
+ </div>
195
+ </div>
196
+ `;
197
+
198
+ exerciseFeedback.innerHTML = html;
199
+ }
200
+
201
+ tryAgain() {
202
+ this.answerSubmitted = false;
203
+ this.render(this.currentExercise);
204
+ }
205
+
206
+ nextExercise() {
207
+ // در این نسخه ساده، به روز بعد می‌رویم
208
+ // در نسخه‌های آینده می‌توان منطق پیچیده‌تری پیاده‌سازی کرد
209
+ const nextDay = this.currentExercise.day + 1;
210
+ document.dispatchEvent(new CustomEvent('dayChanged', {
211
+ detail: { day: nextDay }
212
+ }));
213
+ }
214
+
215
+ showSolution() {
216
+ if (!this.currentExercise) return;
217
+
218
+ // در این نسخه ساده، فقط کلمات کلیدی نمایش داده می‌شوند
219
+ // در نسخه‌های آینده می‌توان راه‌حل کامل را نمایش داد
220
+ const keywords = this.currentExercise.exercise.expected_keywords;
221
+ Utils.showNotification(
222
+ `کلمات کلیدی مورد انتظار: ${keywords.join('، ')}`,
223
+ 'info'
224
+ );
225
+ }
226
+
227
+ autoSaveAnswer() {
228
+ const answerTextarea = document.getElementById('exerciseAnswer');
229
+ if (!answerTextarea || this.answerSubmitted) return;
230
+
231
+ const answer = answerTextarea.value;
232
+ const key = `exercise_${this.currentExercise.day}_answer`;
233
+ Utils.saveToLocalStorage(key, answer);
234
+ }
235
+
236
+ loadSavedAnswer() {
237
+ const answerTextarea = document.getElementById('exerciseAnswer');
238
+ if (!answerTextarea) return;
239
+
240
+ const key = `exercise_${this.currentExercise.day}_answer`;
241
+ const savedAnswer = Utils.loadFromLocalStorage(key);
242
+
243
+ if (savedAnswer) {
244
+ answerTextarea.value = savedAnswer;
245
+ }
246
+ }
247
+
248
+ clearSavedAnswer() {
249
+ const key = `exercise_${this.currentExercise.day}_answer`;
250
+ localStorage.removeItem(key);
251
+ }
252
+
253
+ escapeHtml(unsafe) {
254
+ return unsafe
255
+ .replace(/&/g, "&amp;")
256
+ .replace(/</g, "&lt;")
257
+ .replace(/>/g, "&gt;")
258
+ .replace(/"/g, "&quot;")
259
+ .replace(/'/g, "&#039;");
260
+ }
261
+
262
+ debounce(func, wait) {
263
+ let timeout;
264
+ return function executedFunction(...args) {
265
+ const later = () => {
266
+ clearTimeout(timeout);
267
+ func(...args);
268
+ };
269
+ clearTimeout(timeout);
270
+ timeout = setTimeout(later, wait);
271
+ };
272
+ }
273
+ }
274
+
275
+ // ایجاد نمونه اصلی
276
+ const exerciseUI = new ExerciseUI();