NitinBot001 commited on
Commit
7ba9095
·
verified ·
1 Parent(s): 5ed8087

Update static/script.js

Browse files
Files changed (1) hide show
  1. static/script.js +479 -80
static/script.js CHANGED
@@ -17,11 +17,12 @@ document.addEventListener('DOMContentLoaded', () => {
17
  const imagePreview = document.getElementById('image-preview');
18
  const removeImageBtn = document.getElementById('remove-image-btn');
19
 
20
- // Add search and filter elements (assuming they exist in HTML or create them)
21
- let searchInput, clearAllBtn;
22
 
23
  // --- State ---
24
  let currentChatId = null;
 
25
  let conversationsCache = {};
26
  let selectedImageFile = null;
27
  let imgbbApiKey = '';
@@ -29,10 +30,106 @@ document.addEventListener('DOMContentLoaded', () => {
29
  let firebaseDB = null;
30
  let useFirebase = false;
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  // --- Firebase Configuration ---
33
  const initializeFirebase = async () => {
34
  try {
35
- // Check if Firebase is available and configured
36
  if (typeof firebase !== 'undefined' && window.firebaseConfig) {
37
  const { initializeApp } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-app.js');
38
  const { getFirestore, collection, doc, setDoc, getDoc, getDocs, deleteDoc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
@@ -53,32 +150,35 @@ document.addEventListener('DOMContentLoaded', () => {
53
  }
54
  };
55
 
56
- // --- Storage Management ---
57
  const saveSessionData = async (chatId, data) => {
58
  try {
59
  if (useFirebase && firebaseDB) {
60
  const { setDoc, doc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
61
- await setDoc(doc(firebaseDB, 'chat_sessions', chatId), {
62
  ...data,
 
 
63
  updatedAt: new Date().toISOString()
64
  });
65
- console.log('Session saved to Firebase:', chatId);
66
  } else {
67
- // Fallback to localStorage
68
- const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
69
- sessions[chatId] = {
70
  ...data,
 
71
  updatedAt: new Date().toISOString()
72
  };
73
- localStorage.setItem('easyfarms_sessions', JSON.stringify(sessions));
74
- console.log('Session saved to localStorage:', chatId);
75
  }
76
  } catch (error) {
77
  console.error('Failed to save session data:', error);
78
  // Fallback to localStorage
79
- const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
80
- sessions[chatId] = data;
81
- localStorage.setItem('easyfarms_sessions', JSON.stringify(sessions));
82
  }
83
  };
84
 
@@ -86,7 +186,7 @@ document.addEventListener('DOMContentLoaded', () => {
86
  try {
87
  if (useFirebase && firebaseDB) {
88
  const { getDoc, doc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
89
- const docRef = doc(firebaseDB, 'chat_sessions', chatId);
90
  const docSnap = await getDoc(docRef);
91
  if (docSnap.exists()) {
92
  console.log('Session loaded from Firebase:', chatId);
@@ -94,42 +194,44 @@ document.addEventListener('DOMContentLoaded', () => {
94
  }
95
  } else {
96
  // Fallback to localStorage
97
- const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
98
- if (sessions[chatId]) {
99
  console.log('Session loaded from localStorage:', chatId);
100
- return sessions[chatId];
101
  }
102
  }
103
  return null;
104
  } catch (error) {
105
  console.error('Failed to load session data:', error);
106
  // Fallback to localStorage
107
- const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
108
- return sessions[chatId] || null;
109
  }
110
  };
111
 
112
  const loadAllSessions = async () => {
113
  try {
114
  if (useFirebase && firebaseDB) {
115
- const { getDocs, collection } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
116
- const querySnapshot = await getDocs(collection(firebaseDB, 'chat_sessions'));
 
117
  const sessions = {};
118
  querySnapshot.forEach((doc) => {
119
- sessions[doc.id] = doc.data();
 
120
  });
121
- console.log('All sessions loaded from Firebase');
122
  return sessions;
123
  } else {
124
  // Fallback to localStorage
125
- const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
126
- console.log('All sessions loaded from localStorage');
127
  return sessions;
128
  }
129
  } catch (error) {
130
  console.error('Failed to load all sessions:', error);
131
  // Fallback to localStorage
132
- return JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
133
  }
134
  };
135
 
@@ -137,13 +239,13 @@ document.addEventListener('DOMContentLoaded', () => {
137
  try {
138
  if (useFirebase && firebaseDB) {
139
  const { deleteDoc, doc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
140
- await deleteDoc(doc(firebaseDB, 'chat_sessions', chatId));
141
  console.log('Session deleted from Firebase:', chatId);
142
  } else {
143
  // Fallback to localStorage
144
- const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
145
- delete sessions[chatId];
146
- localStorage.setItem('easyfarms_sessions', JSON.stringify(sessions));
147
  console.log('Session deleted from localStorage:', chatId);
148
  }
149
  } catch (error) {
@@ -160,11 +262,15 @@ document.addEventListener('DOMContentLoaded', () => {
160
 
161
  // Initialize Firebase
162
  firebase = await initializeFirebase();
 
 
 
163
 
164
  await fetchConfig();
165
  await loadCachedSessions();
166
  await renderChatHistoryFromAPI();
167
  setupSearchAndFilter();
 
168
 
169
  // Event Listeners
170
  sendBtn.addEventListener('click', sendMessage);
@@ -195,7 +301,10 @@ document.addEventListener('DOMContentLoaded', () => {
195
  }
196
  });
197
 
198
- console.log('EasyFarms Chat initialized with new backend integration');
 
 
 
199
  };
200
 
201
  const fetchConfig = async () => {
@@ -211,6 +320,63 @@ document.addEventListener('DOMContentLoaded', () => {
211
  }
212
  };
213
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  const setupSearchAndFilter = () => {
215
  // Create search input if it doesn't exist
216
  if (!searchInput) {
@@ -227,14 +393,13 @@ document.addEventListener('DOMContentLoaded', () => {
227
  font-size: 14px;
228
  `;
229
 
230
- // Insert before chat history list
231
  chatHistoryList.parentNode.insertBefore(searchInput, chatHistoryList);
232
  }
233
 
234
  // Create clear all button if it doesn't exist
235
  if (!clearAllBtn) {
236
  clearAllBtn = document.createElement('button');
237
- clearAllBtn.textContent = 'Clear All Chats';
238
  clearAllBtn.className = 'clear-all-btn';
239
  clearAllBtn.style.cssText = `
240
  width: 100%;
@@ -248,7 +413,6 @@ document.addEventListener('DOMContentLoaded', () => {
248
  font-size: 12px;
249
  `;
250
 
251
- // Insert after search input
252
  searchInput.parentNode.insertBefore(clearAllBtn, chatHistoryList);
253
  }
254
 
@@ -271,32 +435,29 @@ document.addEventListener('DOMContentLoaded', () => {
271
  };
272
 
273
  const confirmClearAllChats = () => {
274
- if (confirm('Are you sure you want to delete all chats? This action cannot be undone.')) {
275
  clearAllChats();
276
  }
277
  };
278
 
279
  const clearAllChats = async () => {
280
  try {
281
- // Get all chat IDs
 
 
 
 
 
 
 
 
 
 
 
282
  const allSessions = await loadAllSessions();
283
  const chatIds = Object.keys(allSessions);
284
 
285
- // Delete each chat from backend
286
  for (const chatId of chatIds) {
287
- try {
288
- const response = await fetch(`/chat/${chatId}`, {
289
- method: 'DELETE'
290
- });
291
-
292
- if (response.ok) {
293
- console.log('Deleted chat from backend:', chatId);
294
- }
295
- } catch (error) {
296
- console.error('Failed to delete chat from backend:', chatId, error);
297
- }
298
-
299
- // Delete from local storage
300
  await deleteSessionData(chatId);
301
  }
302
 
@@ -304,21 +465,14 @@ document.addEventListener('DOMContentLoaded', () => {
304
  conversationsCache = {};
305
  chatHistoryList.innerHTML = '';
306
  startNewChat();
 
307
 
308
- console.log('All chats cleared successfully');
309
  } catch (error) {
310
  console.error('Failed to clear all chats:', error);
311
  }
312
  };
313
- // Generate or get user ID (could be device-based or login-based)
314
- const getUserId = () => {
315
- let userId = localStorage.getItem('easyfarms_user_id');
316
- if (!userId) {
317
- userId = 'user_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
318
- localStorage.setItem('easyfarms_user_id', userId);
319
- }
320
- return userId;
321
- };
322
  // --- Core Chat Functions ---
323
  const sendMessage = async () => {
324
  const messageText = messageInput.value.trim();
@@ -343,18 +497,18 @@ document.addEventListener('DOMContentLoaded', () => {
343
  permanentImageUrl = await uploadImageToImgBB(selectedImageFile);
344
  }
345
 
346
- // Prepare request data
347
  const requestData = {
348
  query: messageText,
349
  session_id: currentChatId,
350
- user_id: getUserId() // Add this
351
  };
352
 
353
  if (permanentImageUrl) {
354
  requestData.image_url = permanentImageUrl;
355
  }
356
 
357
- // Send to new API endpoint
358
  const response = await fetch('/chat', {
359
  method: 'POST',
360
  headers: {
@@ -395,6 +549,9 @@ document.addEventListener('DOMContentLoaded', () => {
395
  data.is_new_chat
396
  );
397
 
 
 
 
398
  } catch (error) {
399
  console.error('Error sending message:', error);
400
 
@@ -437,14 +594,14 @@ document.addEventListener('DOMContentLoaded', () => {
437
  // --- Chat Management ---
438
  const startNewChat = () => {
439
  currentChatId = null;
440
- chatMessages.innerHTML = `<div class="welcome-message"><h1>EasyFarms Assistant</h1></div>`;
441
  chatTitle.textContent = "New Chat";
442
  updateActiveChatItem();
443
- console.log('Started new chat');
444
  };
445
 
446
  const switchChat = async (chatId) => {
447
- console.log('Switching to chat:', chatId);
448
  currentChatId = chatId;
449
  chatMessages.innerHTML = '';
450
 
@@ -461,7 +618,7 @@ document.addEventListener('DOMContentLoaded', () => {
461
  });
462
 
463
  try {
464
- const response = await fetch(`/chat/${chatId}/messages`);
465
 
466
  if (!response.ok) {
467
  throw new Error(`HTTP error! status: ${response.status}`);
@@ -505,7 +662,7 @@ document.addEventListener('DOMContentLoaded', () => {
505
 
506
  try {
507
  // Delete from backend
508
- const response = await fetch(`/chat/${chatId}`, {
509
  method: 'DELETE'
510
  });
511
 
@@ -530,6 +687,7 @@ document.addEventListener('DOMContentLoaded', () => {
530
  }
531
 
532
  console.log('Chat deleted successfully:', chatId);
 
533
 
534
  } catch (error) {
535
  console.error('Error deleting chat:', error);
@@ -546,14 +704,15 @@ document.addEventListener('DOMContentLoaded', () => {
546
  conversationsCache[chatId] = {
547
  title,
548
  messages: [],
549
- created_at: new Date().toISOString()
 
550
  };
551
 
552
  // Add to UI
553
  const item = createChatHistoryItem(chatId, title);
554
  chatHistoryList.prepend(item);
555
 
556
- console.log('Created new chat:', chatId);
557
  }
558
 
559
  // Create message objects with IDs
@@ -575,6 +734,7 @@ document.addEventListener('DOMContentLoaded', () => {
575
  // Add to cache
576
  conversationsCache[chatId].messages.push(userMessage, assistantMessage);
577
  conversationsCache[chatId].updated_at = new Date().toISOString();
 
578
 
579
  // Save to storage
580
  await saveSessionData(chatId, conversationsCache[chatId]);
@@ -582,7 +742,7 @@ document.addEventListener('DOMContentLoaded', () => {
582
  updateActiveChatItem();
583
  chatTitle.textContent = conversationsCache[chatId].title;
584
 
585
- console.log('Updated cache for chat:', chatId);
586
  };
587
 
588
  const createChatHistoryItem = (chatId, title) => {
@@ -629,7 +789,7 @@ document.addEventListener('DOMContentLoaded', () => {
629
  const loadCachedSessions = async () => {
630
  try {
631
  conversationsCache = await loadAllSessions();
632
- console.log('Loaded cached sessions:', Object.keys(conversationsCache).length);
633
  } catch (error) {
634
  console.error('Failed to load cached sessions:', error);
635
  conversationsCache = {};
@@ -692,14 +852,14 @@ document.addEventListener('DOMContentLoaded', () => {
692
  // --- API Integration ---
693
  const renderChatHistoryFromAPI = async () => {
694
  try {
695
- const response = await fetch('/chats');
696
 
697
  if (!response.ok) {
698
  throw new Error(`HTTP error! status: ${response.status}`);
699
  }
700
 
701
  const sessions = await response.json();
702
- console.log('Loaded chat sessions from API:', sessions.length);
703
 
704
  chatHistoryList.innerHTML = '';
705
 
@@ -715,6 +875,7 @@ document.addEventListener('DOMContentLoaded', () => {
715
  conversationsCache[session.session_id].message_count = session.message_count;
716
  conversationsCache[session.session_id].created_at = session.created_at;
717
  conversationsCache[session.session_id].updated_at = session.updated_at;
 
718
 
719
  // Create UI item
720
  const item = createChatHistoryItem(session.session_id, session.title);
@@ -723,18 +884,21 @@ document.addEventListener('DOMContentLoaded', () => {
723
 
724
  // Save updated cache
725
  for (const [chatId, data] of Object.entries(conversationsCache)) {
726
- await saveSessionData(chatId, data);
 
 
727
  }
728
 
729
  updateActiveChatItem();
 
730
 
731
  } catch (error) {
732
  console.error("Failed to render chat history from API:", error);
733
 
734
  // Fallback to local cache
735
- console.log('Falling back to local cache...');
736
  for (const [chatId, data] of Object.entries(conversationsCache)) {
737
- if (data.title) {
738
  const item = createChatHistoryItem(chatId, data.title);
739
  chatHistoryList.appendChild(item);
740
  }
@@ -750,6 +914,241 @@ document.addEventListener('DOMContentLoaded', () => {
750
 
751
  const closeSidebar = () => appContainer.classList.remove('sidebar-visible');
752
 
753
- // --- Start the Application ---
754
- init();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
755
  });
 
17
  const imagePreview = document.getElementById('image-preview');
18
  const removeImageBtn = document.getElementById('remove-image-btn');
19
 
20
+ // Add search and filter elements
21
+ let searchInput, clearAllBtn, userStatsDiv;
22
 
23
  // --- State ---
24
  let currentChatId = null;
25
+ let currentUserId = null;
26
  let conversationsCache = {};
27
  let selectedImageFile = null;
28
  let imgbbApiKey = '';
 
30
  let firebaseDB = null;
31
  let useFirebase = false;
32
 
33
+ // --- User Management ---
34
+ const generateUserFingerprint = () => {
35
+ // Create a device fingerprint based on available browser info
36
+ const canvas = document.createElement('canvas');
37
+ const ctx = canvas.getContext('2d');
38
+ ctx.textBaseline = 'top';
39
+ ctx.font = '14px Arial';
40
+ ctx.fillText('Device fingerprint', 2, 2);
41
+
42
+ const fingerprint = [
43
+ navigator.userAgent,
44
+ navigator.language,
45
+ navigator.platform,
46
+ navigator.cookieEnabled,
47
+ navigator.doNotTrack,
48
+ screen.width + 'x' + screen.height,
49
+ screen.colorDepth,
50
+ new Date().getTimezoneOffset(),
51
+ canvas.toDataURL(),
52
+ navigator.hardwareConcurrency || 'unknown',
53
+ navigator.deviceMemory || 'unknown'
54
+ ].join('|');
55
+
56
+ // Create hash
57
+ return hashCode(fingerprint).toString();
58
+ };
59
+
60
+ const hashCode = (str) => {
61
+ let hash = 0;
62
+ for (let i = 0; i < str.length; i++) {
63
+ const char = str.charCodeAt(i);
64
+ hash = ((hash << 5) - hash) + char;
65
+ hash = hash & hash; // Convert to 32bit integer
66
+ }
67
+ return Math.abs(hash);
68
+ };
69
+
70
+ const initializeUser = async () => {
71
+ // Try to get existing user ID from storage
72
+ let userId = localStorage.getItem('easyfarms_user_id');
73
+
74
+ if (!userId) {
75
+ // Generate new user ID based on device fingerprint
76
+ const deviceFingerprint = generateUserFingerprint();
77
+ const timestamp = Date.now();
78
+ const randomPart = Math.random().toString(36).substr(2, 8);
79
+
80
+ userId = `user_${deviceFingerprint}_${timestamp}_${randomPart}`;
81
+ localStorage.setItem('easyfarms_user_id', userId);
82
+
83
+ console.log('Generated new user ID:', userId);
84
+ } else {
85
+ console.log('Using existing user ID:', userId);
86
+ }
87
+
88
+ currentUserId = userId;
89
+
90
+ // Also save to Firebase if available
91
+ if (useFirebase && firebaseDB) {
92
+ try {
93
+ await saveUserData(userId, {
94
+ created_at: new Date().toISOString(),
95
+ device_fingerprint: generateUserFingerprint(),
96
+ last_seen: new Date().toISOString()
97
+ });
98
+ } catch (error) {
99
+ console.error('Failed to save user data to Firebase:', error);
100
+ }
101
+ }
102
+
103
+ return userId;
104
+ };
105
+
106
+ const saveUserData = async (userId, userData) => {
107
+ try {
108
+ if (useFirebase && firebaseDB) {
109
+ const { setDoc, doc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
110
+ await setDoc(doc(firebaseDB, 'users', userId), {
111
+ ...userData,
112
+ updatedAt: new Date().toISOString()
113
+ });
114
+ console.log('User data saved to Firebase:', userId);
115
+ } else {
116
+ // Fallback to localStorage
117
+ const users = JSON.parse(localStorage.getItem('easyfarms_users') || '{}');
118
+ users[userId] = {
119
+ ...userData,
120
+ updatedAt: new Date().toISOString()
121
+ };
122
+ localStorage.setItem('easyfarms_users', JSON.stringify(users));
123
+ console.log('User data saved to localStorage:', userId);
124
+ }
125
+ } catch (error) {
126
+ console.error('Failed to save user data:', error);
127
+ }
128
+ };
129
+
130
  // --- Firebase Configuration ---
131
  const initializeFirebase = async () => {
132
  try {
 
133
  if (typeof firebase !== 'undefined' && window.firebaseConfig) {
134
  const { initializeApp } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-app.js');
135
  const { getFirestore, collection, doc, setDoc, getDoc, getDocs, deleteDoc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
 
150
  }
151
  };
152
 
153
+ // --- Storage Management with User Isolation ---
154
  const saveSessionData = async (chatId, data) => {
155
  try {
156
  if (useFirebase && firebaseDB) {
157
  const { setDoc, doc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
158
+ await setDoc(doc(firebaseDB, 'user_sessions', `${currentUserId}_${chatId}`), {
159
  ...data,
160
+ userId: currentUserId,
161
+ chatId: chatId,
162
  updatedAt: new Date().toISOString()
163
  });
164
+ console.log('Session saved to Firebase:', chatId, 'for user:', currentUserId);
165
  } else {
166
+ // Fallback to localStorage with user isolation
167
+ const userSessions = JSON.parse(localStorage.getItem(`easyfarms_sessions_${currentUserId}`) || '{}');
168
+ userSessions[chatId] = {
169
  ...data,
170
+ userId: currentUserId,
171
  updatedAt: new Date().toISOString()
172
  };
173
+ localStorage.setItem(`easyfarms_sessions_${currentUserId}`, JSON.stringify(userSessions));
174
+ console.log('Session saved to localStorage:', chatId, 'for user:', currentUserId);
175
  }
176
  } catch (error) {
177
  console.error('Failed to save session data:', error);
178
  // Fallback to localStorage
179
+ const userSessions = JSON.parse(localStorage.getItem(`easyfarms_sessions_${currentUserId}`) || '{}');
180
+ userSessions[chatId] = data;
181
+ localStorage.setItem(`easyfarms_sessions_${currentUserId}`, JSON.stringify(userSessions));
182
  }
183
  };
184
 
 
186
  try {
187
  if (useFirebase && firebaseDB) {
188
  const { getDoc, doc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
189
+ const docRef = doc(firebaseDB, 'user_sessions', `${currentUserId}_${chatId}`);
190
  const docSnap = await getDoc(docRef);
191
  if (docSnap.exists()) {
192
  console.log('Session loaded from Firebase:', chatId);
 
194
  }
195
  } else {
196
  // Fallback to localStorage
197
+ const userSessions = JSON.parse(localStorage.getItem(`easyfarms_sessions_${currentUserId}`) || '{}');
198
+ if (userSessions[chatId]) {
199
  console.log('Session loaded from localStorage:', chatId);
200
+ return userSessions[chatId];
201
  }
202
  }
203
  return null;
204
  } catch (error) {
205
  console.error('Failed to load session data:', error);
206
  // Fallback to localStorage
207
+ const userSessions = JSON.parse(localStorage.getItem(`easyfarms_sessions_${currentUserId}`) || '{}');
208
+ return userSessions[chatId] || null;
209
  }
210
  };
211
 
212
  const loadAllSessions = async () => {
213
  try {
214
  if (useFirebase && firebaseDB) {
215
+ const { getDocs, collection, query, where } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
216
+ const q = query(collection(firebaseDB, 'user_sessions'), where('userId', '==', currentUserId));
217
+ const querySnapshot = await getDocs(q);
218
  const sessions = {};
219
  querySnapshot.forEach((doc) => {
220
+ const data = doc.data();
221
+ sessions[data.chatId] = data;
222
  });
223
+ console.log('All sessions loaded from Firebase for user:', currentUserId);
224
  return sessions;
225
  } else {
226
  // Fallback to localStorage
227
+ const sessions = JSON.parse(localStorage.getItem(`easyfarms_sessions_${currentUserId}`) || '{}');
228
+ console.log('All sessions loaded from localStorage for user:', currentUserId);
229
  return sessions;
230
  }
231
  } catch (error) {
232
  console.error('Failed to load all sessions:', error);
233
  // Fallback to localStorage
234
+ return JSON.parse(localStorage.getItem(`easyfarms_sessions_${currentUserId}`) || '{}');
235
  }
236
  };
237
 
 
239
  try {
240
  if (useFirebase && firebaseDB) {
241
  const { deleteDoc, doc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
242
+ await deleteDoc(doc(firebaseDB, 'user_sessions', `${currentUserId}_${chatId}`));
243
  console.log('Session deleted from Firebase:', chatId);
244
  } else {
245
  // Fallback to localStorage
246
+ const userSessions = JSON.parse(localStorage.getItem(`easyfarms_sessions_${currentUserId}`) || '{}');
247
+ delete userSessions[chatId];
248
+ localStorage.setItem(`easyfarms_sessions_${currentUserId}`, JSON.stringify(userSessions));
249
  console.log('Session deleted from localStorage:', chatId);
250
  }
251
  } catch (error) {
 
262
 
263
  // Initialize Firebase
264
  firebase = await initializeFirebase();
265
+
266
+ // Initialize user
267
+ await initializeUser();
268
 
269
  await fetchConfig();
270
  await loadCachedSessions();
271
  await renderChatHistoryFromAPI();
272
  setupSearchAndFilter();
273
+ setupUserStats();
274
 
275
  // Event Listeners
276
  sendBtn.addEventListener('click', sendMessage);
 
301
  }
302
  });
303
 
304
+ // Update last seen periodically
305
+ setInterval(updateLastSeen, 30000); // Every 30 seconds
306
+
307
+ console.log('EasyFarms Chat initialized with user authentication for user:', currentUserId);
308
  };
309
 
310
  const fetchConfig = async () => {
 
320
  }
321
  };
322
 
323
+ const setupUserStats = () => {
324
+ // Create user stats div
325
+ if (!userStatsDiv) {
326
+ userStatsDiv = document.createElement('div');
327
+ userStatsDiv.className = 'user-stats';
328
+ userStatsDiv.style.cssText = `
329
+ padding: 10px;
330
+ margin: 10px 0;
331
+ background: #f8f9fa;
332
+ border-radius: 4px;
333
+ font-size: 12px;
334
+ color: #666;
335
+ border-top: 1px solid #eee;
336
+ `;
337
+
338
+ // Insert at the bottom of sidebar
339
+ chatHistoryList.parentNode.appendChild(userStatsDiv);
340
+ }
341
+
342
+ updateUserStats();
343
+ };
344
+
345
+ const updateUserStats = async () => {
346
+ try {
347
+ const response = await fetch(`/users/${currentUserId}/stats`);
348
+ if (response.ok) {
349
+ const stats = await response.json();
350
+ userStatsDiv.innerHTML = `
351
+ <div><strong>Your Stats</strong></div>
352
+ <div>Chats: ${stats.total_sessions || 0}</div>
353
+ <div>Messages: ${stats.total_messages || 0}</div>
354
+ <div>Recent: ${stats.recent_sessions_24h || 0}</div>
355
+ `;
356
+ }
357
+ } catch (error) {
358
+ console.error('Failed to update user stats:', error);
359
+ // Show basic local stats
360
+ const sessions = await loadAllSessions();
361
+ const sessionCount = Object.keys(sessions).length;
362
+ userStatsDiv.innerHTML = `
363
+ <div><strong>Local Stats</strong></div>
364
+ <div>Chats: ${sessionCount}</div>
365
+ <div>User: ${currentUserId.substr(0, 12)}...</div>
366
+ `;
367
+ }
368
+ };
369
+
370
+ const updateLastSeen = async () => {
371
+ try {
372
+ await saveUserData(currentUserId, {
373
+ last_seen: new Date().toISOString()
374
+ });
375
+ } catch (error) {
376
+ console.error('Failed to update last seen:', error);
377
+ }
378
+ };
379
+
380
  const setupSearchAndFilter = () => {
381
  // Create search input if it doesn't exist
382
  if (!searchInput) {
 
393
  font-size: 14px;
394
  `;
395
 
 
396
  chatHistoryList.parentNode.insertBefore(searchInput, chatHistoryList);
397
  }
398
 
399
  // Create clear all button if it doesn't exist
400
  if (!clearAllBtn) {
401
  clearAllBtn = document.createElement('button');
402
+ clearAllBtn.textContent = 'Clear All My Chats';
403
  clearAllBtn.className = 'clear-all-btn';
404
  clearAllBtn.style.cssText = `
405
  width: 100%;
 
413
  font-size: 12px;
414
  `;
415
 
 
416
  searchInput.parentNode.insertBefore(clearAllBtn, chatHistoryList);
417
  }
418
 
 
435
  };
436
 
437
  const confirmClearAllChats = () => {
438
+ if (confirm('Are you sure you want to delete all your chats? This action cannot be undone.')) {
439
  clearAllChats();
440
  }
441
  };
442
 
443
  const clearAllChats = async () => {
444
  try {
445
+ // Delete all user chats from backend
446
+ const response = await fetch(`/users/${currentUserId}/chats`, {
447
+ method: 'DELETE'
448
+ });
449
+
450
+ if (response.ok) {
451
+ console.log('All chats deleted from backend for user:', currentUserId);
452
+ } else {
453
+ console.error('Failed to delete all chats from backend');
454
+ }
455
+
456
+ // Clear local storage
457
  const allSessions = await loadAllSessions();
458
  const chatIds = Object.keys(allSessions);
459
 
 
460
  for (const chatId of chatIds) {
 
 
 
 
 
 
 
 
 
 
 
 
 
461
  await deleteSessionData(chatId);
462
  }
463
 
 
465
  conversationsCache = {};
466
  chatHistoryList.innerHTML = '';
467
  startNewChat();
468
+ updateUserStats();
469
 
470
+ console.log('All chats cleared successfully for user:', currentUserId);
471
  } catch (error) {
472
  console.error('Failed to clear all chats:', error);
473
  }
474
  };
475
+
 
 
 
 
 
 
 
 
476
  // --- Core Chat Functions ---
477
  const sendMessage = async () => {
478
  const messageText = messageInput.value.trim();
 
497
  permanentImageUrl = await uploadImageToImgBB(selectedImageFile);
498
  }
499
 
500
+ // Prepare request data with user ID
501
  const requestData = {
502
  query: messageText,
503
  session_id: currentChatId,
504
+ user_id: currentUserId
505
  };
506
 
507
  if (permanentImageUrl) {
508
  requestData.image_url = permanentImageUrl;
509
  }
510
 
511
+ // Send to API endpoint
512
  const response = await fetch('/chat', {
513
  method: 'POST',
514
  headers: {
 
549
  data.is_new_chat
550
  );
551
 
552
+ // Update stats
553
+ updateUserStats();
554
+
555
  } catch (error) {
556
  console.error('Error sending message:', error);
557
 
 
594
  // --- Chat Management ---
595
  const startNewChat = () => {
596
  currentChatId = null;
597
+ chatMessages.innerHTML = `<div class="welcome-message"><h1>EasyFarms Assistant</h1><p>User: ${currentUserId.substr(0, 16)}...</p></div>`;
598
  chatTitle.textContent = "New Chat";
599
  updateActiveChatItem();
600
+ console.log('Started new chat for user:', currentUserId);
601
  };
602
 
603
  const switchChat = async (chatId) => {
604
+ console.log('Switching to chat:', chatId, 'for user:', currentUserId);
605
  currentChatId = chatId;
606
  chatMessages.innerHTML = '';
607
 
 
618
  });
619
 
620
  try {
621
+ const response = await fetch(`/chat/${chatId}/messages?user_id=${currentUserId}`);
622
 
623
  if (!response.ok) {
624
  throw new Error(`HTTP error! status: ${response.status}`);
 
662
 
663
  try {
664
  // Delete from backend
665
+ const response = await fetch(`/chat/${chatId}?user_id=${currentUserId}`, {
666
  method: 'DELETE'
667
  });
668
 
 
687
  }
688
 
689
  console.log('Chat deleted successfully:', chatId);
690
+ updateUserStats();
691
 
692
  } catch (error) {
693
  console.error('Error deleting chat:', error);
 
704
  conversationsCache[chatId] = {
705
  title,
706
  messages: [],
707
+ created_at: new Date().toISOString(),
708
+ user_id: currentUserId
709
  };
710
 
711
  // Add to UI
712
  const item = createChatHistoryItem(chatId, title);
713
  chatHistoryList.prepend(item);
714
 
715
+ console.log('Created new chat:', chatId, 'for user:', currentUserId);
716
  }
717
 
718
  // Create message objects with IDs
 
734
  // Add to cache
735
  conversationsCache[chatId].messages.push(userMessage, assistantMessage);
736
  conversationsCache[chatId].updated_at = new Date().toISOString();
737
+ conversationsCache[chatId].user_id = currentUserId;
738
 
739
  // Save to storage
740
  await saveSessionData(chatId, conversationsCache[chatId]);
 
742
  updateActiveChatItem();
743
  chatTitle.textContent = conversationsCache[chatId].title;
744
 
745
+ console.log('Updated cache for chat:', chatId, 'user:', currentUserId);
746
  };
747
 
748
  const createChatHistoryItem = (chatId, title) => {
 
789
  const loadCachedSessions = async () => {
790
  try {
791
  conversationsCache = await loadAllSessions();
792
+ console.log('Loaded cached sessions for user:', currentUserId, 'Count:', Object.keys(conversationsCache).length);
793
  } catch (error) {
794
  console.error('Failed to load cached sessions:', error);
795
  conversationsCache = {};
 
852
  // --- API Integration ---
853
  const renderChatHistoryFromAPI = async () => {
854
  try {
855
+ const response = await fetch(`/chats?user_id=${currentUserId}`);
856
 
857
  if (!response.ok) {
858
  throw new Error(`HTTP error! status: ${response.status}`);
859
  }
860
 
861
  const sessions = await response.json();
862
+ console.log('Loaded chat sessions from API for user:', currentUserId, sessions.length);
863
 
864
  chatHistoryList.innerHTML = '';
865
 
 
875
  conversationsCache[session.session_id].message_count = session.message_count;
876
  conversationsCache[session.session_id].created_at = session.created_at;
877
  conversationsCache[session.session_id].updated_at = session.updated_at;
878
+ conversationsCache[session.session_id].user_id = currentUserId;
879
 
880
  // Create UI item
881
  const item = createChatHistoryItem(session.session_id, session.title);
 
884
 
885
  // Save updated cache
886
  for (const [chatId, data] of Object.entries(conversationsCache)) {
887
+ if (data.user_id === currentUserId) {
888
+ await saveSessionData(chatId, data);
889
+ }
890
  }
891
 
892
  updateActiveChatItem();
893
+ updateUserStats();
894
 
895
  } catch (error) {
896
  console.error("Failed to render chat history from API:", error);
897
 
898
  // Fallback to local cache
899
+ console.log('Falling back to local cache for user:', currentUserId);
900
  for (const [chatId, data] of Object.entries(conversationsCache)) {
901
+ if (data.title && data.user_id === currentUserId) {
902
  const item = createChatHistoryItem(chatId, data.title);
903
  chatHistoryList.appendChild(item);
904
  }
 
914
 
915
  const closeSidebar = () => appContainer.classList.remove('sidebar-visible');
916
 
917
+ // --- User Account Management ---
918
+ const exportUserData = async () => {
919
+ try {
920
+ const response = await fetch(`/users/${currentUserId}/export`);
921
+ if (response.ok) {
922
+ const userData = await response.json();
923
+ const dataStr = JSON.stringify(userData, null, 2);
924
+ const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
925
+
926
+ const exportFileDefaultName = `easyfarms_data_${currentUserId.substr(0, 8)}_${new Date().toISOString().split('T')[0]}.json`;
927
+
928
+ const linkElement = document.createElement('a');
929
+ linkElement.setAttribute('href', dataUri);
930
+ linkElement.setAttribute('download', exportFileDefaultName);
931
+ linkElement.click();
932
+
933
+ console.log('User data exported successfully');
934
+ } else {
935
+ throw new Error('Export failed');
936
+ }
937
+ } catch (error) {
938
+ console.error('Failed to export user data:', error);
939
+ alert('Failed to export data. Please try again.');
940
+ }
941
+ };
942
+
943
+ const deleteUserAccount = async () => {
944
+ if (!confirm('Are you sure you want to delete your account? This will permanently delete all your chats and data.')) {
945
+ return;
946
+ }
947
+
948
+ if (!confirm('This action cannot be undone. Type "DELETE" to confirm:') ||
949
+ prompt('Type "DELETE" to confirm account deletion:') !== 'DELETE') {
950
+ return;
951
+ }
952
+
953
+ try {
954
+ const response = await fetch(`/users/${currentUserId}`, {
955
+ method: 'DELETE'
956
+ });
957
+
958
+ if (response.ok) {
959
+ // Clear all local data
960
+ localStorage.removeItem('easyfarms_user_id');
961
+ localStorage.removeItem(`easyfarms_sessions_${currentUserId}`);
962
+ localStorage.removeItem(`easyfarms_users`);
963
+
964
+ // Clear Firebase data if applicable
965
+ if (useFirebase && firebaseDB) {
966
+ try {
967
+ const { deleteDoc, doc, collection, getDocs, query, where } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
968
+
969
+ // Delete user document
970
+ await deleteDoc(doc(firebaseDB, 'users', currentUserId));
971
+
972
+ // Delete user sessions
973
+ const q = query(collection(firebaseDB, 'user_sessions'), where('userId', '==', currentUserId));
974
+ const querySnapshot = await getDocs(q);
975
+ querySnapshot.forEach(async (doc) => {
976
+ await deleteDoc(doc.ref);
977
+ });
978
+
979
+ } catch (firebaseError) {
980
+ console.error('Failed to clean Firebase data:', firebaseError);
981
+ }
982
+ }
983
+
984
+ alert('Account deleted successfully. Refreshing page...');
985
+ location.reload();
986
+
987
+ } else {
988
+ throw new Error('Account deletion failed');
989
+ }
990
+
991
+ } catch (error) {
992
+ console.error('Failed to delete account:', error);
993
+ alert('Failed to delete account. Please try again.');
994
+ }
995
+ };
996
+
997
+ const createUserMenu = () => {
998
+ // Create user menu button
999
+ const userMenuBtn = document.createElement('button');
1000
+ userMenuBtn.innerHTML = '⚙️';
1001
+ userMenuBtn.title = 'User Settings';
1002
+ userMenuBtn.style.cssText = `
1003
+ position: fixed;
1004
+ top: 10px;
1005
+ right: 10px;
1006
+ background: #007bff;
1007
+ color: white;
1008
+ border: none;
1009
+ border-radius: 50%;
1010
+ width: 40px;
1011
+ height: 40px;
1012
+ cursor: pointer;
1013
+ font-size: 16px;
1014
+ z-index: 1000;
1015
+ `;
1016
+
1017
+ // Create user menu dropdown
1018
+ const userMenu = document.createElement('div');
1019
+ userMenu.style.cssText = `
1020
+ position: fixed;
1021
+ top: 55px;
1022
+ right: 10px;
1023
+ background: white;
1024
+ border: 1px solid #ddd;
1025
+ border-radius: 4px;
1026
+ padding: 10px;
1027
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
1028
+ display: none;
1029
+ z-index: 1001;
1030
+ min-width: 200px;
1031
+ `;
1032
+
1033
+ userMenu.innerHTML = `
1034
+ <div style="margin-bottom: 10px; font-weight: bold;">User Settings</div>
1035
+ <div style="margin-bottom: 5px; font-size: 12px; color: #666;">ID: ${currentUserId.substr(0, 16)}...</div>
1036
+ <button id="export-data-btn" style="width: 100%; margin: 5px 0; padding: 5px; background: #28a745; color: white; border: none; border-radius: 3px; cursor: pointer;">Export My Data</button>
1037
+ <button id="delete-account-btn" style="width: 100%; margin: 5px 0; padding: 5px; background: #dc3545; color: white; border: none; border-radius: 3px; cursor: pointer;">Delete Account</button>
1038
+ `;
1039
+
1040
+ document.body.appendChild(userMenuBtn);
1041
+ document.body.appendChild(userMenu);
1042
+
1043
+ // Toggle menu
1044
+ userMenuBtn.addEventListener('click', (e) => {
1045
+ e.stopPropagation();
1046
+ userMenu.style.display = userMenu.style.display === 'none' ? 'block' : 'none';
1047
+ });
1048
+
1049
+ // Close menu when clicking outside
1050
+ document.addEventListener('click', () => {
1051
+ userMenu.style.display = 'none';
1052
+ });
1053
+
1054
+ // Menu actions
1055
+ document.getElementById('export-data-btn').addEventListener('click', exportUserData);
1056
+ document.getElementById('delete-account-btn').addEventListener('click', deleteUserAccount);
1057
+ };
1058
+
1059
+ // --- Enhanced Error Handling ---
1060
+ window.addEventListener('unhandledrejection', (event) => {
1061
+ console.error('Unhandled promise rejection:', event.reason);
1062
+ // Don't show error to user for now, just log it
1063
+ });
1064
+
1065
+ window.addEventListener('error', (event) => {
1066
+ console.error('Global error:', event.error);
1067
+ // Don't show error to user for now, just log it
1068
+ });
1069
+
1070
+ // --- Session Recovery ---
1071
+ const attemptSessionRecovery = async () => {
1072
+ try {
1073
+ // Try to recover any unsaved messages
1074
+ const unsavedMessages = localStorage.getItem(`unsaved_messages_${currentUserId}`);
1075
+ if (unsavedMessages) {
1076
+ const messages = JSON.parse(unsavedMessages);
1077
+ console.log('Found unsaved messages, attempting recovery:', messages);
1078
+
1079
+ // Display recovered messages
1080
+ messages.forEach(msg => {
1081
+ displayMessage({
1082
+ role: msg.role,
1083
+ content: `[RECOVERED] ${msg.content}`,
1084
+ isError: true
1085
+ });
1086
+ });
1087
+
1088
+ // Clear unsaved messages
1089
+ localStorage.removeItem(`unsaved_messages_${currentUserId}`);
1090
+ }
1091
+ } catch (error) {
1092
+ console.error('Session recovery failed:', error);
1093
+ }
1094
+ };
1095
+
1096
+ const saveUnsavedMessage = (role, content) => {
1097
+ try {
1098
+ const unsavedMessages = JSON.parse(localStorage.getItem(`unsaved_messages_${currentUserId}`) || '[]');
1099
+ unsavedMessages.push({
1100
+ role,
1101
+ content,
1102
+ timestamp: new Date().toISOString()
1103
+ });
1104
+
1105
+ // Keep only last 5 unsaved messages
1106
+ if (unsavedMessages.length > 5) {
1107
+ unsavedMessages.splice(0, unsavedMessages.length - 5);
1108
+ }
1109
+
1110
+ localStorage.setItem(`unsaved_messages_${currentUserId}`, JSON.stringify(unsavedMessages));
1111
+ } catch (error) {
1112
+ console.error('Failed to save unsaved message:', error);
1113
+ }
1114
+ };
1115
+
1116
+ // --- Performance Monitoring ---
1117
+ const logPerformance = () => {
1118
+ if (performance && performance.timing) {
1119
+ const perfData = performance.timing;
1120
+ const loadTime = perfData.loadEventEnd - perfData.navigationStart;
1121
+ console.log(`Page load time: ${loadTime}ms`);
1122
+
1123
+ // Log to backend if needed
1124
+ // fetch('/analytics/performance', {
1125
+ // method: 'POST',
1126
+ // body: JSON.stringify({ user_id: currentUserId, load_time: loadTime })
1127
+ // });
1128
+ }
1129
+ };
1130
+
1131
+ // --- Initialize Everything ---
1132
+ // Add user menu after initialization
1133
+ const setupUserInterface = () => {
1134
+ createUserMenu();
1135
+ attemptSessionRecovery();
1136
+ logPerformance();
1137
+ };
1138
+
1139
+ // Start the Application
1140
+ init().then(() => {
1141
+ setupUserInterface();
1142
+ console.log('EasyFarms Chat fully initialized for user:', currentUserId);
1143
+ }).catch((error) => {
1144
+ console.error('Failed to initialize application:', error);
1145
+ document.body.innerHTML = `
1146
+ <div style="padding: 20px; text-align: center;">
1147
+ <h2>Failed to Load EasyFarms Chat</h2>
1148
+ <p>Please refresh the page and try again.</p>
1149
+ <p style="color: #666; font-size: 12px;">Error: ${error.message}</p>
1150
+ </div>
1151
+ `;
1152
+ });
1153
+
1154
  });