higher5fh commited on
Commit
84ab9eb
·
verified ·
1 Parent(s): 0626e47

Update frontend/index.html

Browse files
Files changed (1) hide show
  1. frontend/index.html +775 -0
frontend/index.html CHANGED
@@ -0,0 +1,775 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Irish Legal AI Assistant</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
+ <style>
9
+ :root {
10
+ --primary: #1a365d;
11
+ --secondary: #2c5282;
12
+ --accent: #e53e3e;
13
+ --light: #f7fafc;
14
+ --dark: #2d3748;
15
+ --success: #38a169;
16
+ --warning: #dd6b20;
17
+ --danger: #e53e3e;
18
+ --gray: #a0aec0;
19
+ --bg-light: #edf2f7;
20
+ --border: #e2e8f0;
21
+ }
22
+
23
+ * {
24
+ margin: 0;
25
+ padding: 0;
26
+ box-sizing: border-box;
27
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
28
+ }
29
+
30
+ body {
31
+ background-color: var(--bg-light);
32
+ color: var(--dark);
33
+ min-height: 100vh;
34
+ display: flex;
35
+ flex-direction: column;
36
+ }
37
+
38
+ .container {
39
+ max-width: 1200px;
40
+ margin: 0 auto;
41
+ padding: 0 20px;
42
+ width: 100%;
43
+ }
44
+
45
+ /* Header Styles */
46
+ header {
47
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
48
+ color: white;
49
+ padding: 1rem 0;
50
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
51
+ position: sticky;
52
+ top: 0;
53
+ z-index: 100;
54
+ }
55
+
56
+ .header-content {
57
+ display: flex;
58
+ justify-content: space-between;
59
+ align-items: center;
60
+ }
61
+
62
+ .logo {
63
+ display: flex;
64
+ align-items: center;
65
+ gap: 10px;
66
+ }
67
+
68
+ .logo i {
69
+ font-size: 1.8rem;
70
+ color: #fff;
71
+ }
72
+
73
+ .logo h1 {
74
+ font-size: 1.5rem;
75
+ font-weight: 600;
76
+ }
77
+
78
+ .session-info {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 15px;
82
+ font-size: 0.9rem;
83
+ }
84
+
85
+ .security-badge {
86
+ background: rgba(255, 255, 255, 0.2);
87
+ padding: 5px 10px;
88
+ border-radius: 20px;
89
+ display: flex;
90
+ align-items: center;
91
+ gap: 5px;
92
+ }
93
+
94
+ /* Main Layout */
95
+ .main-layout {
96
+ display: flex;
97
+ flex: 1;
98
+ gap: 20px;
99
+ padding: 20px 0;
100
+ }
101
+
102
+ /* Chat Container */
103
+ .chat-container {
104
+ flex: 1;
105
+ background: white;
106
+ border-radius: 10px;
107
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
108
+ display: flex;
109
+ flex-direction: column;
110
+ overflow: hidden;
111
+ }
112
+
113
+ .chat-header {
114
+ padding: 15px 20px;
115
+ border-bottom: 1px solid var(--border);
116
+ display: flex;
117
+ justify-content: space-between;
118
+ align-items: center;
119
+ }
120
+
121
+ .chat-header h2 {
122
+ font-size: 1.2rem;
123
+ color: var(--primary);
124
+ }
125
+
126
+ .status-indicator {
127
+ display: flex;
128
+ align-items: center;
129
+ gap: 5px;
130
+ font-size: 0.9rem;
131
+ color: var(--success);
132
+ }
133
+
134
+ .status-dot {
135
+ width: 10px;
136
+ height: 10px;
137
+ background: var(--success);
138
+ border-radius: 50%;
139
+ }
140
+
141
+ .chat-history {
142
+ flex: 1;
143
+ padding: 20px;
144
+ overflow-y: auto;
145
+ display: flex;
146
+ flex-direction: column;
147
+ gap: 20px;
148
+ background-color: #fafafa;
149
+ }
150
+
151
+ .message {
152
+ max-width: 80%;
153
+ padding: 15px;
154
+ border-radius: 10px;
155
+ position: relative;
156
+ animation: fadeIn 0.3s ease-out;
157
+ }
158
+
159
+ @keyframes fadeIn {
160
+ from { opacity: 0; transform: translateY(10px); }
161
+ to { opacity: 1; transform: translateY(0); }
162
+ }
163
+
164
+ .user-message {
165
+ background: var(--primary);
166
+ color: white;
167
+ align-self: flex-end;
168
+ border-bottom-right-radius: 0;
169
+ }
170
+
171
+ .ai-message {
172
+ background: white;
173
+ border: 1px solid var(--border);
174
+ align-self: flex-start;
175
+ border-bottom-left-radius: 0;
176
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
177
+ }
178
+
179
+ .message-header {
180
+ display: flex;
181
+ justify-content: space-between;
182
+ margin-bottom: 8px;
183
+ font-size: 0.8rem;
184
+ opacity: 0.8;
185
+ }
186
+
187
+ .ai-message .message-header {
188
+ color: var(--primary);
189
+ }
190
+
191
+ .user-message .message-header {
192
+ color: rgba(255, 255, 255, 0.8);
193
+ }
194
+
195
+ .message-content {
196
+ line-height: 1.6;
197
+ }
198
+
199
+ .message-content p {
200
+ margin-bottom: 10px;
201
+ }
202
+
203
+ .sources-container {
204
+ margin-top: 15px;
205
+ padding-top: 15px;
206
+ border-top: 1px dashed var(--border);
207
+ }
208
+
209
+ .sources-title {
210
+ font-weight: 600;
211
+ margin-bottom: 8px;
212
+ color: var(--primary);
213
+ }
214
+
215
+ .source-item {
216
+ padding: 8px;
217
+ background: rgba(56, 178, 172, 0.1);
218
+ border-radius: 5px;
219
+ margin-bottom: 5px;
220
+ font-size: 0.85rem;
221
+ }
222
+
223
+ /* Input Area */
224
+ .input-container {
225
+ padding: 15px 20px;
226
+ border-top: 1px solid var(--border);
227
+ background: white;
228
+ }
229
+
230
+ .input-area {
231
+ display: flex;
232
+ gap: 10px;
233
+ }
234
+
235
+ textarea {
236
+ flex: 1;
237
+ padding: 12px 15px;
238
+ border: 1px solid var(--border);
239
+ border-radius: 8px;
240
+ resize: none;
241
+ font-size: 1rem;
242
+ height: 60px;
243
+ transition: border-color 0.2s;
244
+ }
245
+
246
+ textarea:focus {
247
+ outline: none;
248
+ border-color: var(--primary);
249
+ box-shadow: 0 0 0 2px rgba(26, 54, 93, 0.1);
250
+ }
251
+
252
+ button {
253
+ background: var(--primary);
254
+ color: white;
255
+ border: none;
256
+ border-radius: 8px;
257
+ padding: 0 25px;
258
+ font-weight: 600;
259
+ cursor: pointer;
260
+ transition: background 0.2s;
261
+ display: flex;
262
+ align-items: center;
263
+ justify-content: center;
264
+ gap: 8px;
265
+ }
266
+
267
+ button:hover {
268
+ background: var(--secondary);
269
+ }
270
+
271
+ button:disabled {
272
+ background: var(--gray);
273
+ cursor: not-allowed;
274
+ }
275
+
276
+ /* Sidebar */
277
+ .sidebar {
278
+ width: 300px;
279
+ background: white;
280
+ border-radius: 10px;
281
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
282
+ padding: 20px;
283
+ display: flex;
284
+ flex-direction: column;
285
+ gap: 20px;
286
+ }
287
+
288
+ .card {
289
+ background: white;
290
+ border-radius: 8px;
291
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
292
+ overflow: hidden;
293
+ }
294
+
295
+ .card-header {
296
+ background: var(--primary);
297
+ color: white;
298
+ padding: 12px 15px;
299
+ font-weight: 600;
300
+ }
301
+
302
+ .card-body {
303
+ padding: 15px;
304
+ }
305
+
306
+ .history-item {
307
+ padding: 10px 0;
308
+ border-bottom: 1px solid var(--border);
309
+ cursor: pointer;
310
+ transition: background 0.2s;
311
+ }
312
+
313
+ .history-item:last-child {
314
+ border-bottom: none;
315
+ }
316
+
317
+ .history-item:hover {
318
+ background: var(--bg-light);
319
+ }
320
+
321
+ .history-question {
322
+ font-weight: 500;
323
+ margin-bottom: 5px;
324
+ display: -webkit-box;
325
+ -webkit-line-clamp: 2;
326
+ -webkit-box-orient: vertical;
327
+ overflow: hidden;
328
+ }
329
+
330
+ .history-date {
331
+ font-size: 0.8rem;
332
+ color: var(--gray);
333
+ }
334
+
335
+ /* Typing indicator */
336
+ .typing-indicator {
337
+ display: flex;
338
+ align-items: center;
339
+ gap: 5px;
340
+ padding: 15px;
341
+ background: white;
342
+ border: 1px solid var(--border);
343
+ border-radius: 10px;
344
+ align-self: flex-start;
345
+ max-width: 80px;
346
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
347
+ }
348
+
349
+ .typing-dot {
350
+ width: 8px;
351
+ height: 8px;
352
+ background: var(--gray);
353
+ border-radius: 50%;
354
+ animation: typing 1.4s infinite ease-in-out;
355
+ }
356
+
357
+ .typing-dot:nth-child(1) {
358
+ animation-delay: 0s;
359
+ }
360
+
361
+ .typing-dot:nth-child(2) {
362
+ animation-delay: 0.2s;
363
+ }
364
+
365
+ .typing-dot:nth-child(3) {
366
+ animation-delay: 0.4s;
367
+ }
368
+
369
+ @keyframes typing {
370
+ 0%, 60%, 100% { transform: translateY(0); }
371
+ 30% { transform: translateY(-5px); }
372
+ }
373
+
374
+ /* Footer */
375
+ footer {
376
+ background: var(--dark);
377
+ color: white;
378
+ padding: 20px 0;
379
+ margin-top: auto;
380
+ }
381
+
382
+ .footer-content {
383
+ display: flex;
384
+ justify-content: space-between;
385
+ align-items: center;
386
+ }
387
+
388
+ .disclaimer {
389
+ font-size: 0.85rem;
390
+ opacity: 0.8;
391
+ max-width: 600px;
392
+ }
393
+
394
+ /* Responsive Design */
395
+ @media (max-width: 900px) {
396
+ .main-layout {
397
+ flex-direction: column;
398
+ }
399
+
400
+ .sidebar {
401
+ width: 100%;
402
+ }
403
+
404
+ .message {
405
+ max-width: 90%;
406
+ }
407
+ }
408
+
409
+ @media (max-width: 600px) {
410
+ .header-content {
411
+ flex-direction: column;
412
+ gap: 10px;
413
+ align-items: flex-start;
414
+ }
415
+
416
+ .footer-content {
417
+ flex-direction: column;
418
+ gap: 15px;
419
+ align-items: flex-start;
420
+ }
421
+
422
+ .input-area {
423
+ flex-direction: column;
424
+ }
425
+
426
+ button {
427
+ padding: 12px;
428
+ }
429
+ }
430
+ </style>
431
+ </head>
432
+ <body>
433
+ <!-- Header -->
434
+ <header>
435
+ <div class="container header-content">
436
+ <div class="logo">
437
+ <i class="fas fa-balance-scale"></i>
438
+ <h1>Irish Legal AI Assistant</h1>
439
+ </div>
440
+ <div class="session-info">
441
+ <div class="security-badge">
442
+ <i class="fas fa-lock"></i>
443
+ <span>Secure Session</span>
444
+ </div>
445
+ <div id="sessionTimer">Session Active</div>
446
+ </div>
447
+ </div>
448
+ </header>
449
+
450
+ <div class="container">
451
+ <div class="main-layout">
452
+ <!-- Chat Interface -->
453
+ <div class="chat-container">
454
+ <div class="chat-header">
455
+ <h2><i class="fas fa-comments"></i> Legal Consultation</h2>
456
+ <div class="status-indicator">
457
+ <div class="status-dot"></div>
458
+ <span id="connectionStatus">Connected</span>
459
+ </div>
460
+ </div>
461
+
462
+ <div class="chat-history" id="chatHistory">
463
+ <div class="message ai-message">
464
+ <div class="message-header">
465
+ <span><i class="fas fa-robot"></i> Legal Assistant</span>
466
+ <span>Just now</span>
467
+ </div>
468
+ <div class="message-content">
469
+ <p>Welcome to the Irish Legal AI Assistant! I'm here to help with your legal questions regarding Irish law.</p>
470
+ <p>Please ask your question in the box below. I'll provide a concise answer with relevant legal sources.</p>
471
+ </div>
472
+ </div>
473
+ </div>
474
+
475
+ <div class="input-container">
476
+ <div class="input-area">
477
+ <textarea id="userInput" placeholder="Ask a question about Irish law..." required></textarea>
478
+ <button id="sendButton">
479
+ <i class="fas fa-paper-plane"></i> Send
480
+ </button>
481
+ </div>
482
+ </div>
483
+ </div>
484
+
485
+ <!-- Sidebar -->
486
+ <div class="sidebar">
487
+ <div class="card">
488
+ <div class="card-header">
489
+ <i class="fas fa-history"></i> Session History
490
+ </div>
491
+ <div class="card-body" id="historyList">
492
+ <!-- History items will be added dynamically -->
493
+ </div>
494
+ </div>
495
+
496
+ <div class="card">
497
+ <div class="card-header">
498
+ <i class="fas fa-lightbulb"></i> Tips for Better Results
499
+ </div>
500
+ <div class="card-body">
501
+ <ul style="padding-left: 20px; display: flex; flex-direction: column; gap: 10px;">
502
+ <li>Be specific with your questions</li>
503
+ <li>Include relevant context when possible</li>
504
+ <li>Ask about recent legal changes</li>
505
+ <li>Request practical implications</li>
506
+ <li>Specify areas of law (employment, property, etc.)</li>
507
+ </ul>
508
+ </div>
509
+ </div>
510
+ </div>
511
+ </div>
512
+ </div>
513
+
514
+ <!-- Footer -->
515
+ <footer>
516
+ <div class="container footer-content">
517
+ <div class="disclaimer">
518
+ <p><strong>Disclaimer:</strong> This AI assistant provides general legal information for educational purposes only and does not constitute legal advice. For personal legal matters, consult a qualified solicitor. The developers accept no liability for actions taken based on this information.</p>
519
+ </div>
520
+ </div>
521
+ </footer>
522
+
523
+ <script>
524
+ document.addEventListener('DOMContentLoaded', function() {
525
+ const chatHistory = document.getElementById('chatHistory');
526
+ const userInput = document.getElementById('userInput');
527
+ const sendButton = document.getElementById('sendButton');
528
+ const historyList = document.getElementById('historyList');
529
+ const sessionTimerElement = document.getElementById('sessionTimer');
530
+
531
+ // Session management
532
+ let sessionId = null;
533
+ let sessionHistory = [];
534
+ let sessionCheckInterval;
535
+ const SESSION_CHECK_INTERVAL = 5000; // Check every 5 seconds
536
+
537
+ // Check for existing session cookie
538
+ function getCookie(name) {
539
+ const value = `; ${document.cookie}`;
540
+ const parts = value.split(`; ${name}=`);
541
+ if (parts.length === 2) return parts.pop().split(';').shift();
542
+ }
543
+
544
+ // Initialize session
545
+ function initializeSession() {
546
+ // Start session status checker
547
+ startSessionStatusChecker();
548
+ }
549
+
550
+ // Start session status checking
551
+ function startSessionStatusChecker() {
552
+ // Check immediately
553
+ checkSessionStatus();
554
+
555
+ // Set up periodic checking
556
+ sessionCheckInterval = setInterval(checkSessionStatus, SESSION_CHECK_INTERVAL);
557
+ }
558
+
559
+ // Check session status with backend
560
+ async function checkSessionStatus() {
561
+ try {
562
+ const response = await fetch('/session/status', {
563
+ method: 'GET',
564
+ credentials: 'include'
565
+ });
566
+
567
+ if (!response.ok) throw new Error('Status check failed');
568
+ const data = await response.json();
569
+
570
+ updateSessionDisplay(data);
571
+
572
+ if (data.status === 'expired') {
573
+ handleSessionExpiry();
574
+ }
575
+ } catch (error) {
576
+ console.error('Session status check error:', error);
577
+ sessionTimerElement.textContent = "Connection Error";
578
+ }
579
+ }
580
+
581
+ // Update session timer display
582
+ function updateSessionDisplay(sessionData) {
583
+ switch (sessionData.status) {
584
+ case 'new':
585
+ sessionTimerElement.textContent = "New Session";
586
+ sessionId = null;
587
+ sessionHistory = [];
588
+ updateSessionHistory([]);
589
+ break;
590
+
591
+ case 'expired':
592
+ sessionTimerElement.textContent = "Session Expired";
593
+ sessionId = null;
594
+ sessionHistory = [];
595
+ updateSessionHistory([]);
596
+ break;
597
+
598
+ case 'active':
599
+ sessionId = sessionData.session_id;
600
+ const minutes = Math.floor(sessionData.ttl / 60);
601
+ const seconds = sessionData.ttl % 60;
602
+ sessionTimerElement.textContent = `Expires in ${minutes}:${seconds.toString().padStart(2, '0')}`;
603
+
604
+ // Only update history if we detect a change
605
+ if (sessionHistory.length !== sessionData.history_count) {
606
+ // We don't have full history data, so we'll keep our local copy
607
+ // But we can update the count display
608
+ }
609
+ break;
610
+ }
611
+ }
612
+
613
+ // Handle session expiry
614
+ function handleSessionExpiry() {
615
+ // Clear session data
616
+ sessionId = null;
617
+ sessionHistory = [];
618
+
619
+ // Remove expired session cookie
620
+ document.cookie = 'session_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
621
+
622
+ // Clear chat except initial message
623
+ while (chatHistory.children.length > 1) {
624
+ chatHistory.removeChild(chatHistory.lastChild);
625
+ }
626
+
627
+ // Add expiry notification
628
+ addMessage("Your session has expired due to a timeout. A new session will be created with your next question. If session creation fails, you can still try again.", 'ai');
629
+
630
+ // Update history display
631
+ updateSessionHistory([]);
632
+ }
633
+
634
+ // Update session history display
635
+ function updateSessionHistory(history) {
636
+ sessionHistory = history;
637
+ historyList.innerHTML = '';
638
+
639
+ if (history.length === 0) {
640
+ historyList.innerHTML = '<p style="color: var(--gray); text-align: center;">No history yet</p>';
641
+ return;
642
+ }
643
+
644
+ history.forEach(item => {
645
+ const historyItem = document.createElement('div');
646
+ historyItem.className = 'history-item';
647
+ historyItem.innerHTML = `
648
+ <div class="history-question">${item.q}</div>
649
+ <div class="history-date">${new Date(item.timestamp).toLocaleString()}</div>
650
+ `;
651
+ historyItem.addEventListener('click', () => {
652
+ // Scroll to the corresponding message in chat
653
+ const messages = document.querySelectorAll('.message');
654
+ for (let msg of messages) {
655
+ if (msg.textContent.includes(item.q)) {
656
+ msg.scrollIntoView({ behavior: 'smooth' });
657
+ break;
658
+ }
659
+ }
660
+ });
661
+ historyList.appendChild(historyItem);
662
+ });
663
+ }
664
+
665
+ // Handle sending messages
666
+ sendButton.addEventListener('click', sendMessage);
667
+ userInput.addEventListener('keypress', function(e) {
668
+ if (e.key === 'Enter' && !e.shiftKey) {
669
+ e.preventDefault();
670
+ sendMessage();
671
+ }
672
+ });
673
+
674
+ async function sendMessage() {
675
+ const question = userInput.value.trim();
676
+ if (!question) return;
677
+
678
+ // Add user message to chat
679
+ addMessage(question, 'user');
680
+ userInput.value = '';
681
+ sendButton.disabled = true;
682
+
683
+ // Show typing indicator
684
+ const typingIndicator = document.createElement('div');
685
+ typingIndicator.className = 'typing-indicator';
686
+ typingIndicator.innerHTML = `
687
+ <div class="typing-dot"></div>
688
+ <div class="typing-dot"></div>
689
+ <div class="typing-dot"></div>
690
+ `;
691
+ chatHistory.appendChild(typingIndicator);
692
+ chatHistory.scrollTop = chatHistory.scrollHeight;
693
+
694
+ try {
695
+ // Send query to backend
696
+ const response = await fetch('/query', {
697
+ method: 'POST',
698
+ headers: {
699
+ 'Content-Type': 'application/json',
700
+ },
701
+ body: JSON.stringify({ query: question }),
702
+ credentials: 'include'
703
+ });
704
+
705
+ if (!response.ok) {
706
+ throw new Error(`HTTP error! status: ${response.status}`);
707
+ }
708
+
709
+ const data = await response.json();
710
+
711
+ // Remove typing indicator
712
+ chatHistory.removeChild(typingIndicator);
713
+
714
+ // Add AI response
715
+ addMessage(data.answer, 'ai', data.sources);
716
+
717
+ // Update session info
718
+ if (data.session_id) {
719
+ sessionId = data.session_id;
720
+ sessionHistory.push({
721
+ q: question,
722
+ a: data.answer,
723
+ timestamp: new Date().toISOString()
724
+ });
725
+ updateSessionHistory(sessionHistory);
726
+ }
727
+
728
+ } catch (error) {
729
+ console.error('Error sending message:', error);
730
+ chatHistory.removeChild(typingIndicator);
731
+ addMessage("Sorry, there was an error processing your request. Please try again.", 'ai');
732
+ } finally {
733
+ sendButton.disabled = false;
734
+ }
735
+ }
736
+
737
+ function addMessage(content, sender, sources = []) {
738
+ const messageDiv = document.createElement('div');
739
+ messageDiv.className = `message ${sender}-message`;
740
+
741
+ const now = new Date();
742
+ const timeString = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
743
+
744
+ messageDiv.innerHTML = `
745
+ <div class="message-header">
746
+ <span><i class="${sender === 'ai' ? 'fas fa-robot' : 'fas fa-user'}"></i> ${sender === 'ai' ? 'Legal Assistant' : 'You'}</span>
747
+ <span>${timeString}</span>
748
+ </div>
749
+ <div class="message-content">
750
+ ${formatMessageContent(content)}
751
+ ${sources && sources.length ? `
752
+ <div class="sources-container">
753
+ <div class="sources-title"><i class="fas fa-book"></i> Legal Sources:</div>
754
+ ${sources.map(source => `<div class="source-item">${source}</div>`).join('')}
755
+ </div>
756
+ ` : ''}
757
+ </div>
758
+ `;
759
+
760
+ chatHistory.appendChild(messageDiv);
761
+ chatHistory.scrollTop = chatHistory.scrollHeight;
762
+ }
763
+
764
+ function formatMessageContent(content) {
765
+ // Convert line breaks to paragraphs
766
+ const paragraphs = content.split('\n\n');
767
+ return paragraphs.map(p => `<p>${p}</p>`).join('');
768
+ }
769
+
770
+ // Initialize the session
771
+ initializeSession();
772
+ });
773
+ </script>
774
+ </body>
775
+ </html>