Ultronprime commited on
Commit
16ef808
·
verified ·
1 Parent(s): 401bd3d

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +946 -19
  3. prompts.txt +1 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Conv
3
- emoji:
4
- colorFrom: yellow
5
- colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: conv
3
+ emoji: 🐳
4
+ colorFrom: gray
5
+ colorTo: green
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,946 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Conversation Analysis Dashboard</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ @keyframes fadeIn {
11
+ from { opacity: 0; transform: translateY(10px); }
12
+ to { opacity: 1; transform: translateY(0); }
13
+ }
14
+ .fade-in {
15
+ animation: fadeIn 0.3s ease-out forwards;
16
+ }
17
+ .sidebar {
18
+ transition: transform 0.3s ease-in-out;
19
+ }
20
+ .sidebar-open {
21
+ transform: translateX(0);
22
+ }
23
+ @media (max-width: 768px) {
24
+ .sidebar {
25
+ transform: translateX(-100%);
26
+ position: fixed;
27
+ top: 0;
28
+ left: 0;
29
+ z-index: 40;
30
+ height: 100vh;
31
+ }
32
+ .sidebar-open {
33
+ transform: translateX(0);
34
+ }
35
+ }
36
+ .conversation-card:hover {
37
+ transform: translateY(-2px);
38
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
39
+ }
40
+ .progress-bar {
41
+ transition: width 0.5s ease;
42
+ }
43
+ .modal-overlay {
44
+ background-color: rgba(0, 0, 0, 0.5);
45
+ }
46
+ .modal-content {
47
+ max-height: 90vh;
48
+ }
49
+ .line-clamp-2 {
50
+ display: -webkit-box;
51
+ -webkit-line-clamp: 2;
52
+ -webkit-box-orient: vertical;
53
+ overflow: hidden;
54
+ }
55
+ </style>
56
+ </head>
57
+ <body class="bg-gray-50 font-sans antialiased">
58
+ <!-- Mobile menu button -->
59
+ <div class="md:hidden fixed top-4 left-4 z-50">
60
+ <button id="mobileMenuBtn" class="p-2 rounded-md bg-white shadow-md text-gray-700">
61
+ <i class="fas fa-bars"></i>
62
+ </button>
63
+ </div>
64
+
65
+ <!-- Sidebar -->
66
+ <div id="sidebar" class="sidebar w-64 bg-white border-r border-gray-200 fixed h-full overflow-y-auto">
67
+ <div class="p-4 border-b border-gray-200">
68
+ <div class="flex items-center space-x-2">
69
+ <div class="bg-purple-600 text-white p-2 rounded-lg">
70
+ <i class="fas fa-comments text-xl"></i>
71
+ </div>
72
+ <h1 class="text-xl font-bold text-gray-800">ConvoAnalyzer</h1>
73
+ </div>
74
+ <p class="text-xs text-gray-500 mt-1">Conversation Analysis Dashboard</p>
75
+ </div>
76
+
77
+ <div class="p-4 space-y-6">
78
+ <div>
79
+ <h3 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Configuration</h3>
80
+ <div class="space-y-4">
81
+ <div>
82
+ <label class="block text-sm font-medium text-gray-700 mb-1">Worker Count</label>
83
+ <div class="flex items-center space-x-2">
84
+ <input id="workerCount" type="range" min="1" max="8" value="4" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
85
+ <span id="workerCountDisplay" class="text-sm font-medium text-gray-700 w-8 text-center">4</span>
86
+ </div>
87
+ </div>
88
+
89
+ <div>
90
+ <label class="block text-sm font-medium text-gray-700 mb-1">Analysis Model</label>
91
+ <select id="modelSelect" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm rounded-md">
92
+ <option value="default">Default Model</option>
93
+ <option value="advanced">Advanced Model</option>
94
+ <option value="custom">Custom Model</option>
95
+ </select>
96
+ </div>
97
+ </div>
98
+ </div>
99
+
100
+ <div>
101
+ <h3 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Actions</h3>
102
+ <div class="space-y-2">
103
+ <button id="uploadBtn" class="w-full flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500">
104
+ <i class="fas fa-upload mr-2"></i> Upload Conversations
105
+ </button>
106
+ <button id="startAnalysisBtn" class="w-full flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-purple-700 bg-white hover:bg-purple-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500">
107
+ <i class="fas fa-play mr-2"></i> Start Analysis
108
+ </button>
109
+ <div class="flex space-x-2">
110
+ <button id="pauseBtn" disabled class="flex-1 flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-gray-700 bg-gray-200 opacity-50 cursor-not-allowed">
111
+ <i class="fas fa-pause mr-2"></i> Pause
112
+ </button>
113
+ <button id="stopBtn" disabled class="flex-1 flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 opacity-50 cursor-not-allowed hover:bg-red-700">
114
+ <i class="fas fa-stop mr-2"></i> Stop
115
+ </button>
116
+ </div>
117
+ <button id="downloadBtn" class="w-full flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
118
+ <i class="fas fa-download mr-2"></i> Download Results
119
+ </button>
120
+ </div>
121
+ </div>
122
+
123
+ <div>
124
+ <h3 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Agent Status</h3>
125
+ <div class="space-y-2">
126
+ <div class="flex items-center justify-between p-2 bg-gray-50 rounded-md">
127
+ <div class="flex items-center space-x-2">
128
+ <i class="fas fa-smile text-blue-500"></i>
129
+ <span class="text-sm font-medium text-gray-700">Sentiment</span>
130
+ </div>
131
+ <span id="sentimentStatus" class="text-sm font-medium text-gray-600">Idle</span>
132
+ </div>
133
+ <div class="flex items-center justify-between p-2 bg-gray-50 rounded-md">
134
+ <div class="flex items-center space-x-2">
135
+ <i class="fas fa-tags text-blue-500"></i>
136
+ <span class="text-sm font-medium text-gray-700">Topic</span>
137
+ </div>
138
+ <span id="topicStatus" class="text-sm font-medium text-gray-600">Idle</span>
139
+ </div>
140
+ <div class="flex items-center justify-between p-2 bg-gray-50 rounded-md">
141
+ <div class="flex items-center space-x-2">
142
+ <i class="fas fa-tasks text-blue-500"></i>
143
+ <span class="text-sm font-medium text-gray-700">Progress</span>
144
+ </div>
145
+ <span id="progressStatus" class="text-sm font-medium text-gray-600">Idle</span>
146
+ </div>
147
+ <div class="flex items-center justify-between p-2 bg-gray-50 rounded-md">
148
+ <div class="flex items-center space-x-2">
149
+ <i class="fas fa-file-alt text-blue-500"></i>
150
+ <span class="text-sm font-medium text-gray-700">Summary</span>
151
+ </div>
152
+ <span id="summaryStatus" class="text-sm font-medium text-gray-600">Idle</span>
153
+ </div>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </div>
158
+
159
+ <!-- Main content -->
160
+ <div class="md:ml-64">
161
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
162
+ <!-- Header -->
163
+ <div class="pt-6 pb-4">
164
+ <div class="flex flex-col md:flex-row md:items-center md:justify-between">
165
+ <h2 class="text-2xl font-bold leading-tight text-gray-900">Conversation Analysis Dashboard</h2>
166
+ <div class="mt-2 md:mt-0 flex items-center space-x-2">
167
+ <span class="text-sm text-gray-500">Last run:</span>
168
+ <span id="lastRunTime" class="text-sm font-medium text-gray-700">Never</span>
169
+ </div>
170
+ </div>
171
+ </div>
172
+
173
+ <!-- Stats -->
174
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
175
+ <div class="bg-white overflow-hidden shadow rounded-lg">
176
+ <div class="px-4 py-5 sm:p-6">
177
+ <div class="flex items-center">
178
+ <div class="flex-shrink-0 bg-green-500 rounded-md p-3">
179
+ <i class="fas fa-check-circle text-white"></i>
180
+ </div>
181
+ <div class="ml-5 w-0 flex-1">
182
+ <dl>
183
+ <dt class="text-sm font-medium text-gray-500 truncate">Completed</dt>
184
+ <dd class="flex items-baseline">
185
+ <div class="text-2xl font-semibold text-gray-900">
186
+ <span id="completedCount">0</span>
187
+ </div>
188
+ </dd>
189
+ </dl>
190
+ </div>
191
+ </div>
192
+ </div>
193
+ </div>
194
+ <div class="bg-white overflow-hidden shadow rounded-lg">
195
+ <div class="px-4 py-5 sm:p-6">
196
+ <div class="flex items-center">
197
+ <div class="flex-shrink-0 bg-red-500 rounded-md p-3">
198
+ <i class="fas fa-exclamation-circle text-white"></i>
199
+ </div>
200
+ <div class="ml-5 w-0 flex-1">
201
+ <dl>
202
+ <dt class="text-sm font-medium text-gray-500 truncate">Errors</dt>
203
+ <dd class="flex items-baseline">
204
+ <div class="text-2xl font-semibold text-gray-900">
205
+ <span id="errorCount">0</span>
206
+ </div>
207
+ </dd>
208
+ </dl>
209
+ </div>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ <div class="bg-white overflow-hidden shadow rounded-lg">
214
+ <div class="px-4 py-5 sm:p-6">
215
+ <div class="flex items-center">
216
+ <div class="flex-shrink-0 bg-blue-500 rounded-md p-3">
217
+ <i class="fas fa-spinner text-white"></i>
218
+ </div>
219
+ <div class="ml-5 w-0 flex-1">
220
+ <dl>
221
+ <dt class="text-sm font-medium text-gray-500 truncate">Processing</dt>
222
+ <dd class="flex items-baseline">
223
+ <div class="text-2xl font-semibold text-gray-900">
224
+ <span id="processingCount">0</span>
225
+ </div>
226
+ </dd>
227
+ </dl>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ </div>
232
+ <div class="bg-white overflow-hidden shadow rounded-lg">
233
+ <div class="px-4 py-5 sm:p-6">
234
+ <div class="flex items-center">
235
+ <div class="flex-shrink-0 bg-purple-500 rounded-md p-3">
236
+ <i class="fas fa-chart-line text-white"></i>
237
+ </div>
238
+ <div class="ml-5 w-0 flex-1">
239
+ <dl>
240
+ <dt class="text-sm font-medium text-gray-500 truncate">Progress</dt>
241
+ <dd class="flex items-baseline">
242
+ <div class="text-2xl font-semibold text-gray-900">
243
+ <span id="progressPercent">0</span>%
244
+ </div>
245
+ </dd>
246
+ </dl>
247
+ </div>
248
+ </div>
249
+ </div>
250
+ </div>
251
+ </div>
252
+
253
+ <!-- Progress bar -->
254
+ <div class="mb-6">
255
+ <div class="flex justify-between mb-1">
256
+ <span class="text-sm font-medium text-gray-700">Analysis Progress</span>
257
+ <span class="text-sm font-medium text-gray-700"><span id="progressPercent2">0</span>%</span>
258
+ </div>
259
+ <div class="w-full bg-gray-200 rounded-full h-2.5">
260
+ <div id="progressBar" class="progress-bar bg-purple-600 h-2.5 rounded-full" style="width: 0%"></div>
261
+ </div>
262
+ </div>
263
+
264
+ <!-- Agent Counts -->
265
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
266
+ <div class="bg-white p-4 rounded-lg shadow">
267
+ <div class="flex items-center justify-between">
268
+ <div class="flex items-center space-x-2">
269
+ <div class="p-2 rounded-md bg-blue-100 text-blue-600">
270
+ <i class="fas fa-smile"></i>
271
+ </div>
272
+ <span class="text-sm font-medium text-gray-700">Sentiment</span>
273
+ </div>
274
+ <span id="sentimentCount" class="text-lg font-bold text-gray-900">0</span>
275
+ </div>
276
+ </div>
277
+ <div class="bg-white p-4 rounded-lg shadow">
278
+ <div class="flex items-center justify-between">
279
+ <div class="flex items-center space-x-2">
280
+ <div class="p-2 rounded-md bg-green-100 text-green-600">
281
+ <i class="fas fa-tags"></i>
282
+ </div>
283
+ <span class="text-sm font-medium text-gray-700">Topics</span>
284
+ </div>
285
+ <span id="topicCount" class="text-lg font-bold text-gray-900">0</span>
286
+ </div>
287
+ </div>
288
+ <div class="bg-white p-4 rounded-lg shadow">
289
+ <div class="flex items-center justify-between">
290
+ <div class="flex items-center space-x-2">
291
+ <div class="p-2 rounded-md bg-purple-100 text-purple-600">
292
+ <i class="fas fa-tasks"></i>
293
+ </div>
294
+ <span class="text-sm font-medium text-gray-700">Progress</span>
295
+ </div>
296
+ <span id="progressCount" class="text-lg font-bold text-gray-900">0</span>
297
+ </div>
298
+ </div>
299
+ <div class="bg-white p-4 rounded-lg shadow">
300
+ <div class="flex items-center justify-between">
301
+ <div class="flex items-center space-x-2">
302
+ <div class="p-2 rounded-md bg-yellow-100 text-yellow-600">
303
+ <i class="fas fa-file-alt"></i>
304
+ </div>
305
+ <span class="text-sm font-medium text-gray-700">Summaries</span>
306
+ </div>
307
+ <span id="summaryCount" class="text-lg font-bold text-gray-900">0</span>
308
+ </div>
309
+ </div>
310
+ </div>
311
+
312
+ <!-- Visualizations -->
313
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
314
+ <div class="bg-white p-4 rounded-lg shadow">
315
+ <div id="sentimentChart">
316
+ <h4 class="font-medium text-gray-900 mb-3">Sentiment Distribution</h4>
317
+ <div class="text-center py-10 text-gray-400">
318
+ <i class="fas fa-chart-pie text-2xl mb-2"></i>
319
+ <p class="text-sm">No sentiment data available yet</p>
320
+ </div>
321
+ </div>
322
+ </div>
323
+ <div class="bg-white p-4 rounded-lg shadow">
324
+ <div id="topicChart">
325
+ <h4 class="font-medium text-gray-900 mb-3">Top Topics</h4>
326
+ <div class="text-center py-10 text-gray-400">
327
+ <i class="fas fa-chart-bar text-2xl mb-2"></i>
328
+ <p class="text-sm">No topic data available yet</p>
329
+ </div>
330
+ </div>
331
+ </div>
332
+ </div>
333
+
334
+ <!-- Results section -->
335
+ <div class="bg-white shadow rounded-lg overflow-hidden mb-6">
336
+ <div class="px-4 py-5 border-b border-gray-200 sm:px-6">
337
+ <div class="flex flex-col md:flex-row md:items-center md:justify-between">
338
+ <h3 class="text-lg font-medium leading-6 text-gray-900">Conversation Results</h3>
339
+ <p class="mt-1 text-sm text-gray-500 md:mt-0">
340
+ Showing <span id="showingCount" class="font-medium">0</span> of <span id="totalCount" class="font-medium">0</span> conversations
341
+ </p>
342
+ </div>
343
+ </div>
344
+ <div id="conversationResults" class="divide-y divide-gray-200">
345
+ <div class="text-center py-10 text-gray-400">
346
+ <i class="fas fa-comments text-4xl mb-2"></i>
347
+ <p>No analysis results yet. Start the analysis to see results here.</p>
348
+ </div>
349
+ </div>
350
+ </div>
351
+ </div>
352
+ </div>
353
+
354
+ <!-- Upload Modal -->
355
+ <div id="uploadModal" class="fixed z-50 inset-0 overflow-y-auto hidden">
356
+ <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
357
+ <div class="fixed inset-0 transition-opacity modal-overlay" aria-hidden="true">
358
+ <div class="absolute inset-0 bg-gray-500 opacity-75"></div>
359
+ </div>
360
+ <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
361
+ <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
362
+ <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
363
+ <div class="sm:flex sm:items-start">
364
+ <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-purple-100 sm:mx-0 sm:h-10 sm:w-10">
365
+ <i class="fas fa-file-upload text-purple-600"></i>
366
+ </div>
367
+ <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
368
+ <h3 class="text-lg leading-6 font-medium text-gray-900">Upload Conversations</h3>
369
+ <div class="mt-4">
370
+ <div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
371
+ <div class="space-y-1 text-center">
372
+ <div class="flex text-sm text-gray-600">
373
+ <label for="fileUpload" class="relative cursor-pointer bg-white rounded-md font-medium text-purple-600 hover:text-purple-500 focus-within:outline-none">
374
+ <span>Upload a file</span>
375
+ <input id="fileUpload" name="fileUpload" type="file" class="sr-only">
376
+ </label>
377
+ <p class="pl-1">or drag and drop</p>
378
+ </div>
379
+ <p class="text-xs text-gray-500">CSV, JSON, or TXT files up to 10MB</p>
380
+ </div>
381
+ </div>
382
+ <div class="mt-4">
383
+ <p class="text-sm text-gray-500">Selected file: <span id="fileNameDisplay" class="font-medium">None</span></p>
384
+ </div>
385
+ <div class="mt-4">
386
+ <div class="w-full bg-gray-200 rounded-full h-2.5">
387
+ <div id="uploadProgress" class="bg-purple-600 h-2.5 rounded-full" style="width: 0%"></div>
388
+ </div>
389
+ </div>
390
+ </div>
391
+ </div>
392
+ </div>
393
+ </div>
394
+ <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
395
+ <button id="confirmUpload" type="button" disabled class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-purple-600 text-base font-medium text-white hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 sm:ml-3 sm:w-auto sm:text-sm">
396
+ Upload and Extract
397
+ </button>
398
+ <button id="closeUploadModal" type="button" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
399
+ Cancel
400
+ </button>
401
+ </div>
402
+ </div>
403
+ </div>
404
+ </div>
405
+
406
+ <!-- Analysis Details Modal -->
407
+ <div id="analysisModal" class="fixed z-50 inset-0 overflow-y-auto hidden">
408
+ <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
409
+ <div class="fixed inset-0 transition-opacity modal-overlay" aria-hidden="true">
410
+ <div class="absolute inset-0 bg-gray-500 opacity-75"></div>
411
+ </div>
412
+ <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
413
+ <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-2xl sm:w-full">
414
+ <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
415
+ <div class="sm:flex sm:items-start">
416
+ <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
417
+ <i class="fas fa-chart-bar text-blue-600"></i>
418
+ </div>
419
+ <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
420
+ <h3 class="text-lg leading-6 font-medium text-gray-900">Conversation Analysis Details</h3>
421
+ <div class="mt-4">
422
+ <div class="bg-gray-50 p-4 rounded-lg mb-4">
423
+ <h4 class="font-medium text-gray-900 mb-2">Summary</h4>
424
+ <p id="modalSummary" class="text-sm text-gray-700">Loading summary...</p>
425
+ </div>
426
+
427
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
428
+ <div>
429
+ <h4 class="font-medium text-gray-900 mb-2">Success Indicators</h4>
430
+ <div id="successIndicators" class="space-y-2">
431
+ <div class="bg-green-50 border border-green-100 p-3 rounded-lg">
432
+ <p class="text-sm text-green-800">Positive engagement throughout the conversation</p>
433
+ </div>
434
+ <div class="bg-green-50 border border-green-100 p-3 rounded-lg">
435
+ <p class="text-sm text-green-800">Clear communication of key points</p>
436
+ </div>
437
+ </div>
438
+ </div>
439
+ <div>
440
+ <h4 class="font-medium text-gray-900 mb-2">Struggle Indicators</h4>
441
+ <div id="struggleIndicators" class="space-y-2">
442
+ <div class="bg-red-50 border border-red-100 p-3 rounded-lg">
443
+ <p class="text-sm text-red-800">Some confusion around technical terms</p>
444
+ </div>
445
+ </div>
446
+ </div>
447
+ </div>
448
+
449
+ <div class="mb-4">
450
+ <h4 class="font-medium text-gray-900 mb-2">Main Topics</h4>
451
+ <div id="mainTopics" class="flex flex-wrap gap-1">
452
+ <span class="inline-block bg-white px-2 py-1 rounded-md text-xs mr-1 mb-1">Product Features</span>
453
+ <span class="inline-block bg-white px-2 py-1 rounded-md text-xs mr-1 mb-1">Pricing</span>
454
+ <span class="inline-block bg-white px-2 py-1 rounded-md text-xs mr-1 mb-1">Implementation</span>
455
+ </div>
456
+ </div>
457
+
458
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
459
+ <div class="bg-gray-50 p-3 rounded-lg">
460
+ <h4 class="font-medium text-gray-900 mb-2">Sentiment Trend</h4>
461
+ <div class="flex items-center">
462
+ <i class="fas fa-smile text-green-600 text-sm mr-2"></i>
463
+ <span id="sentimentTrend" class="text-sm font-medium text-green-600">Mostly Positive</span>
464
+ </div>
465
+ </div>
466
+ <div class="bg-gray-50 p-3 rounded-lg">
467
+ <h4 class="font-medium text-gray-900 mb-2">Key Learning</h4>
468
+ <p id="keyLearning" class="text-sm text-gray-700">The customer was particularly interested in integration capabilities with their existing systems.</p>
469
+ </div>
470
+ </div>
471
+ </div>
472
+ </div>
473
+ </div>
474
+ </div>
475
+ <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
476
+ <button id="closeModal" type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
477
+ Close
478
+ </button>
479
+ </div>
480
+ </div>
481
+ </div>
482
+ </div>
483
+
484
+ <script>
485
+ // DOM Elements
486
+ const mobileMenuBtn = document.getElementById('mobileMenuBtn');
487
+ const sidebar = document.getElementById('sidebar');
488
+ const workerCountInput = document.getElementById('workerCount');
489
+ const workerCountDisplay = document.getElementById('workerCountDisplay');
490
+ const uploadBtn = document.getElementById('uploadBtn');
491
+ const uploadModal = document.getElementById('uploadModal');
492
+ const closeUploadModalBtn = document.getElementById('closeUploadModal');
493
+ const fileUploadInput = document.getElementById('fileUpload');
494
+ const fileNameDisplay = document.getElementById('fileNameDisplay');
495
+ const confirmUploadBtn = document.getElementById('confirmUpload');
496
+ const uploadProgress = document.getElementById('uploadProgress');
497
+ const startAnalysisBtn = document.getElementById('startAnalysisBtn');
498
+ const lastRunTimeSpan = document.getElementById('lastRunTime');
499
+ const completedCountSpan = document.getElementById('completedCount');
500
+ const errorCountSpan = document.getElementById('errorCount');
501
+ const processingCountSpan = document.getElementById('processingCount');
502
+ const progressPercentSpan = document.getElementById('progressPercent');
503
+ const progressPercentSpan2 = document.getElementById('progressPercent2');
504
+ const progressBarDiv = document.getElementById('progressBar');
505
+ const sentimentCountSpan = document.getElementById('sentimentCount');
506
+ const topicCountSpan = document.getElementById('topicCount');
507
+ const progressCountSpan = document.getElementById('progressCount');
508
+ const summaryCountSpan = document.getElementById('summaryCount');
509
+ const sentimentStatusSpan = document.getElementById('sentimentStatus');
510
+ const topicStatusSpan = document.getElementById('topicStatus');
511
+ const progressStatusSpan = document.getElementById('progressStatus');
512
+ const summaryStatusSpan = document.getElementById('summaryStatus');
513
+ const resultsContainer = document.getElementById('conversationResults');
514
+ const showingCountSpan = document.getElementById('showingCount');
515
+ const totalCountSpan = document.getElementById('totalCount');
516
+ const analysisModal = document.getElementById('analysisModal');
517
+ const closeModalBtn = document.getElementById('closeModal');
518
+ const modalSummary = document.getElementById('modalSummary');
519
+ const successIndicatorsDiv = document.getElementById('successIndicators');
520
+ const struggleIndicatorsDiv = document.getElementById('struggleIndicators');
521
+ const mainTopicsDiv = document.getElementById('mainTopics');
522
+ const sentimentTrendSpan = document.getElementById('sentimentTrend');
523
+ const sentimentTrendIcon = document.querySelector('#sentimentTrend').previousElementSibling;
524
+ const keyLearningPara = document.getElementById('keyLearning');
525
+ const downloadBtn = document.getElementById('downloadBtn');
526
+ const pauseBtn = document.getElementById('pauseBtn');
527
+ const stopBtn = document.getElementById('stopBtn');
528
+ const modelSelect = document.getElementById('modelSelect');
529
+ const sentimentChartDiv = document.getElementById('sentimentChart');
530
+ const topicChartDiv = document.getElementById('topicChart');
531
+
532
+ // Sample data for demonstration
533
+ const sampleResults = [
534
+ {
535
+ filename: "Customer Support Call - 2023-05-15",
536
+ date: "May 15, 2023",
537
+ sentiment: "Positive",
538
+ summary_snippet: "The customer was satisfied with the resolution provided and appreciated the quick response time.",
539
+ topics: ["Support", "Resolution", "Response Time"],
540
+ exchanges: 12,
541
+ has_details: true
542
+ },
543
+ {
544
+ filename: "Sales Demo - Acme Corp",
545
+ date: "May 10, 2023",
546
+ sentiment: "Neutral",
547
+ summary_snippet: "The prospect showed interest in the product but had concerns about pricing and implementation timeline.",
548
+ topics: ["Pricing", "Implementation", "Features"],
549
+ exchanges: 24,
550
+ has_details: true
551
+ },
552
+ {
553
+ filename: "Team Meeting - Project Kickoff",
554
+ date: "May 5, 2023",
555
+ sentiment: "Positive",
556
+ summary_snippet: "The team aligned on project goals and timelines with clear action items assigned.",
557
+ topics: ["Goals", "Timelines", "Action Items"],
558
+ exchanges: 18,
559
+ has_details: true
560
+ }
561
+ ];
562
+
563
+ // Event Listeners
564
+ mobileMenuBtn.addEventListener('click', function() {
565
+ sidebar.classList.toggle('sidebar-open');
566
+ });
567
+
568
+ // Close sidebar when clicking outside on mobile
569
+ document.addEventListener('click', function(event) {
570
+ if (!sidebar.contains(event.target) && !mobileMenuBtn.contains(event.target) && sidebar.classList.contains('sidebar-open') && window.innerWidth <= 768) {
571
+ sidebar.classList.remove('sidebar-open');
572
+ }
573
+ });
574
+
575
+ // Worker count display
576
+ workerCountInput.addEventListener('input', function() {
577
+ workerCountDisplay.textContent = this.value;
578
+ });
579
+
580
+ // File upload modal
581
+ uploadBtn.addEventListener('click', function() {
582
+ uploadModal.classList.remove('hidden');
583
+ });
584
+
585
+ closeUploadModalBtn.addEventListener('click', function() {
586
+ uploadModal.classList.add('hidden');
587
+ resetUploadModal();
588
+ });
589
+
590
+ // Reset upload modal UI
591
+ function resetUploadModal() {
592
+ fileUploadInput.value = '';
593
+ fileNameDisplay.textContent = 'None';
594
+ confirmUploadBtn.disabled = true;
595
+ uploadProgress.style.width = '0%';
596
+ }
597
+
598
+ // File selection handler
599
+ fileUploadInput.addEventListener('change', function(e) {
600
+ const file = e.target.files[0];
601
+ if (file) {
602
+ fileNameDisplay.textContent = file.name;
603
+ confirmUploadBtn.disabled = false;
604
+ } else {
605
+ resetUploadModal();
606
+ }
607
+ });
608
+
609
+ // Confirm upload button
610
+ confirmUploadBtn.addEventListener('click', function() {
611
+ const file = fileUploadInput.files[0];
612
+ if (!file) return;
613
+
614
+ // Simulate upload progress
615
+ let progress = 0;
616
+ const interval = setInterval(() => {
617
+ progress += 10;
618
+ uploadProgress.style.width = `${progress}%`;
619
+
620
+ if (progress >= 100) {
621
+ clearInterval(interval);
622
+ setTimeout(() => {
623
+ // Update UI with sample data
624
+ totalCountSpan.textContent = sampleResults.length;
625
+ showingCountSpan.textContent = 0;
626
+
627
+ // Reset analysis progress
628
+ completedCountSpan.textContent = '0';
629
+ errorCountSpan.textContent = '0';
630
+ processingCountSpan.textContent = '0';
631
+ progressPercentSpan.textContent = '0';
632
+ progressPercentSpan2.textContent = '0';
633
+ progressBarDiv.style.width = '0%';
634
+
635
+ // Update agent statuses
636
+ updateAgentStatuses("Ready");
637
+
638
+ // Close modal after upload
639
+ uploadModal.classList.add('hidden');
640
+ resetUploadModal();
641
+
642
+ // Show success message
643
+ alert('File uploaded successfully! 3 conversations extracted.');
644
+ }, 500);
645
+ }
646
+ }, 200);
647
+ });
648
+
649
+ // Update agent statuses
650
+ function updateAgentStatuses(status) {
651
+ const statusInfo = {
652
+ "Idle": { text: "Idle", color: "gray" },
653
+ "Ready": { text: "Ready", color: "blue" },
654
+ "Active": { text: "Active", color: "blue" },
655
+ "Completed": { text: "Completed", color: "green" },
656
+ "Error": { text: "Error", color: "red" }
657
+ };
658
+
659
+ const currentStatus = statusInfo[status] || statusInfo["Idle"];
660
+
661
+ sentimentStatusSpan.textContent = currentStatus.text;
662
+ sentimentStatusSpan.className = `text-sm font-medium text-${currentStatus.color}-600`;
663
+
664
+ topicStatusSpan.textContent = currentStatus.text;
665
+ topicStatusSpan.className = `text-sm font-medium text-${currentStatus.color}-600`;
666
+
667
+ progressStatusSpan.textContent = currentStatus.text;
668
+ progressStatusSpan.className = `text-sm font-medium text-${currentStatus.color}-600`;
669
+
670
+ summaryStatusSpan.textContent = currentStatus.text;
671
+ summaryStatusSpan.className = `text-sm font-medium text-${currentStatus.color}-600`;
672
+ }
673
+
674
+ // Start analysis button
675
+ startAnalysisBtn.addEventListener('click', function() {
676
+ const model = modelSelect.value;
677
+
678
+ // Disable button and show loading
679
+ startAnalysisBtn.disabled = true;
680
+ startAnalysisBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Starting...';
681
+ startAnalysisBtn.classList.add('opacity-50', 'cursor-not-allowed');
682
+
683
+ // Simulate analysis progress
684
+ setTimeout(() => {
685
+ // Update last run time
686
+ lastRunTimeSpan.textContent = new Date().toLocaleString();
687
+
688
+ // Update agent statuses
689
+ updateAgentStatuses("Active");
690
+
691
+ // Enable pause/stop buttons
692
+ pauseBtn.disabled = false;
693
+ stopBtn.disabled = false;
694
+
695
+ // Simulate analysis progress
696
+ let progress = 0;
697
+ const analysisInterval = setInterval(() => {
698
+ progress += 5;
699
+
700
+ // Update progress
701
+ progressPercentSpan.textContent = progress;
702
+ progressPercentSpan2.textContent = progress;
703
+ progressBarDiv.style.width = `${progress}%`;
704
+
705
+ // Update counts
706
+ completedCountSpan.textContent = Math.floor(progress / 100 * sampleResults.length);
707
+ processingCountSpan.textContent = sampleResults.length - Math.floor(progress / 100 * sampleResults.length);
708
+
709
+ // Update agent counts
710
+ sentimentCountSpan.textContent = Math.floor(progress / 30);
711
+ topicCountSpan.textContent = Math.floor(progress / 25);
712
+ progressCountSpan.textContent = Math.floor(progress / 20);
713
+ summaryCountSpan.textContent = Math.floor(progress / 35);
714
+
715
+ // When analysis completes
716
+ if (progress >= 100) {
717
+ clearInterval(analysisInterval);
718
+
719
+ // Update UI for completion
720
+ updateAgentStatuses("Completed");
721
+ startAnalysisBtn.disabled = false;
722
+ startAnalysisBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Analysis Complete';
723
+ startAnalysisBtn.classList.remove('opacity-50', 'cursor-not-allowed');
724
+ startAnalysisBtn.classList.add('bg-green-100', 'text-green-700', 'hover:bg-green-200');
725
+
726
+ // Disable pause/stop buttons
727
+ pauseBtn.disabled = true;
728
+ stopBtn.disabled = true;
729
+
730
+ // Show results
731
+ updateResultsList();
732
+ updateVisualizations();
733
+ }
734
+ }, 300);
735
+ }, 1000);
736
+ });
737
+
738
+ // Update results list
739
+ function updateResultsList() {
740
+ resultsContainer.innerHTML = '';
741
+
742
+ sampleResults.forEach((result, index) => {
743
+ const card = document.createElement('div');
744
+ card.className = 'conversation-card bg-white border border-gray-200 rounded-lg overflow-hidden shadow-sm transition cursor-pointer mb-4 fade-in';
745
+ card.style.animationDelay = `${index * 0.1}s`;
746
+
747
+ // Determine sentiment color
748
+ let sentimentClass = 'bg-gray-100 text-gray-800';
749
+ if (result.sentiment.toLowerCase() === 'positive') {
750
+ sentimentClass = 'bg-green-100 text-green-800';
751
+ } else if (result.sentiment.toLowerCase() === 'negative') {
752
+ sentimentClass = 'bg-red-100 text-red-800';
753
+ }
754
+
755
+ card.innerHTML = `
756
+ <div class="p-4 border-b border-gray-200">
757
+ <div class="flex justify-between items-start">
758
+ <div>
759
+ <h4 class="font-medium text-gray-900 truncate">${result.filename}</h4>
760
+ <p class="text-xs text-gray-500 mt-1">${result.date}</p>
761
+ </div>
762
+ <span class="px-2 py-1 rounded-full text-xs font-medium ${sentimentClass}">
763
+ ${result.sentiment}
764
+ </span>
765
+ </div>
766
+ </div>
767
+ <div class="p-4">
768
+ <p class="text-sm text-gray-600 line-clamp-2">${result.summary_snippet}</p>
769
+ <div class="mt-3 flex flex-wrap gap-1">
770
+ ${result.topics.map(topic =>
771
+ `<span class="inline-block bg-gray-100 px-2 py-1 rounded-md text-xs">${topic}</span>`
772
+ ).join('')}
773
+ </div>
774
+ </div>
775
+ <div class="px-4 py-2 bg-gray-50 border-t border-gray-200 flex justify-between items-center">
776
+ <span class="text-xs text-gray-500">${result.exchanges} exchanges</span>
777
+ <button class="text-blue-600 hover:text-blue-800 text-sm font-medium" data-filename="${result.filename}">
778
+ View Details
779
+ </button>
780
+ </div>
781
+ `;
782
+
783
+ // Add click handler to show modal
784
+ const detailButton = card.querySelector('button');
785
+ detailButton.addEventListener('click', function(e) {
786
+ e.stopPropagation();
787
+ showAnalysisModal(result.filename);
788
+ });
789
+
790
+ card.addEventListener('click', function() {
791
+ showAnalysisModal(result.filename);
792
+ });
793
+
794
+ resultsContainer.appendChild(card);
795
+ });
796
+
797
+ showingCountSpan.textContent = sampleResults.length;
798
+ totalCountSpan.textContent = sampleResults.length;
799
+ }
800
+
801
+ // Show analysis modal with details
802
+ function showAnalysisModal(filename) {
803
+ // Find the selected conversation
804
+ const conversation = sampleResults.find(r => r.filename === filename);
805
+ if (!conversation) return;
806
+
807
+ // Update modal content
808
+ modalSummary.textContent = conversation.summary_snippet;
809
+
810
+ // Update success indicators
811
+ successIndicatorsDiv.innerHTML = `
812
+ <div class="bg-green-50 border border-green-100 p-3 rounded-lg">
813
+ <p class="text-sm text-green-800">Positive engagement throughout the conversation</p>
814
+ </div>
815
+ <div class="bg-green-50 border border-green-100 p-3 rounded-lg">
816
+ <p class="text-sm text-green-800">Clear communication of key points</p>
817
+ </div>
818
+ `;
819
+
820
+ // Update struggle indicators
821
+ struggleIndicatorsDiv.innerHTML = `
822
+ <div class="bg-red-50 border border-red-100 p-3 rounded-lg">
823
+ <p class="text-sm text-red-800">Some confusion around technical terms</p>
824
+ </div>
825
+ `;
826
+
827
+ // Update main topics
828
+ mainTopicsDiv.innerHTML = conversation.topics.map(topic =>
829
+ `<span class="inline-block bg-white px-2 py-1 rounded-md text-xs mr-1 mb-1">${topic}</span>`
830
+ ).join('');
831
+
832
+ // Update sentiment trend
833
+ let sentimentIcon, sentimentColor, sentimentText;
834
+ if (conversation.sentiment.toLowerCase() === 'positive') {
835
+ sentimentIcon = 'fa-smile text-green-600';
836
+ sentimentColor = 'text-green-600';
837
+ sentimentText = 'Mostly Positive';
838
+ } else if (conversation.sentiment.toLowerCase() === 'negative') {
839
+ sentimentIcon = 'fa-frown text-red-600';
840
+ sentimentColor = 'text-red-600';
841
+ sentimentText = 'Mostly Negative';
842
+ } else {
843
+ sentimentIcon = 'fa-meh text-gray-500';
844
+ sentimentColor = 'text-gray-600';
845
+ sentimentText = 'Neutral';
846
+ }
847
+
848
+ sentimentTrendIcon.className = `fas ${sentimentIcon} text-sm mr-2`;
849
+ sentimentTrendSpan.textContent = sentimentText;
850
+ sentimentTrendSpan.className = `text-sm font-medium ${sentimentColor}`;
851
+
852
+ // Update key learning
853
+ keyLearningPara.textContent = "The customer was particularly interested in integration capabilities with their existing systems.";
854
+
855
+ // Show modal
856
+ analysisModal.classList.remove('hidden');
857
+ }
858
+
859
+ // Close modal
860
+ closeModalBtn.addEventListener('click', function() {
861
+ analysisModal.classList.add('hidden');
862
+ });
863
+
864
+ // Download results
865
+ downloadBtn.addEventListener('click', function() {
866
+ alert('Downloading analysis results...');
867
+ });
868
+
869
+ // Pause analysis
870
+ pauseBtn.addEventListener('click', function() {
871
+ alert('Analysis paused. Click Start to resume.');
872
+ updateAgentStatuses("Ready");
873
+ });
874
+
875
+ // Stop analysis
876
+ stopBtn.addEventListener('click', function() {
877
+ if (confirm('Are you sure you want to stop the analysis? Progress will be saved.')) {
878
+ alert('Analysis stopped.');
879
+ updateAgentStatuses("Ready");
880
+ }
881
+ });
882
+
883
+ // Update visualizations
884
+ function updateVisualizations() {
885
+ // Sentiment distribution
886
+ const sentimentCounts = sampleResults.reduce((acc, res) => {
887
+ const sentiment = res.sentiment || 'N/A';
888
+ acc[sentiment] = (acc[sentiment] || 0) + 1;
889
+ return acc;
890
+ }, {});
891
+
892
+ let sentimentHtml = '<h4 class="font-medium text-gray-900 mb-3">Sentiment Distribution</h4>';
893
+ sentimentHtml += '<ul class="list-disc list-inside text-sm text-gray-700">';
894
+ for (const [sentiment, count] of Object.entries(sentimentCounts)) {
895
+ let colorClass = 'text-gray-600';
896
+ if (sentiment.toLowerCase() === 'positive') colorClass = 'text-green-600';
897
+ else if (sentiment.toLowerCase() === 'negative') colorClass = 'text-red-600';
898
+
899
+ sentimentHtml += `<li class="${colorClass}">${sentiment}: ${count}</li>`;
900
+ }
901
+ sentimentHtml += '</ul>';
902
+ sentimentChartDiv.innerHTML = sentimentHtml;
903
+
904
+ // Top topics
905
+ const allTopics = sampleResults.flatMap(res => res.topics || []);
906
+ const topicCounts = allTopics.reduce((acc, topic) => {
907
+ acc[topic] = (acc[topic] || 0) + 1;
908
+ return acc;
909
+ }, {});
910
+
911
+ const sortedTopics = Object.entries(topicCounts).sort(([, a], [, b]) => b - a);
912
+
913
+ let topicHtml = '<h4 class="font-medium text-gray-900 mb-3">Top Topics</h4>';
914
+ topicHtml += '<ul class="list-disc list-inside text-sm text-gray-700">';
915
+ sortedTopics.slice(0, 5).forEach(([topic, count]) => {
916
+ topicHtml += `<li>${topic}: ${count}</li>`;
917
+ });
918
+ topicHtml += '</ul>';
919
+ topicChartDiv.innerHTML = topicHtml;
920
+ }
921
+
922
+ // Initialize
923
+ document.addEventListener('DOMContentLoaded', function() {
924
+ // Set initial values
925
+ workerCountDisplay.textContent = workerCountInput.value;
926
+ lastRunTimeSpan.textContent = 'Never';
927
+
928
+ // Add fade-in class to cards when they're added
929
+ const observer = new MutationObserver(function(mutations) {
930
+ mutations.forEach(function(mutation) {
931
+ mutation.addedNodes.forEach(function(node) {
932
+ if (node.classList && node.classList.contains('conversation-card')) {
933
+ node.classList.add('fade-in');
934
+ }
935
+ });
936
+ });
937
+ });
938
+
939
+ observer.observe(resultsContainer, {
940
+ childList: true,
941
+ subtree: true
942
+ });
943
+ });
944
+ </script>
945
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Ultronprime/conv" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
946
+ </html>
prompts.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ <script> const mobileMenuBtn = document.getElementById('mobileMenuBtn'); const sidebar = document.getElementById('sidebar'); const workerCountInput = document.getElementById('workerCount'); const workerCountDisplay = document.getElementById('workerCountDisplay'); const uploadBtn = document.getElementById('uploadBtn'); const uploadModal = document.getElementById('uploadModal'); const closeUploadModalBtn = document.getElementById('closeUploadModal'); const fileUploadInput = document.getElementById('fileUpload'); const fileNameDisplay = document.getElementById('fileNameDisplay'); const confirmUploadBtn = document.getElementById('confirmUpload'); const uploadProgress = document.getElementById('uploadProgress'); const startAnalysisBtn = document.getElementById('startAnalysis'); const lastRunTimeSpan = document.getElementById('lastRunTime'); const completedCountSpan = document.getElementById('completedCount'); const errorCountSpan = document.getElementById('errorCount'); const processingCountSpan = document.getElementById('processingCount'); const progressPercentSpan = document.getElementById('progressPercent'); const progressBarDiv = document.getElementById('progressBar'); const sentimentCountSpan = document.getElementById('sentimentCount'); const topicCountSpan = document.getElementById('topicCount'); const progressCountSpan = document.getElementById('progressCount'); const summaryCountSpan = document.getElementById('summaryCount'); const sentimentStatusSpan = document.getElementById('sentimentStatus'); const topicStatusSpan = document.getElementById('topicStatus'); const progressStatusSpan = document.getElementById('progressStatus'); const summaryStatusSpan = document.getElementById('summaryStatus'); const resultsContainer = document.getElementById('conversationResults'); const showingCountSpan = document.getElementById('showingCount'); const totalCountSpan = document.getElementById('totalCount'); const analysisModal = document.getElementById('analysisModal'); const closeModalBtn = document.getElementById('closeModal'); const modalSummary = document.getElementById('modalSummary'); const successIndicatorsDiv = document.getElementById('successIndicators'); const struggleIndicatorsDiv = document.getElementById('struggleIndicators'); const mainTopicsDiv = document.getElementById('mainTopics'); const sentimentTrendSpan = document.getElementById('sentimentTrend'); const sentimentTrendIcon = sentimentTrendSpan.previousElementSibling.querySelector('i'); // Assuming the icon is the previous sibling const keyLearningPara = document.getElementById('keyLearning'); const downloadBtn = document.getElementById('downloadBtn'); const pauseBtn = document.getElementById('pauseBtn'); const stopBtn = document.getElementById('stopBtn'); const modelSelect = document.getElementById('modelSelect'); let statusInterval = null; // To hold the interval for status polling // Initial state update updateStatusDisplay(); // Call once on load // Mobile menu toggle mobileMenuBtn.addEventListener('click', function() { sidebar.classList.toggle('sidebar-open'); }); // Close sidebar on click outside (simple example, might need refinement) document.addEventListener('click', function(event) { if (!sidebar.contains(event.target) && !mobileMenuBtn.contains(event.target) && sidebar.classList.contains('sidebar-open') && window.innerWidth <= 768) { sidebar.classList.remove('sidebar-open'); } }); // Update worker count display (still useful for UI config) workerCountInput.addEventListener('input', function() { // Use input for live updates workerCountDisplay.textContent = this.value; // Note: Changing this *during* analysis won't change the actual worker count // A real app would need to signal the backend to resize the pool }); // File upload modal uploadBtn.addEventListener('click', function() { uploadModal.classList.remove('hidden'); }); closeUploadModalBtn.addEventListener('click', function() { uploadModal.classList.add('hidden'); resetUploadModal(); // Reset modal state on close }); // Reset upload modal UI function resetUploadModal() { fileUploadInput.value = ''; // Clear selected file fileNameDisplay.textContent = 'None'; confirmUploadBtn.disabled = true; uploadProgress.style.width = '0%'; } // File upload handling fileUploadInput.addEventListener('change', function(e) { const file = e.target.files[0]; if (file) { fileNameDisplay.textContent = file.name; confirmUploadBtn.disabled = false; } else { resetUploadModal(); } }); // Confirm Upload button (sends file to backend) confirmUploadBtn.addEventListener('click', async function() { const file = fileUploadInput.files[0]; if (!file) return; const formData = new FormData(); formData.append('file', file); // Simulate progress visually for the upload itself (optional, backend handles processing) // We won't do a real upload progress bar here as it's complex uploadProgress.style.width = '0%'; confirmUploadBtn.disabled = true; confirmUploadBtn.textContent = 'Uploading...'; try { const response = await fetch('/upload', { method: 'POST', body: formData, }); if (!response.ok) { const error = await response.json(); throw new Error(error.detail || 'Upload failed'); } const result = await response.json(); console.log('Upload successful:', result); uploadProgress.style.width = '100%'; alert(result.message); // Update total count based on files extracted totalCountSpan.textContent = result.file_count; showingCountSpan.textContent = 0; // No results shown yet // Reset analysis progress UI completedCountSpan.textContent = '0'; errorCountSpan.textContent = '0'; processingCountSpan.textContent = '0'; progressPercentSpan.textContent = '0'; progressBarDiv.style.width = '0%'; sentimentCountSpan.textContent = '0'; topicCountSpan.textContent = '0'; progressCountSpan.textContent = '0'; summaryCountSpan.textContent = '0'; // Clear previous results from display resultsContainer.innerHTML = ` <div class="text-center py-10 text-gray-400"> <i class="fas fa-comments text-4xl mb-2"></i> <p>Files uploaded. Start analysis to see results here.</p> </div> `; // Update agent statuses to 'Ready' or 'Idle' after upload updateAgentStatuses("Ready"); setTimeout(() => { uploadModal.classList.add('hidden'); resetUploadModal(); }, 500); // Give user a moment to read alert } catch (error) { console.error('Upload error:', error); alert('Upload failed: ' + error.message); uploadProgress.style.width = '0%'; // Reset progress on error confirmUploadBtn.disabled = false; confirmUploadBtn.textContent = 'Upload and Extract'; updateAgentStatuses("Idle"); // Stay idle on error } }); // Function to update agent status text and color function updateAgentStatuses(status) { const statusMapping = { "Idle": { text: "Idle", color: "gray" }, "Ready": { text: "Ready", color: "blue" }, // New status after upload "Active": { text: "Active", color: "blue" }, "Completed": { text: "Completed", color: "green" }, "Error": { text: "Error", color: "red" } // For individual agent errors }; const s = statusMapping[status] || statusMapping["Idle"]; sentimentStatusSpan.textContent = s.text; sentimentStatusSpan.className = `text-sm font-medium text-${s.color}-600`; topicStatusSpan.textContent = s.text; topicStatusSpan.className = `text-sm font-medium text-${s.color}-600`; progressStatusSpan.textContent = s.text; progressStatusSpan.className = `text-sm font-medium text-${s.color}-600`; summaryStatusSpan.textContent = s.text; summaryStatusSpan.className = `text-sm font-medium text-${s.color}-600`; } // Start Analysis button startAnalysisBtn.addEventListener('click', async function() { const model = modelSelect.value; // Disable button and indicate starting startAnalysisBtn.disabled = true; startAnalysisBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> <span>Starting...</span>'; startAnalysisBtn.classList.add('opacity-50', 'cursor-not-allowed'); try { const response = await fetch('/start_analysis', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ model: model }) }); if (!response.ok) { const error = await response.json(); throw new Error(error.detail || 'Failed to start analysis'); } const result = await response.json(); console.log('Analysis start message:', result.message); // Update UI immediately to reflect starting lastRunTimeSpan.textContent = new Date().toLocaleString(); updateAgentStatuses("Active"); // Polling will update counts and progress // Start polling for status and results if (statusInterval) clearInterval(statusInterval); // Clear any existing interval statusInterval = setInterval(updateStatusDisplay, 2000); // Poll every 2 seconds // Enable pause/stop buttons pauseBtn.disabled = false; stopBtn.disabled = false; } catch (error) { console.error('Error starting analysis:', error); alert('Failed to start analysis: ' + error.message); // Re-enable button and reset text startAnalysisBtn.disabled = false; startAnalysisBtn.innerHTML = '<i class="fas fa-play"></i> <span>Start Analysis</span>'; startAnalysisBtn.classList.remove('opacity-50', 'cursor-not-allowed'); updateAgentStatuses("Idle"); } }); // Function to poll backend status and update UI async function updateStatusDisplay() { try { const statusResponse = await fetch('/status'); const status = await statusResponse.json(); completedCountSpan.textContent = status.completed_files; errorCountSpan.textContent = status.error_files; processingCountSpan.textContent = status.processing_files_count; progressPercentSpan.textContent = status.progress_percent; progressBarDiv.style.width = status.progress_percent + '%'; lastRunTimeSpan.textContent = status.last_run_time; totalCountSpan.textContent = status.total_files; // Ensure total is correct // Update agent counts sentimentCountSpan.textContent = status.agent_counts.sentiment; topicCountSpan.textContent = status.agent_counts.topic; progressCountSpan.textContent = status.agent_counts.progress; summaryCountSpan.textContent = status.agent_counts.summary; // Update agent statuses based on overall status updateAgentStatuses(status.status); // Uses the main status from backend // Update Start button based on status if (status.status === "Running") { startAnalysisBtn.disabled = true; startAnalysisBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> <span>Analyzing...</span>'; startAnalysisBtn.classList.add('opacity-50', 'cursor-not-allowed'); startAnalysisBtn.classList.remove('bg-white', 'text-purple-700', 'hover:bg-purple-50'); startAnalysisBtn.classList.add('bg-gray-200', 'text-gray-700'); // Indicate disabled running state } else if (status.status === "Completed") { if (statusInterval) clearInterval(statusInterval); // Stop polling on completion startAnalysisBtn.disabled = false; startAnalysisBtn.innerHTML = '<i class="fas fa-check"></i> <span>Analysis Complete</span>'; startAnalysisBtn.classList.remove('opacity-50', 'cursor-not-allowed', 'bg-gray-200', 'text-gray-700', 'bg-white', 'text-purple-700', 'hover:bg-purple-50'); startAnalysisBtn.classList.add('bg-green-100', 'text-green-700', 'hover:bg-green-200'); // Indicate success // Disable pause/stop on completion pauseBtn.disabled = true; stopBtn.disabled = true; } else if (status.status === "Idle" || status.status === "Ready") { startAnalysisBtn.disabled = status.total_files === 0; // Disable if no files startAnalysisBtn.innerHTML = '<i class="fas fa-play"></i> <span>Start Analysis</span>'; startAnalysisBtn.classList.remove('opacity-50', 'cursor-not-allowed', 'bg-green-100', 'text-green-700', 'hover:bg-green-200', 'bg-gray-200', 'text-gray-700'); startAnalysisBtn.classList.add('bg-white', 'text-purple-700', 'hover:bg-purple-50'); // Ensure buttons are disabled if not running if (status.status === "Idle") { // Truly idle, before upload pauseBtn.disabled = true; stopBtn.disabled = true; } else { // Ready after upload pauseBtn.disabled = true; // Can't pause before starting stopBtn.disabled = false; // Can 'stop' to clear files } } // Fetch and display results list if analysis is running or completed if (status.status === "Running" || status.status === "Completed") { await updateResultsList(); // Charts would be updated here based on aggregated data from /status or a new endpoint } } catch (error) { console.error('Error fetching status:', error); // If status fetch fails repeatedly, stop polling and indicate error // For simplicity, we just log here. } } // Function to fetch and display the list of results async function updateResultsList() { try { const resultsResponse = await fetch('/results'); const results = await resultsResponse.json(); // Clear existing results, but keep the placeholder if no results yet if (results.length > 0) { const emptyMessage = resultsContainer.querySelector('.text-center.py-10.text-gray-400'); if (emptyMessage) { emptyMessage.remove(); } resultsContainer.innerHTML = ''; // Clear only if there are results to show results.forEach(result => { const card = document.createElement('div'); card.className = 'conversation-card bg-white border border-gray-200 rounded-lg overflow-hidden shadow-sm transition cursor-pointer fade-in'; // Determine sentiment class for badge let sentimentClass = 'bg-gray-100 text-gray-800'; if (result.sentiment.toLowerCase() === 'positive') { sentimentClass = 'bg-green-100 text-green-800'; } else if (result.sentiment.toLowerCase() === 'negative') { sentimentClass = 'bg-red-100 text-red-800'; } card.innerHTML = ` <div class="p-4 border-b border-gray-200"> <div class="flex justify-between items-start"> <div> <h4 class="font-medium text-gray-900 truncate">${result.filename}</h4> <p class="text-xs text-gray-500 mt-1">${result.date || 'N/A'}</p> </div> <span class="px-2 py-1 rounded-full text-xs font-medium ${sentimentClass}"> ${result.sentiment || 'N/A'} </span> </div> </div> <div class="p-4"> <p class="text-sm text-gray-600 line-clamp-2">${result.summary_snippet || 'No summary snippet available.'}</p> <div class="mt-3 flex flex-wrap gap-1"> ${(result.topics || []).map(topic => `<span class="inline-block bg-gray-100 px-2 py-1 rounded-md text-xs">${topic}</span>` ).join('') || '<span class="inline-block text-xs text-gray-500">No topics identified.</span>'} </div> </div> <div class="px-4 py-2 bg-gray-50 border-t border-gray-200 flex justify-between items-center"> <span class="text-xs text-gray-500">${result.exchanges || 'N/A'} exchanges</span> ${result.has_details ? ` <button class="text-blue-600 hover:text-blue-800 text-sm font-medium" data-filename="${result.filename}"> View Details </button> ` : `<span class="text-xs text-red-500">Analysis Failed</span>`} </div> `; // Add click handler to show modal (on button or card) if(result.has_details) { const detailButton = card.querySelector('button'); detailButton.addEventListener('click', function(e) { e.stopPropagation(); // Prevent card click from triggering showAnalysisModal(detailButton.dataset.filename); }); card.addEventListener('click', function() { showAnalysisModal(result.filename); }); } resultsContainer.appendChild(card); // Use appendChild to add in order }); showingCountSpan.textContent = results.length; // Update showing count // totalCountSpan should already be set by upload } else if (totalCountSpan.textContent > 0) { // Files uploaded, but no results yet (analysis not started or still processing first files) const emptyMessage = resultsContainer.querySelector('.text-center.py-10.text-gray-400'); if (!emptyMessage) { resultsContainer.innerHTML = ` <div class="text-center py-10 text-gray-400"> <i class="fas fa-comments text-4xl mb-2"></i> <p>Analysis in progress or no results yet. Results will appear here.</p> </div> `; } showingCountSpan.textContent = 0; } else { // No files uploaded const emptyMessage = resultsContainer.querySelector('.text-center.py-10.text-gray-400'); if (!emptyMessage) { resultsContainer.innerHTML = ` <div class="text-center py-10 text-gray-400"> <i class="fas fa-comments text-4xl mb-2"></i> <p>No analysis results yet. Start the analysis to see results here.</p> </div> `; } showingCountSpan.textContent = 0; } } catch (error) { console.error('Error fetching results list:', error); resultsContainer.innerHTML = ` <div class="text-center py-10 text-red-400"> <i class="fas fa-exclamation-circle text-4xl mb-2"></i> <p>Error loading results.</p> </div> `; } } // Show analysis modal with details fetched from backend async function showAnalysisModal(filename) { try { // Clear previous modal content and show loading modalSummary.textContent = 'Loading...'; successIndicatorsDiv.innerHTML = '<p class="text-gray-500">Loading...</p>'; struggleIndicatorsDiv.innerHTML = '<p class="text-gray-500">Loading...</p>'; mainTopicsDiv.innerHTML = '<p class="text-gray-500">Loading...</p>'; sentimentTrendSpan.textContent = 'Loading...'; sentimentTrendIcon.className = 'fas fa-spinner fa-spin text-gray-500 text-sm'; // Loading icon keyLearningPara.textContent = 'Loading...'; const response = await fetch(`/analyze/${encodeURIComponent(filename)}`); if (!response.ok) { const error = await response.json(); throw new Error(error.detail || 'Failed to load analysis details'); } const details = await response.json(); console.log('Analysis details:', details); // Populate modal content modalSummary.textContent = details.summary || 'No summary available.'; // Success Indicators successIndicatorsDiv.innerHTML = ''; if (details.success_indicators && details.success_indicators.length > 0) { details.success_indicators.forEach(indicator => { const div = document.createElement('div'); div.className = 'bg-green-50 border border-green-100 p-3 rounded-lg'; div.innerHTML = `<p class="text-sm text-green-800">${indicator}</p>`; // Assuming indicator is the text itself successIndicatorsDiv.appendChild(div); }); } else { successIndicatorsDiv.innerHTML = '<p class="text-gray-500 text-sm">No specific success indicators identified.</p>'; } // Struggle Indicators struggleIndicatorsDiv.innerHTML = ''; if (details.struggle_indicators && details.struggle_indicators.length > 0) { details.struggle_indicators.forEach(indicator => { const div = document.createElement('div'); div.className = 'bg-red-50 border border-red-100 p-3 rounded-lg'; div.innerHTML = `<p class="text-sm text-red-800">${indicator}</p>`; // Assuming indicator is the text itself struggleIndicatorsDiv.appendChild(div); }); } else { struggleIndicatorsDiv.innerHTML = '<p class="text-gray-500 text-sm">No specific struggle indicators identified.</p>'; } // Main Topics mainTopicsDiv.innerHTML = ''; if (details.topics && details.topics.length > 0) { details.topics.forEach(topic => { const span = document.createElement('span'); span.className = 'inline-block bg-white px-2 py-1 rounded-md text-xs mr-1 mb-1'; span.textContent = topic; mainTopicsDiv.appendChild(span); }); } else { mainTopicsDiv.innerHTML = '<p class="text-gray-500 text-sm">No main topics identified.</p>'; } // Sentiment Trend (simplified mapping from main sentiment) const sentiment = details.sentiment?.label || 'N/A'; let sentimentTrendText = 'N/A'; let sentimentIconClass = 'fas fa-question text-gray-500'; let sentimentColorClass = 'text-gray-600'; if (sentiment.toLowerCase() === 'positive') { sentimentTrendText = 'Mostly Positive'; sentimentIconClass = 'fas fa-smile text-green-600'; sentimentColorClass = 'text-green-600'; } else if (sentiment.toLowerCase() === 'negative') { sentimentTrendText = 'Mostly Negative'; sentimentIconClass = 'fas fa-frown text-red-600'; sentimentColorClass = 'text-red-600'; } else if (sentiment.toLowerCase() === 'neutral') { sentimentTrendText = 'Neutral'; sentimentIconClass = 'fas fa-meh text-gray-500'; sentimentColorClass = 'text-gray-600'; } sentimentTrendSpan.textContent = sentimentTrendText; sentimentTrendSpan.className = `text-sm font-medium ${sentimentColorClass}`; sentimentTrendIcon.className = sentimentIconClass + ' text-sm'; // Key Learning keyLearningPara.textContent = details.key_learning || 'No key learning identified.'; // Store filename for download button in modal (if needed) // The current download button downloads ALL results, you'd need to add a new endpoint for single file download // Show modal analysisModal.classList.remove('hidden'); } catch (error) { console.error('Error fetching analysis details:', error); // Display error in modal if fetch fails modalSummary.textContent = `Error loading details: ${error.message}`; successIndicatorsDiv.innerHTML = ''; struggleIndicatorsDiv.innerHTML = ''; mainTopicsDiv.innerHTML = ''; sentimentTrendSpan.textContent = 'Error'; sentimentTrendIcon.className = 'fas fa-times-circle text-red-600 text-sm'; keyLearningPara.textContent = 'Could not load detailed analysis.'; analysisModal.classList.remove('hidden'); } } // Close modal closeModalBtn.addEventListener('click', function() { analysisModal.classList.add('hidden'); }); // Download results (calls backend endpoint) downloadBtn.addEventListener('click', function() { // The backend handles generating and sending the zip file window.location.href = '/download_results'; // This will trigger the download in the browser // Optional: Add visual feedback like a spinner or message alert('Generating and downloading results...'); // Simple feedback }); // Pause/stop buttons (call backend endpoints - functionality depends on backend implementation) pauseBtn.addEventListener('click', async function() { try { const response = await fetch('/pause_analysis', { method: 'POST' }); const result = await response.json(); console.log(result.message); alert(result.message); // UI might need updating to show paused state (backend status should handle this) } catch (error) { console.error('Error pausing analysis:', error); alert('Failed to pause analysis: ' + error.message); } }); stopBtn.addEventListener('click', async function() { const confirmStop = confirm("Are you sure you want to stop the analysis? Progress will be saved."); if (!confirmStop) return; try { const response = await fetch('/stop_analysis', { method: 'POST' }); const result = await response.json(); console.log(result.message); alert(result.message); // UI needs to update to indicate stopped state and potentially stop polling if (statusInterval) clearInterval(statusInterval); updateStatusDisplay(); // Final status update } catch (error) { console.error('Error stopping analysis:', error); alert('Failed to stop analysis: ' + error.message); } }); // Initial call to update status display on page load // This fetches the state in case the app was restarted or already had files document.addEventListener('DOMContentLoaded', () => { updateStatusDisplay(); }); // Helper function to update the chart areas with simple text/lists // This replaces the static HTML visualization simulation function updateVisualizations() { // This function is called by updateStatusDisplay when analysis is completed (or periodically) // Fetch aggregate data from the backend or use data from state.get_status() + results const status = state.get_status(); // Access state directly on frontend (not ideal) or fetch aggregate data // --- Sentiment Distribution (Simple Text Summary) --- const sentimentChartDiv = document.getElementById('sentimentChart'); const resultsList = state.get_results_list(); // Or fetch /results and process const sentimentCounts = resultsList.reduce((acc, res) => { const sentiment = res.sentiment || 'N/A'; acc[sentiment] = (acc[sentiment] || 0) + 1; return acc; }, {}); let sentimentHtml = '<h4 class="font-medium text-gray-900 mb-3">Sentiment Distribution</h4>'; if (resultsList.length === 0) { sentimentHtml += `<div class="text-center py-10 text-gray-400"> <i class="fas fa-chart-pie text-2xl mb-2"></i> <p class="text-sm">No sentiment data available yet</p> </div>`; } else { sentimentHtml += '<ul class="list-disc list-inside text-sm text-gray-700">'; for (const [sentiment, count] of Object.entries(sentimentCounts)) { let colorClass = 'text-gray-600'; if (sentiment.toLowerCase() === 'positive') colorClass = 'text-green-600'; else if (sentiment.toLowerCase() === 'negative') colorClass = 'text-red-600'; sentimentHtml += `<li class="${colorClass}">${sentiment}: ${count}</li>`; } sentimentHtml += '</ul>'; } sentimentChartDiv.innerHTML = sentimentHtml; // --- Top Topics (Simple List) --- const topicChartDiv = document.getElementById('topicChart'); const allTopics = resultsList.flatMap(res => res.topics || []).filter(topic => topic !== 'Topic extraction pipeline failed to load.'); const topicCounts = allTopics.reduce((acc, topic) => { acc[topic] = (acc[topic] || 0) + 1; return acc; }, {}); const sortedTopics = Object.entries(topicCounts).sort(([, a], [, b]) => b - a); let topicHtml = '<h4 class="font-medium text-gray-900 mb-3">Top Topics</h4>'; if (sortedTopics.length === 0) { topicHtml += `<div class="text-center py-10 text-gray-400"> <i class="fas fa-chart-bar text-2xl mb-2"></i> <p class="text-sm">No topic data available yet</p> </div>`; } else { topicHtml += '<ul class="list-disc list-inside text-sm text-gray-700">'; sortedTopics.slice(0, 5).forEach(([topic, count]) => { // Show top 5 topicHtml += `<li>${topic}: ${count}</li>`; }); topicHtml += '</ul>'; } topicChartDiv.innerHTML = topicHtml; } // Call update visualizations after results are updated, especially on completion // The updateStatusDisplay could call updateVisualizations periodically or on completion // For now, let's add a manual call example or call it when analysis is detected as completed. // A better approach is to fetch aggregate data from a backend endpoint dedicated to charts. // For this simplified version, let's update charts only when analysis completes. // (Modified the updateStatusDisplay to call updateVisualizations on "Completed") </script>