GuglielmoTor commited on
Commit
c57d01f
Β·
verified Β·
1 Parent(s): cf05908

Create ui_main_page_enhancements.py

Browse files
Files changed (1) hide show
  1. ui/ui_main_page_enhancements.py +709 -0
ui/ui_main_page_enhancements.py ADDED
@@ -0,0 +1,709 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ui_main_page_enhancements.py
2
+ import gradio as gr
3
+ import pandas as pd # Needed for state variables that hold DataFrames
4
+
5
+ # Custom CSS for professional, modern UI
6
+ CUSTOM_CSS = """
7
+ /* === ROOT VARIABLES === */
8
+ :root {
9
+ --primary-color: #0A66C2;
10
+ --primary-hover: #004182;
11
+ --secondary-color: #5E9ED6;
12
+ --accent-color: #F3F2EF;
13
+ --success-color: #57C4A3;
14
+ --warning-color: #F5B800;
15
+ --error-color: #CC1016;
16
+ --text-primary: #000000DE;
17
+ --text-secondary: #00000099;
18
+ --text-tertiary: #00000061;
19
+ --background-primary: #FFFFFF;
20
+ --background-secondary: #F8F9FA;
21
+ --background-tertiary: #F3F2EF;
22
+ --border-color: #E5E5E5;
23
+ --border-radius: 12px;
24
+ --shadow-light: 0 2px 8px rgba(0, 0, 0, 0.08);
25
+ --shadow-medium: 0 4px 16px rgba(0, 0, 0, 0.12);
26
+ --shadow-heavy: 0 8px 32px rgba(0, 0, 0, 0.16);
27
+ --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
28
+ }
29
+
30
+ /* === GLOBAL STYLES === */
31
+ .gradio-container {
32
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important;
33
+ min-height: 100vh;
34
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
35
+ }
36
+
37
+ .gradio-container .main {
38
+ background: transparent;
39
+ padding: 0;
40
+ max-width: none !important;
41
+ }
42
+
43
+ /* === MAIN HEADER === */
44
+ .main-header {
45
+ background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
46
+ color: white;
47
+ padding: 2rem 0;
48
+ margin: -1rem -1rem 2rem -1rem;
49
+ text-align: center;
50
+ box-shadow: var(--shadow-medium);
51
+ position: relative;
52
+ overflow: hidden;
53
+ }
54
+
55
+ .main-header::before {
56
+ content: '';
57
+ position: absolute;
58
+ top: 0;
59
+ left: 0;
60
+ right: 0;
61
+ bottom: 0;
62
+ background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
63
+ opacity: 0.3;
64
+ }
65
+
66
+ .main-header h1 {
67
+ font-size: 2.5rem !important;
68
+ font-weight: 700 !important;
69
+ margin: 0 !important;
70
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
71
+ position: relative;
72
+ z-index: 1;
73
+ }
74
+
75
+ .main-header .subtitle {
76
+ font-size: 1.1rem;
77
+ opacity: 0.9;
78
+ margin-top: 0.5rem;
79
+ position: relative;
80
+ z-index: 1;
81
+ }
82
+
83
+ /* === STATUS BAR === */
84
+ .status-container {
85
+ background: var(--background-primary);
86
+ border-radius: var(--border-radius);
87
+ padding: 1rem;
88
+ margin-bottom: 1.5rem;
89
+ box-shadow: var(--shadow-light);
90
+ border-left: 4px solid var(--primary-color);
91
+ }
92
+
93
+ /* === TAB CONTAINER === */
94
+ .tab-nav {
95
+ background: var(--background-primary);
96
+ border-radius: var(--border-radius);
97
+ padding: 0.5rem;
98
+ box-shadow: var(--shadow-light);
99
+ margin-bottom: 1.5rem;
100
+ border: none;
101
+ }
102
+
103
+ .tab-nav button {
104
+ background: transparent !important;
105
+ border: none !important;
106
+ padding: 1rem 1.5rem !important;
107
+ margin: 0 0.25rem !important;
108
+ border-radius: 8px !important;
109
+ font-weight: 600 !important;
110
+ font-size: 0.95rem !important;
111
+ transition: var(--transition) !important;
112
+ color: var(--text-secondary) !important;
113
+ position: relative;
114
+ }
115
+
116
+ .tab-nav button:hover {
117
+ background: var(--background-secondary) !important;
118
+ color: var(--text-primary) !important;
119
+ transform: translateY(-2px);
120
+ }
121
+
122
+ .tab-nav button.selected {
123
+ background: var(--primary-color) !important;
124
+ color: white !important;
125
+ box-shadow: var(--shadow-medium);
126
+ }
127
+
128
+ /* === TAB CONTENT === */
129
+ .tabitem {
130
+ background: var(--background-primary);
131
+ border-radius: var(--border-radius);
132
+ padding: 2rem;
133
+ box-shadow: var(--shadow-light);
134
+ border: 1px solid var(--border-color);
135
+ margin-top: 0;
136
+ }
137
+
138
+ /* === FORM ELEMENTS === */
139
+ .gr-form {
140
+ background: var(--background-primary);
141
+ border-radius: var(--border-radius);
142
+ padding: 1.5rem;
143
+ box-shadow: var(--shadow-light);
144
+ border: 1px solid var(--border-color);
145
+ }
146
+
147
+ input, textarea, select {
148
+ border: 2px solid var(--border-color) !important;
149
+ border-radius: 8px !important;
150
+ padding: 0.75rem 1rem !important;
151
+ font-size: 0.95rem !important;
152
+ transition: var(--transition) !important;
153
+ background: var(--background-primary) !important;
154
+ }
155
+
156
+ input:focus, textarea:focus, select:focus {
157
+ border-color: var(--primary-color) !important;
158
+ box-shadow: 0 0 0 3px rgba(10, 102, 194, 0.1) !important;
159
+ outline: none !important;
160
+ }
161
+
162
+ /* === BUTTONS === */
163
+ .gr-button {
164
+ background: var(--primary-color) !important;
165
+ color: white !important;
166
+ border: none !important;
167
+ border-radius: 8px !important;
168
+ padding: 0.75rem 1.5rem !important;
169
+ font-weight: 600 !important;
170
+ font-size: 0.95rem !important;
171
+ transition: var(--transition) !important;
172
+ cursor: pointer !important;
173
+ box-shadow: var(--shadow-light);
174
+ }
175
+
176
+ .gr-button:hover {
177
+ background: var(--primary-hover) !important;
178
+ transform: translateY(-2px);
179
+ box-shadow: var(--shadow-medium);
180
+ }
181
+
182
+ .gr-button.secondary {
183
+ background: var(--background-secondary) !important;
184
+ color: var(--text-primary) !important;
185
+ border: 2px solid var(--border-color) !important;
186
+ }
187
+
188
+ .gr-button.secondary:hover {
189
+ background: var(--background-tertiary) !important;
190
+ border-color: var(--primary-color) !important;
191
+ }
192
+
193
+ /* === CARDS === */
194
+ .card {
195
+ background: var(--background-primary);
196
+ border-radius: var(--border-radius);
197
+ padding: 1.5rem;
198
+ box-shadow: var(--shadow-light);
199
+ border: 1px solid var(--border-color);
200
+ transition: var(--transition);
201
+ margin-bottom: 1rem;
202
+ }
203
+
204
+ .card:hover {
205
+ box-shadow: var(--shadow-medium);
206
+ transform: translateY(-2px);
207
+ }
208
+
209
+ .card-header {
210
+ font-size: 1.25rem;
211
+ font-weight: 700;
212
+ color: var(--text-primary);
213
+ margin-bottom: 0.5rem;
214
+ display: flex;
215
+ align-items: center;
216
+ gap: 0.5rem;
217
+ }
218
+
219
+ .card-content {
220
+ color: var(--text-secondary);
221
+ line-height: 1.6;
222
+ }
223
+
224
+ /* === CHARTS AND PLOTS === */
225
+ .plot-container {
226
+ background: var(--background-primary);
227
+ border-radius: var(--border-radius);
228
+ padding: 1rem;
229
+ box-shadow: var(--shadow-light);
230
+ border: 1px solid var(--border-color);
231
+ margin: 1rem 0;
232
+ }
233
+
234
+ /* === MARKDOWN CONTENT === */
235
+ .markdown-content h1,
236
+ .markdown-content h2,
237
+ .markdown-content h3 {
238
+ color: var(--text-primary);
239
+ font-weight: 700;
240
+ margin-top: 1.5rem;
241
+ margin-bottom: 0.75rem;
242
+ }
243
+
244
+ .markdown-content h1 {
245
+ font-size: 2rem;
246
+ border-bottom: 3px solid var(--primary-color);
247
+ padding-bottom: 0.5rem;
248
+ }
249
+
250
+ .markdown-content h2 {
251
+ font-size: 1.5rem;
252
+ color: var(--primary-color);
253
+ }
254
+
255
+ .markdown-content h3 {
256
+ font-size: 1.25rem;
257
+ color: var(--secondary-color);
258
+ }
259
+
260
+ .markdown-content p {
261
+ line-height: 1.7;
262
+ color: var(--text-secondary);
263
+ margin-bottom: 1rem;
264
+ }
265
+
266
+ .markdown-content ul, .markdown-content ol {
267
+ padding-left: 1.5rem;
268
+ margin-bottom: 1rem;
269
+ }
270
+
271
+ .markdown-content li {
272
+ margin-bottom: 0.5rem;
273
+ line-height: 1.6;
274
+ color: var(--text-secondary);
275
+ }
276
+
277
+ .markdown-content code {
278
+ background: var(--background-tertiary);
279
+ padding: 0.2rem 0.4rem;
280
+ border-radius: 4px;
281
+ font-family: 'Fira Code', monospace;
282
+ font-size: 0.9rem;
283
+ }
284
+
285
+ .markdown-content pre {
286
+ background: var(--background-tertiary);
287
+ padding: 1rem;
288
+ border-radius: 8px;
289
+ overflow-x: auto;
290
+ margin: 1rem 0;
291
+ }
292
+
293
+ /* === LOADING STATES === */
294
+ .loading-spinner {
295
+ display: inline-block;
296
+ width: 20px;
297
+ height: 20px;
298
+ border: 3px solid rgba(10, 102, 194, 0.3);
299
+ border-radius: 50%;
300
+ border-top-color: var(--primary-color);
301
+ animation: spin 1s ease-in-out infinite;
302
+ }
303
+
304
+ @keyframes spin {
305
+ to { transform: rotate(360deg); }
306
+ }
307
+
308
+ /* === RESPONSIVE DESIGN === */
309
+ @media (max-width: 768px) {
310
+ .main-header h1 {
311
+ font-size: 2rem !important;
312
+ }
313
+
314
+ .tabitem {
315
+ padding: 1rem;
316
+ }
317
+
318
+ .tab-nav button {
319
+ padding: 0.75rem 1rem !important;
320
+ font-size: 0.9rem !important;
321
+ }
322
+ }
323
+
324
+ /* === ANIMATIONS === */
325
+ @keyframes fadeIn {
326
+ from { opacity: 0; transform: translateY(20px); }
327
+ to { opacity: 1; transform: translateY(0); }
328
+ }
329
+
330
+ .fade-in {
331
+ animation: fadeIn 0.6s ease-out;
332
+ }
333
+
334
+ /* === ACCESSIBILITY === */
335
+ .sr-only {
336
+ position: absolute;
337
+ width: 1px;
338
+ height: 1px;
339
+ padding: 0;
340
+ margin: -1px;
341
+ overflow: hidden;
342
+ clip: rect(0, 0, 0, 0);
343
+ white-space: nowrap;
344
+ border: 0;
345
+ }
346
+
347
+ /* === GRADIO SPECIFIC OVERRIDES === */
348
+ .gradio-container .wrap {
349
+ border-radius: var(--border-radius) !important;
350
+ border: 1px solid var(--border-color) !important;
351
+ }
352
+
353
+ .gradio-container .panel {
354
+ background: var(--background-primary) !important;
355
+ border-radius: var(--border-radius) !important;
356
+ }
357
+
358
+ .gradio-container .form {
359
+ background: transparent !important;
360
+ }
361
+
362
+ /* === STATUS INDICATORS === */
363
+ .status-success {
364
+ color: var(--success-color) !important;
365
+ background: rgba(87, 196, 163, 0.1) !important;
366
+ padding: 0.5rem 1rem;
367
+ border-radius: 6px;
368
+ border-left: 4px solid var(--success-color);
369
+ }
370
+
371
+ .status-warning {
372
+ color: var(--warning-color) !important;
373
+ background: rgba(245, 184, 0, 0.1) !important;
374
+ padding: 0.5rem 1rem;
375
+ border-radius: 6px;
376
+ border-left: 4px solid var(--warning-color);
377
+ }
378
+
379
+ .status-error {
380
+ color: var(--error-color) !important;
381
+ background: rgba(204, 16, 22, 0.1) !important;
382
+ padding: 0.5rem 1rem;
383
+ border-radius: 6px;
384
+ border-left: 4px solid var(--error-color);
385
+ }
386
+
387
+ /* === HIDE GRADIO FOOTER === */
388
+ footer {
389
+ display: none !important;
390
+ }
391
+
392
+ .footer {
393
+ display: none !important;
394
+ }
395
+
396
+ /* === IMPROVE SPACING === */
397
+ .block {
398
+ margin-bottom: 1.5rem !important;
399
+ }
400
+
401
+ /* === CUSTOM COMPONENTS === */
402
+ .metric-card {
403
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
404
+ color: white;
405
+ padding: 1.5rem;
406
+ border-radius: var(--border-radius);
407
+ text-align: center;
408
+ box-shadow: var(--shadow-medium);
409
+ margin: 0.5rem;
410
+ }
411
+
412
+ .metric-value {
413
+ font-size: 2rem;
414
+ font-weight: 700;
415
+ margin-bottom: 0.5rem;
416
+ }
417
+
418
+ .metric-label {
419
+ font-size: 0.9rem;
420
+ opacity: 0.9;
421
+ }
422
+ """
423
+
424
+ # Custom theme
425
+ custom_theme = gr.themes.Soft(
426
+ primary_hue="blue",
427
+ secondary_hue="sky",
428
+ neutral_hue="slate",
429
+ spacing_size="md",
430
+ radius_size="md"
431
+ ).set(
432
+ body_background_fill="linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)",
433
+ block_background_fill="white",
434
+ block_border_width="1px",
435
+ block_border_color="#e5e5e5",
436
+ block_radius="12px",
437
+ block_shadow="0 2px 8px rgba(0, 0, 0, 0.08)",
438
+ button_primary_background_fill="#0A66C2",
439
+ button_primary_background_fill_hover="#004182",
440
+ button_primary_text_color="white",
441
+ button_secondary_background_fill="#f8f9fa",
442
+ button_secondary_background_fill_hover="#f3f2ef",
443
+ input_border_color="#e5e5e5",
444
+ input_border_color_focus="#0A66C2",
445
+ input_border_width="2px"
446
+ )
447
+
448
+ def update_report_display_enhanced(selected_report_id: str, current_token_state: dict, format_report_for_display_func):
449
+ """
450
+ Updates the report header and body display when a new report is selected.
451
+ This function now expects format_report_for_display to return a dict with
452
+ 'header_html' and 'body_markdown'.
453
+
454
+ Args:
455
+ selected_report_id (str): The ID of the selected report.
456
+ current_token_state (dict): The current state dictionary containing data.
457
+ format_report_for_display_func (callable): The function to format report data.
458
+
459
+ Returns:
460
+ tuple: A tuple of gr.update objects for the header and body displays.
461
+ """
462
+ # Define empty states for header and body with improved styling
463
+ empty_header_html = """
464
+ <div class="card">
465
+ <div class="card-header">
466
+ πŸ“Š Comprehensive Analysis Report
467
+ </div>
468
+ <div class="card-content">
469
+ <p>AI-Generated Insights from Your LinkedIn Data</p>
470
+ <div class="status-badge">Generated from Bubble.io</div>
471
+ </div>
472
+ </div>
473
+ """
474
+ empty_body_markdown_no_selection = """
475
+ <div class="card" style="text-align: center; padding: 3rem;">
476
+ <div style="font-size: 3rem; margin-bottom: 1rem;">πŸ“‹</div>
477
+ <h3 style="color: var(--text-primary); margin-bottom: 0.5rem;">Select a Report</h3>
478
+ <p style="color: var(--text-secondary);">
479
+ Choose a report from the dropdown above to view its detailed analysis and insights.
480
+ </p>
481
+ </div>
482
+ """
483
+ empty_body_markdown_no_data = """
484
+ <div class="card" style="text-align: center; padding: 3rem;">
485
+ <div style="font-size: 3rem; margin-bottom: 1rem;">⚠️</div>
486
+ <h3 style="color: var(--warning-color); margin-bottom: 0.5rem;">Data Not Available</h3>
487
+ <p style="color: var(--text-secondary);">
488
+ Analysis data is not loaded or is empty. Please try refreshing the page.
489
+ </p>
490
+ </div>
491
+ """
492
+ empty_body_markdown_not_found = lambda _id: f"""
493
+ <div class="card" style="text-align: center; padding: 3rem;">
494
+ <div style="font-size: 3rem; margin-bottom: 1rem;">❌</div>
495
+ <h3 style="color: var(--error-color); margin-bottom: 0.5rem;">Report Not Found</h3>
496
+ <p style="color: var(--text-secondary);">
497
+ Report with ID '{_id}' was not found in the database.
498
+ </p>
499
+ </div>
500
+ """
501
+
502
+ if not selected_report_id:
503
+ return gr.update(value=empty_header_html), gr.update(value=empty_body_markdown_no_selection)
504
+
505
+ agentic_df = current_token_state.get("bubble_agentic_analysis_data")
506
+ if agentic_df is None or agentic_df.empty:
507
+ return gr.update(value=empty_header_html), gr.update(value=empty_body_markdown_no_data)
508
+
509
+ selected_report_series_df = agentic_df[agentic_df['_id'] == selected_report_id]
510
+ if selected_report_series_df.empty:
511
+ return gr.update(value=empty_header_html), gr.update(value=empty_body_markdown_not_found(selected_report_id))
512
+
513
+ selected_report_series = selected_report_series_df.iloc[0]
514
+
515
+ # Call the format_report_for_display, which now returns a dict
516
+ formatted_content_parts = format_report_for_display_func(selected_report_series)
517
+
518
+ # Update the two separate Gradio components
519
+ return (
520
+ gr.update(value=formatted_content_parts['header_html']),
521
+ gr.update(value=formatted_content_parts['body_markdown'])
522
+ )
523
+
524
+ def build_main_app_ui(
525
+ analytics_tab_instance,
526
+ AGENTIC_MODULES_LOADED,
527
+ build_home_tab_ui_func,
528
+ create_enhanced_report_tab_func,
529
+ create_enhanced_okr_tab_func,
530
+ format_report_for_display_func # New argument to pass the formatting function
531
+ ):
532
+ """
533
+ Builds the main Gradio application UI with enhanced styling and structure.
534
+
535
+ Args:
536
+ analytics_tab_instance (AnalyticsTab): An instance of the AnalyticsTab class.
537
+ AGENTIC_MODULES_LOADED (bool): Flag indicating if agentic modules are loaded.
538
+ build_home_tab_ui_func (callable): Function to build the Home tab UI.
539
+ create_enhanced_report_tab_func (callable): Function to create the enhanced Report tab.
540
+ create_enhanced_okr_tab_func (callable): Function to create the enhanced OKR tab.
541
+ format_report_for_display_func (callable): Function to format report data for display.
542
+
543
+ Returns:
544
+ tuple: A tuple containing:
545
+ - app (gr.Blocks): The main Gradio application object.
546
+ - url_user_token_display (gr.Textbox): Hidden component for user token.
547
+ - org_urn_display (gr.Textbox): Hidden component for organization URN.
548
+ - status_box (gr.Textbox): Component for system status.
549
+ - token_state (gr.State): Gradio state for token and data.
550
+ - reconstruction_cache_st (gr.State): State for reconstructed OKR data cache.
551
+ - enhanced_okr_display_html (gr.HTML): HTML component for enhanced OKR display.
552
+ - tabs (gr.Tabs): The main tabs component.
553
+ - report_selector_dd (gr.Dropdown): Dropdown for selecting reports.
554
+ - agentic_display_outputs (list): List of Gradio components for agentic results.
555
+ - analytics_tab_instance (AnalyticsTab): The analytics tab instance itself.
556
+ - chat_histories_st (gr.State): State for chat histories.
557
+ """
558
+ app = gr.Blocks(
559
+ theme=custom_theme,
560
+ css=CUSTOM_CSS,
561
+ title="LinkedIn Organization Dashboard",
562
+ head="""
563
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
564
+ <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
565
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
566
+ """
567
+ )
568
+
569
+ with app:
570
+ # --- STATE MANAGEMENT ---
571
+ token_state = gr.State(value={
572
+ "token": None, "client_id": None, "org_urn": None,
573
+ "bubble_posts_df": pd.DataFrame(), "bubble_post_stats_df": pd.DataFrame(),
574
+ "bubble_mentions_df": pd.DataFrame(), "bubble_follower_stats_df": pd.DataFrame(),
575
+ "bubble_agentic_analysis_data": pd.DataFrame(), # To store agentic results from Bubble
576
+ "url_user_token_temp_storage": None,
577
+ "config_date_col_posts": "published_at", "config_date_col_mentions": "date",
578
+ "config_date_col_followers": "date", "config_media_type_col": "media_type",
579
+ "config_eb_labels_col": "li_eb_label"
580
+ })
581
+
582
+ # States for analytics tab chatbot
583
+ chat_histories_st = gr.State({})
584
+ current_chat_plot_id_st = gr.State(None)
585
+ plot_data_for_chatbot_st = gr.State({})
586
+
587
+ # States for agentic results display
588
+ orchestration_raw_results_st = gr.State(None)
589
+ # KEPT for compatibility with load_and_display_agentic_results signature
590
+ key_results_for_selection_st = gr.State([])
591
+ selected_key_result_ids_st = gr.State([])
592
+
593
+ # --- NEW: Session-specific cache for reconstructed OKR data ---
594
+ reconstruction_cache_st = gr.State({})
595
+ # NEW: State to hold the actionable_okrs dictionary explicitly
596
+ actionable_okrs_data_st = gr.State({})
597
+
598
+ # --- UI LAYOUT ---
599
+ # Main Header
600
+ with gr.Row():
601
+ with gr.Column():
602
+ gr.HTML("""
603
+ <div class="main-header">
604
+ <h1>πŸš€ LinkedIn Organization Dashboard</h1>
605
+ <div class="subtitle">Advanced Analytics & AI-Powered Insights for LinkedIn Organizations</div>
606
+ </div>
607
+ """)
608
+
609
+ # Hidden components for token management
610
+ url_user_token_display = gr.Textbox(label="User Token (Hidden)", interactive=False, visible=False)
611
+ org_urn_display = gr.Textbox(label="Org URN (Hidden)", interactive=False, visible=False)
612
+
613
+ # Status section with better styling
614
+ with gr.Row():
615
+ with gr.Column():
616
+ gr.HTML('<div class="status-container">')
617
+ status_box = gr.Textbox(
618
+ label="πŸ”„ System Status",
619
+ interactive=False,
620
+ value="πŸ”„ Initializing dashboard...",
621
+ elem_classes=["status-display"]
622
+ )
623
+ gr.HTML('</div>')
624
+
625
+ with gr.Tabs(elem_classes=["tab-nav"]) as tabs:
626
+ # --- HOME TAB ---
627
+ with gr.TabItem("🏠 Home", id="tab_home", elem_classes=["tabitem"]):
628
+ # Call the new function from ui_generators to build the Home tab content
629
+ btn_graphs, btn_reports, btn_okr, btn_help = build_home_tab_ui_func()
630
+
631
+ # Link buttons to tab selection
632
+ btn_graphs.click(fn=lambda: gr.update(selected="tab_analytics_module"), outputs=tabs)
633
+ btn_reports.click(fn=lambda: gr.update(selected="tab_agentic_report"), outputs=tabs)
634
+ btn_okr.click(fn=lambda: gr.update(selected="tab_agentic_okrs"), outputs=tabs)
635
+ # btn_help.click(fn=lambda: gr.update(selected="tab_help"), outputs=tabs) # Uncomment if you add a help tab
636
+
637
+ # Analytics Tab
638
+ analytics_tab_instance.create_tab_ui() # This is the "Graphs" tab, assuming its ID is "tab_analytics_module"
639
+
640
+ # --- AGENTIC ANALYSIS REPORT TAB ---
641
+ with gr.TabItem("πŸ“Š Analysis Reports", id="tab_agentic_report", visible=AGENTIC_MODULES_LOADED, elem_classes=["tabitem"]):
642
+ # The create_enhanced_report_tab function handles the CSS and HTML structure
643
+ agentic_pipeline_status_md, report_selector_dd, report_header_html_display, report_body_markdown_display = \
644
+ create_enhanced_report_tab_func(AGENTIC_MODULES_LOADED)
645
+
646
+ # --- AGENTIC OKRS TAB ---
647
+ with gr.TabItem("🎯 OKRs & Tasks", id="tab_agentic_okrs", visible=AGENTIC_MODULES_LOADED, elem_classes=["tabitem"]):
648
+ gr.HTML("""
649
+ <div class="card">
650
+ <div class="card-header">
651
+ 🎯 AI Generated OKRs and Actionable Tasks
652
+ </div>
653
+ <div class="card-content">
654
+ <p>Based on AI analysis, the agent has proposed the following OKRs and actionable tasks from Bubble.io data.</p>
655
+ </div>
656
+ </div>
657
+ """)
658
+
659
+ if not AGENTIC_MODULES_LOADED:
660
+ gr.HTML("""
661
+ <div class="card" style="text-align: center; padding: 2rem;">
662
+ <div style="font-size: 2rem; margin-bottom: 1rem;">πŸ”΄</div>
663
+ <h3 style="color: var(--error-color);">Module Loading Error</h3>
664
+ <p>Agentic modules could not be loaded. This tab is currently unavailable.</p>
665
+ </div>
666
+ """)
667
+
668
+ # Keep the old components but make them invisible to maintain load_and_display_agentic_results signature
669
+ with gr.Column(visible=False):
670
+ gr.Markdown("### Suggested Key Results (OLD UI - HIDDEN)")
671
+ key_results_cbg = gr.CheckboxGroup(label="Select Key Results", choices=[], value=[], interactive=True)
672
+ gr.Markdown("### Detailed OKRs and Tasks (OLD UI - HIDDEN)")
673
+ okr_detail_display_md = gr.Markdown("I dettagli OKR appariranno qui.")
674
+
675
+ # NEW: Add the enhanced OKR display HTML component
676
+ enhanced_okr_display_html = create_enhanced_okr_tab_func()
677
+
678
+ # Add footer with enhanced styling
679
+ gr.HTML("""
680
+ <div style="text-align: center; padding: 2rem; margin-top: 3rem; border-top: 1px solid var(--border-color); color: var(--text-tertiary);">
681
+ <p>Β© 2024 LinkedIn Organization Dashboard - Powered by AI & Advanced Analytics</p>
682
+ <p style="font-size: 0.9rem; margin-top: 0.5rem;">
683
+ πŸš€ Built with Gradio β€’ πŸ”— LinkedIn API β€’ πŸ€– AI Analytics β€’ ☁️ Bubble.io Integration
684
+ </p>
685
+ </div>
686
+ """)
687
+
688
+ # Ensure agentic_display_outputs correctly maps to the newly created components
689
+ # This list must match the outputs of load_and_display_agentic_results
690
+ agentic_display_outputs = [
691
+ agentic_pipeline_status_md, # 0: Status Markdown (hidden)
692
+ report_selector_dd, # 1: Dropdown for selecting reports
693
+ key_results_cbg, # 2: Checkbox group for OKRs (kept hidden)
694
+ okr_detail_display_md, # 3: Markdown for detailed OKR display (kept hidden)
695
+ orchestration_raw_results_st, # 4: Raw results state
696
+ selected_key_result_ids_st, # 5: Selected KR IDs state (kept hidden)
697
+ key_results_for_selection_st, # 6: All KRs for selection state (kept hidden)
698
+ report_header_html_display, # 7: New HTML output for header
699
+ report_body_markdown_display, # 8: New Markdown output for body
700
+ reconstruction_cache_st, # 9: Reconstruction cache state
701
+ enhanced_okr_display_html, # 10: NEW: The enhanced HTML display for OKRs
702
+ actionable_okrs_data_st # 11: NEW: The actionable_okrs dictionary state
703
+ ]
704
+
705
+ return (app, url_user_token_display, org_urn_display, status_box,
706
+ token_state, reconstruction_cache_st, enhanced_okr_display_html,
707
+ tabs, report_selector_dd, agentic_display_outputs,
708
+ analytics_tab_instance, chat_histories_st,
709
+ format_report_for_display_func) # Return format_report_for_display_func as well