gebsunamy commited on
Commit
cb2c324
·
verified ·
1 Parent(s): c0136ee

Upload script.js

Browse files
Files changed (1) hide show
  1. static/script.js +322 -59
static/script.js CHANGED
@@ -1,67 +1,330 @@
1
- const chatBox = document.getElementById('chat-box');
2
- const chatForm = document.getElementById('chat-form');
3
- const userInput = document.getElementById('user-input');
4
- const trainingOverlay = document.getElementById('training-overlay');
5
-
6
- // التحقق من حالة التدريب كل ثانية
7
- function checkStatus() {
8
- fetch('/training-status')
9
- .then(r => r.json())
10
- .then(data => {
11
- if (data.is_training) {
12
- trainingOverlay.classList.remove('hidden');
13
- document.getElementById('progress-fill').style.width = data.progress + '%';
14
- document.getElementById('progress-text').innerText = data.progress + '%';
15
- document.getElementById('task-name').innerText = data.current_task;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  } else {
17
- trainingOverlay.classList.add('hidden');
 
18
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  });
20
- }
 
 
 
 
 
 
 
21
 
22
- setInterval(checkStatus, 1500);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- chatForm.onsubmit = async (e) => {
25
- e.preventDefault();
26
- const msg = userInput.value;
27
- appendMsg(msg, 'user');
28
- userInput.value = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- const res = await fetch('/chat', {
31
- method: 'POST',
32
- headers: {'Content-Type': 'application/json'},
33
- body: JSON.stringify({message: msg})
 
 
 
 
 
 
 
 
34
  });
35
-
36
- const data = await res.json();
37
- if (data.error) appendMsg(data.error, 'bot');
38
- else appendMsg(data.response_text, 'bot');
39
- };
40
-
41
- function appendMsg(text, side) {
42
- const div = document.createElement('div');
43
- div.className = `msg ${side}`;
44
- div.innerText = text;
45
- chatBox.appendChild(div);
46
- chatBox.scrollTop = chatBox.scrollHeight;
47
- }
48
-
49
- // أزرار المودال
50
- document.getElementById('add-data-btn').onclick = () => document.getElementById('modal').classList.remove('hidden');
51
- document.getElementById('close-btn').onclick = () => document.getElementById('modal').classList.add('hidden');
52
-
53
- document.getElementById('save-btn').onclick = async () => {
54
- const q = document.getElementById('new-q').value;
55
- const a = document.getElementById('new-a').value;
56
- if(!q || !a) return;
57
-
58
- await fetch('/save', {
59
- method: 'POST',
60
- headers: {'Content-Type': 'application/json'},
61
- body: JSON.stringify([{question: q, answer: a}])
62
  });
63
-
64
- document.getElementById('modal').classList.add('hidden');
65
- document.getElementById('new-q').value = '';
66
- document.getElementById('new-a').value = '';
67
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener("DOMContentLoaded", () => {
2
+ // --- DOM Elements ---
3
+ const chatContainer = document.getElementById("chat-container");
4
+ const chatForm = document.getElementById("chat-form");
5
+ const userInput = document.getElementById("user-input");
6
+ const sendBtn = document.getElementById("send-btn");
7
+ const usageStatsDiv = document.getElementById("usage-stats");
8
+ const modelStatsDiv = document.getElementById("model-stats");
9
+ const suggestionsContainer = document.getElementById("suggestions-container");
10
+ const themeToggle = document.getElementById("theme-toggle");
11
+ const showSaveFormBtn = document.getElementById("show-save-form-btn");
12
+ const saveModalOverlay = document.getElementById("save-modal-overlay");
13
+ const saveForm = document.getElementById("save-form");
14
+ const cancelSaveBtn = document.getElementById("cancel-save-btn");
15
+ const addAnswerBtn = document.getElementById("add-answer-btn");
16
+ const answersContainer = document.getElementById("answers-container");
17
+ const newQuestionInput = document.getElementById("new-question");
18
+ const saveStatus = document.getElementById("save-status");
19
+
20
+ // --- Helper Functions ---
21
+ async function updateStats() {
22
+ try {
23
+ const [statusRes, statsRes] = await Promise.all([fetch("/status"), fetch("/stats")]);
24
+ if (statusRes.ok) {
25
+ const stats = await statusRes.json();
26
+ usageStatsDiv.innerHTML = `📊 ${stats.tokens_used}/${stats.max_tokens} رمز | ⏱️ ${stats.training_time_used.toFixed(1)}ث/${stats.max_training_time}ث تدريب`;
27
+ }
28
+ if (statsRes.ok) {
29
+ const stats = await statsRes.json();
30
+ modelStatsDiv.innerHTML = `🧠 ${stats.question_count} سؤال في قاعدة البيانات`;
31
+ }
32
+ } catch (error) {
33
+ usageStatsDiv.innerHTML = "⚠️ تعذر تحميل الإحصائيات";
34
+ modelStatsDiv.innerHTML = "";
35
+ }
36
+ }
37
+
38
+ function addMessage(text, className) {
39
+ const div = document.createElement("div");
40
+ div.className = `message ${className}`;
41
+ div.textContent = text;
42
+ chatContainer.appendChild(div);
43
+ chatContainer.scrollTop = chatContainer.scrollHeight;
44
+ }
45
+
46
+ function addBotResponse(data) {
47
+ const messageDiv = document.createElement("div");
48
+ messageDiv.className = "message bot-message";
49
+ removeTypingIndicator();
50
+
51
+ if (data.ambiguous_questions) {
52
+ const header = document.createElement('div');
53
+ header.className = 'content';
54
+ header.textContent = 'وجدنا عدة أسئلة متشابهة. أي واحد تقصد؟';
55
+ messageDiv.appendChild(header);
56
+
57
+ const buttonContainer = document.createElement('div');
58
+ buttonContainer.className = 'selection-buttons';
59
+
60
+ data.ambiguous_questions.forEach(q => {
61
+ const btn = document.createElement('button');
62
+ btn.className = 'selection-btn';
63
+ btn.textContent = q;
64
+ btn.onclick = () => handleSelection(data.user_query, q);
65
+ buttonContainer.appendChild(btn);
66
+ });
67
+ messageDiv.appendChild(buttonContainer);
68
+ } else {
69
+ const contentDiv = document.createElement("div");
70
+ contentDiv.className = "content";
71
+ messageDiv.appendChild(contentDiv);
72
+
73
+ const statsDiv = document.createElement("div");
74
+ statsDiv.className = "stats-grid";
75
+ const createStat = (icon, value, label) => {
76
+ const statDiv = document.createElement('div');
77
+ statDiv.className = 'stat-item';
78
+ statDiv.innerHTML = `${icon} <span>${value}</span> ${label}`;
79
+ return statDiv;
80
+ };
81
+ statsDiv.appendChild(createStat('⏱️', `${data.duration.toFixed(3)}s`, ''));
82
+ statsDiv.appendChild(createStat('🔤', data.token_count, 'رمز'));
83
+ if (data.similarity_score !== undefined && data.similarity_score !== null) {
84
+ const scoreValue = `${(data.similarity_score * 100).toFixed(1)}%`;
85
+ statsDiv.appendChild(createStat('🎯', scoreValue, 'تشابه'));
86
+ }
87
+ statsDiv.appendChild(createStat('💻', `${data.cpu_usage[0].toFixed(1)}%`, 'CPU'));
88
+ statsDiv.appendChild(createStat('🧠', `${data.mem_usage[1].toFixed(1)}%`, 'RAM'));
89
+
90
+ typeAnimation(contentDiv, data.response_text, () => {
91
+ messageDiv.appendChild(statsDiv);
92
+ chatContainer.scrollTop = chatContainer.scrollHeight;
93
+ });
94
+ }
95
+
96
+ chatContainer.appendChild(messageDiv);
97
+ chatContainer.scrollTop = chatContainer.scrollHeight;
98
+ }
99
+
100
+ async function handleSelection(originalQuery, selectedQuestion) {
101
+ try {
102
+ await fetch('/log_selection', {
103
+ method: 'POST',
104
+ headers: { 'Content-Type': 'application/json' },
105
+ body: JSON.stringify({ user_query: originalQuery, selected_question: selectedQuestion })
106
+ });
107
+ } catch (error) {
108
+ console.error("Failed to log selection:", error);
109
+ }
110
+
111
+ addMessage(selectedQuestion, "user-message");
112
+ showTypingIndicator();
113
+
114
+ try {
115
+ const response = await fetch("/chat", {
116
+ method: "POST",
117
+ headers: { "Content-Type": "application/json" },
118
+ body: JSON.stringify({ question: selectedQuestion }),
119
+ });
120
+ const data = await response.json();
121
+ if (!response.ok) throw new Error(data.error || 'حدث خطأ في الخادم');
122
+
123
+ const allButtons = document.querySelectorAll('.selection-buttons');
124
+ if (allButtons.length > 0) {
125
+ allButtons[allButtons.length - 1].parentElement.remove();
126
+ }
127
+
128
+ addBotResponse(data);
129
+ } catch (error) {
130
+ addMessage(`❌ خطأ: ${error.message}`, "bot-message");
131
+ } finally {
132
+ removeTypingIndicator();
133
+ }
134
+ }
135
+
136
+ function showTypingIndicator() {
137
+ const div = document.createElement("div");
138
+ div.className = "message bot-message typing-indicator";
139
+ div.innerHTML = "<span></span><span></span><span></span>";
140
+ chatContainer.appendChild(div);
141
+ chatContainer.scrollTop = chatContainer.scrollHeight;
142
+ }
143
+
144
+ function removeTypingIndicator() {
145
+ const indicator = document.querySelector(".typing-indicator");
146
+ if (indicator) indicator.remove();
147
+ }
148
+
149
+ function typeAnimation(element, text, callback) {
150
+ let i = 0;
151
+ element.innerHTML = "";
152
+ const interval = setInterval(() => {
153
+ if (i < text.length) {
154
+ element.innerHTML += text.charAt(i);
155
+ i++;
156
+ chatContainer.scrollTop = chatContainer.scrollHeight;
157
  } else {
158
+ clearInterval(interval);
159
+ if (callback) callback();
160
  }
161
+ }, 20);
162
+ }
163
+
164
+ function displaySuggestions(suggestions) {
165
+ suggestionsContainer.innerHTML = '';
166
+ if (suggestions.length === 0) {
167
+ suggestionsContainer.classList.add('hidden');
168
+ return;
169
+ }
170
+ suggestions.forEach(suggestion => {
171
+ const div = document.createElement('div');
172
+ div.className = 'suggestion';
173
+ div.textContent = suggestion;
174
+ div.addEventListener('click', () => {
175
+ userInput.value = suggestion;
176
+ suggestionsContainer.classList.add('hidden');
177
+ chatForm.dispatchEvent(new Event('submit'));
178
+ });
179
+ suggestionsContainer.appendChild(div);
180
  });
181
+ suggestionsContainer.classList.remove('hidden');
182
+ }
183
+
184
+ function showSaveStatus(message, type) {
185
+ saveStatus.textContent = message;
186
+ saveStatus.className = `status-${type}`;
187
+ saveStatus.classList.remove('hidden');
188
+ }
189
 
190
+ // --- Event Listeners ---
191
+ chatForm.addEventListener("submit", async (e) => {
192
+ e.preventDefault();
193
+ const question = userInput.value.trim();
194
+ if (!question) return;
195
+ addMessage(question, "user-message");
196
+ userInput.value = "";
197
+ suggestionsContainer.classList.add('hidden');
198
+ showTypingIndicator();
199
+ sendBtn.disabled = true;
200
+ try {
201
+ const response = await fetch("/chat", {
202
+ method: "POST",
203
+ headers: { "Content-Type": "application/json" },
204
+ body: JSON.stringify({ question }),
205
+ });
206
+ const data = await response.json();
207
+ if (!response.ok) throw new Error(data.error || 'حدث خطأ في الخادم');
208
+ addBotResponse(data);
209
+ } catch (error) {
210
+ addMessage(`❌ خطأ: ${error.message}`, "bot-message");
211
+ } finally {
212
+ removeTypingIndicator();
213
+ sendBtn.disabled = false;
214
+ updateStats();
215
+ }
216
+ });
217
 
218
+ let suggestionTimeout;
219
+ userInput.addEventListener("input", () => {
220
+ clearTimeout(suggestionTimeout);
221
+ const query = userInput.value.trim();
222
+ if (query.length < 2) {
223
+ suggestionsContainer.classList.add('hidden');
224
+ return;
225
+ }
226
+ suggestionTimeout = setTimeout(async () => {
227
+ try {
228
+ const response = await fetch(`/suggest?q=${encodeURIComponent(query)}`);
229
+ if (!response.ok) throw new Error("فشل في جلب الاقتراحات");
230
+ displaySuggestions(await response.json());
231
+ } catch (error) {
232
+ suggestionsContainer.classList.add('hidden');
233
+ }
234
+ }, 300);
235
+ });
236
 
237
+ // --- Modal & Other UI ---
238
+ const singleQuestionForm = document.getElementById('single-question-form');
239
+ const jsonForm = document.getElementById('json-form');
240
+ showSaveFormBtn.addEventListener("click", () => saveModalOverlay.classList.remove("hidden"));
241
+ cancelSaveBtn.addEventListener("click", () => {
242
+ saveModalOverlay.classList.add("hidden");
243
+ saveForm.reset();
244
+ document.querySelectorAll('.answer-input:not(:first-child)').forEach(el => el.remove());
245
+ saveStatus.textContent = "";
246
+ saveStatus.classList.add('hidden');
247
+ singleQuestionForm.classList.remove('hidden');
248
+ jsonForm.classList.add('hidden');
249
  });
250
+ addAnswerBtn.addEventListener("click", () => {
251
+ const input = document.createElement("input");
252
+ input.type = "text";
253
+ input.className = "answer-input";
254
+ input.placeholder = `الإجابة #${answersContainer.children.length + 1}`;
255
+ answersContainer.appendChild(input);
256
+ input.focus();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  });
258
+ document.querySelectorAll('input[name="save-mode"]').forEach(radio => {
259
+ radio.addEventListener('change', (e) => {
260
+ if (e.target.value === 'json') {
261
+ singleQuestionForm.classList.add('hidden');
262
+ jsonForm.classList.remove('hidden');
263
+ } else {
264
+ singleQuestionForm.classList.remove('hidden');
265
+ jsonForm.classList.add('hidden');
266
+ }
267
+ });
268
+ });
269
+ saveForm.addEventListener("submit", async (e) => {
270
+ e.preventDefault();
271
+ const saveMode = document.querySelector('input[name="save-mode"]:checked').value;
272
+ let requestData;
273
+ if (saveMode === 'json') {
274
+ const jsonInput = document.getElementById('json-input').value.trim();
275
+ if (!jsonInput) return showSaveStatus('الرجاء إدخال بيانات JSON', 'error');
276
+ try {
277
+ requestData = JSON.parse(jsonInput);
278
+ } catch (err) {
279
+ return showSaveStatus('صيغة JSON غير صالحة', 'error');
280
+ }
281
+ } else {
282
+ const question = newQuestionInput.value.trim();
283
+ const answers = Array.from(document.querySelectorAll(".answer-input")).map(input => input.value.trim()).filter(Boolean);
284
+ if (!question || answers.length === 0) return showSaveStatus('الرجاء تقديم سؤال وإجابة واحدة على الأقل', 'error');
285
+ requestData = answers.map(answer => ({ question, answer }));
286
+ }
287
+ showSaveStatus('جاري الحفظ والتدريب...', 'info');
288
+ try {
289
+ const response = await fetch("/save", {
290
+ method: "POST",
291
+ headers: { "Content-Type": "application/json" },
292
+ body: JSON.stringify(requestData),
293
+ });
294
+ const result = await response.json();
295
+ if (!response.ok) throw new Error(result.message || 'حدث خطأ غير معروف');
296
+ showSaveStatus(`✅ ${result.message}`, 'success');
297
+ setTimeout(() => cancelSaveBtn.click(), 2000);
298
+ } catch (error) {
299
+ showSaveStatus(`❌ ${error.message}`, 'error');
300
+ } finally {
301
+ updateStats();
302
+ }
303
+ });
304
+ saveModalOverlay.addEventListener('click', (e) => {
305
+ if (e.target === saveModalOverlay) cancelSaveBtn.click();
306
+ });
307
+ document.addEventListener('click', (e) => {
308
+ if (!userInput.contains(e.target) && !suggestionsContainer.contains(e.target)) {
309
+ suggestionsContainer.classList.add('hidden');
310
+ }
311
+ });
312
+ document.addEventListener('keydown', (e) => {
313
+ if (e.key === 'Escape' && !saveModalOverlay.classList.contains('hidden')) cancelSaveBtn.click();
314
+ if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
315
+ e.preventDefault();
316
+ userInput.focus();
317
+ }
318
+ });
319
+
320
+ // --- Initial Load ---
321
+ const savedTheme = localStorage.getItem('theme') || 'dark';
322
+ document.body.classList.toggle('dark-mode', savedTheme === 'dark');
323
+ themeToggle.textContent = savedTheme === 'dark' ? '☀️' : '🌙';
324
+ updateStats();
325
+ themeToggle.addEventListener('click', () => {
326
+ const isDark = document.body.classList.toggle('dark-mode');
327
+ themeToggle.textContent = isDark ? '☀️' : '🌙';
328
+ localStorage.setItem('theme', isDark ? 'dark' : 'light');
329
+ });
330
+ });