#ui_main_page_enhancements.py import gradio as gr import pandas as pd # Needed for state variables that hold DataFrames # Custom CSS for professional, modern UI CUSTOM_CSS = """ /* === ROOT VARIABLES === */ :root { --primary-color: #0A66C2; --primary-hover: #004182; --secondary-color: #5E9ED6; --accent-color: #F3F2EF; --success-color: #57C4A3; --warning-color: #F5B800; --error-color: #CC1016; --text-primary: #000000DE; --text-secondary: #00000099; --text-tertiary: #00000061; --background-primary: #FFFFFF; --background-secondary: #F8F9FA; --background-tertiary: #F3F2EF; --border-color: #E5E5E5; --border-radius: 12px; --shadow-light: 0 2px 8px rgba(0, 0, 0, 0.08); --shadow-medium: 0 4px 16px rgba(0, 0, 0, 0.12); --shadow-heavy: 0 8px 32px rgba(0, 0, 0, 0.16); --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } /* === GLOBAL STYLES === */ .gradio-container { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important; min-height: 100vh; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important; } .gradio-container .main { background: transparent; padding: 0; max-width: none !important; } /* === MAIN HEADER === */ .main-header { background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%); color: white; padding: 2rem 0; margin: -1rem -1rem 2rem -1rem; text-align: center; box-shadow: var(--shadow-medium); position: relative; overflow: hidden; } .main-header::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: url('data:image/svg+xml,'); opacity: 0.3; } .main-header h1 { font-size: 2.5rem !important; font-weight: 700 !important; margin: 0 !important; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); position: relative; z-index: 1; } .main-header .subtitle { font-size: 1.1rem; opacity: 0.9; margin-top: 0.5rem; position: relative; z-index: 1; } /* === STATUS BAR === */ .status-container { background: var(--background-primary); border-radius: var(--border-radius); padding: 1rem; margin-bottom: 1.5rem; box-shadow: var(--shadow-light); border-left: 4px solid var(--primary-color); } /* === TAB CONTAINER === */ .tab-nav { background: var(--background-primary); border-radius: var(--border-radius); padding: 0.5rem; box-shadow: var(--shadow-light); margin-bottom: 1.5rem; border: none; } .tab-nav button { background: transparent !important; border: none !important; padding: 1rem 1.5rem !important; margin: 0 0.25rem !important; border-radius: 8px !important; font-weight: 600 !important; font-size: 0.95rem !important; transition: var(--transition) !important; color: var(--text-secondary) !important; position: relative; } .tab-nav button:hover { background: var(--background-secondary) !important; color: var(--text-primary) !important; transform: translateY(-2px); } .tab-nav button.selected { background: var(--primary-color) !important; color: white !important; box-shadow: var(--shadow-medium); } /* === TAB CONTENT === */ .tabitem { background: var(--background-primary); border-radius: var(--border-radius); padding: 2rem; box-shadow: var(--shadow-light); border: 1px solid var(--border-color); margin-top: 0; } /* === FORM ELEMENTS === */ .gr-form { background: var(--background-primary); border-radius: var(--border-radius); padding: 1.5rem; box-shadow: var(--shadow-light); border: 1px solid var(--border-color); } input, textarea, select { border: 2px solid var(--border-color) !important; border-radius: 8px !important; padding: 0.75rem 1rem !important; font-size: 0.95rem !important; transition: var(--transition) !important; background: var(--background-primary) !important; } input:focus, textarea:focus, select:focus { border-color: var(--primary-color) !important; box-shadow: 0 0 0 3px rgba(10, 102, 194, 0.1) !important; outline: none !important; } /* === BUTTONS === */ .gr-button { background: var(--primary-color) !important; color: white !important; border: none !important; border-radius: 8px !important; padding: 0.75rem 1.5rem !important; font-weight: 600 !important; font-size: 0.95rem !important; transition: var(--transition) !important; cursor: pointer !important; box-shadow: var(--shadow-light); } .gr-button:hover { background: var(--primary-hover) !important; transform: translateY(-2px); box-shadow: var(--shadow-medium); } .gr-button.secondary { background: var(--background-secondary) !important; color: var(--text-primary) !important; border: 2px solid var(--border-color) !important; } .gr-button.secondary:hover { background: var(--background-tertiary) !important; border-color: var(--primary-color) !important; } /* === CARDS === */ .card { background: var(--background-primary); border-radius: var(--border-radius); padding: 1.5rem; box-shadow: var(--shadow-light); border: 1px solid var(--border-color); transition: var(--transition); margin-bottom: 1rem; } .card:hover { box-shadow: var(--shadow-medium); transform: translateY(-2px); } .card-header { font-size: 1.25rem; font-weight: 700; color: var(--text-primary); margin-bottom: 0.5rem; display: flex; align-items: center; gap: 0.5rem; } .card-content { color: var(--text-secondary); line-height: 1.6; } /* === CHARTS AND PLOTS === */ .plot-container { background: var(--background-primary); border-radius: var(--border-radius); padding: 1rem; box-shadow: var(--shadow-light); border: 1px solid var(--border-color); margin: 1rem 0; } /* === MARKDOWN CONTENT === */ .markdown-content h1, .markdown-content h2, .markdown-content h3 { color: var(--text-primary); font-weight: 700; margin-top: 1.5rem; margin-bottom: 0.75rem; } .markdown-content h1 { font-size: 2rem; border-bottom: 3px solid var(--primary-color); padding-bottom: 0.5rem; } .markdown-content h2 { font-size: 1.5rem; color: var(--primary-color); } .markdown-content h3 { font-size: 1.25rem; color: var(--secondary-color); } .markdown-content p { line-height: 1.7; color: var(--text-secondary); margin-bottom: 1rem; } .markdown-content ul, .markdown-content ol { padding-left: 1.5rem; margin-bottom: 1rem; } .markdown-content li { margin-bottom: 0.5rem; line-height: 1.6; color: var(--text-secondary); } .markdown-content code { background: var(--background-tertiary); padding: 0.2rem 0.4rem; border-radius: 4px; font-family: 'Fira Code', monospace; font-size: 0.9rem; } .markdown-content pre { background: var(--background-tertiary); padding: 1rem; border-radius: 8px; overflow-x: auto; margin: 1rem 0; } /* === LOADING STATES === */ .loading-spinner { display: inline-block; width: 20px; height: 20px; border: 3px solid rgba(10, 102, 194, 0.3); border-radius: 50%; border-top-color: var(--primary-color); animation: spin 1s ease-in-out infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* === RESPONSIVE DESIGN === */ @media (max-width: 768px) { .main-header h1 { font-size: 2rem !important; } .tabitem { padding: 1rem; } .tab-nav button { padding: 0.75rem 1rem !important; font-size: 0.9rem !important; } } /* === ANIMATIONS === */ @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .fade-in { animation: fadeIn 0.6s ease-out; } /* === ACCESSIBILITY === */ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } /* === GRADIO SPECIFIC OVERRIDES === */ .gradio-container .wrap { border-radius: var(--border-radius) !important; border: 1px solid var(--border-color) !important; } .gradio-container .panel { background: var(--background-primary) !important; border-radius: var(--border-radius) !important; } .gradio-container .form { background: transparent !important; } /* === STATUS INDICATORS === */ .status-success { color: var(--success-color) !important; background: rgba(87, 196, 163, 0.1) !important; padding: 0.5rem 1rem; border-radius: 6px; border-left: 4px solid var(--success-color); } .status-warning { color: var(--warning-color) !important; background: rgba(245, 184, 0, 0.1) !important; padding: 0.5rem 1rem; border-radius: 6px; border-left: 4px solid var(--warning-color); } .status-error { color: var(--error-color) !important; background: rgba(204, 16, 22, 0.1) !important; padding: 0.5rem 1rem; border-radius: 6px; border-left: 4px solid var(--error-color); } /* === HIDE GRADIO FOOTER === */ footer { display: none !important; } .footer { display: none !important; } /* === IMPROVE SPACING === */ .block { margin-bottom: 1.5rem !important; } /* === CUSTOM COMPONENTS === */ .metric-card { background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); color: white; padding: 1.5rem; border-radius: var(--border-radius); text-align: center; box-shadow: var(--shadow-medium); margin: 0.5rem; } .metric-value { font-size: 2rem; font-weight: 700; margin-bottom: 0.5rem; } .metric-label { font-size: 0.9rem; opacity: 0.9; } """ # Custom theme custom_theme = gr.themes.Soft( primary_hue="blue", secondary_hue="sky", neutral_hue="slate", spacing_size="md", radius_size="md" ).set( body_background_fill="linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)", block_background_fill="white", block_border_width="1px", block_border_color="#e5e5e5", block_radius="12px", block_shadow="0 2px 8px rgba(0, 0, 0, 0.08)", button_primary_background_fill="#0A66C2", button_primary_background_fill_hover="#004182", button_primary_text_color="white", button_secondary_background_fill="#f8f9fa", button_secondary_background_fill_hover="#f3f2ef", input_border_color="#e5e5e5", input_border_color_focus="#0A66C2", input_border_width="2px" ) def update_report_display_enhanced(selected_report_id: str, current_token_state: dict, format_report_for_display_func): """ Updates the report header and body display when a new report is selected. This function now expects format_report_for_display to return a dict with 'header_html' and 'body_markdown'. Args: selected_report_id (str): The ID of the selected report. current_token_state (dict): The current state dictionary containing data. format_report_for_display_func (callable): The function to format report data. Returns: tuple: A tuple of gr.update objects for the header and body displays. """ # Define empty states for header and body with improved styling empty_header_html = """
AI-Generated Insights from Your LinkedIn Data
Choose a report from the dropdown above to view its detailed analysis and insights.
Analysis data is not loaded or is empty. Please try refreshing the page.
Report with ID '{_id}' was not found in the database.
Based on AI analysis, the agent has proposed the following OKRs and actionable tasks from Bubble.io data.
Agentic modules could not be loaded. This tab is currently unavailable.
© 2024 LinkedIn Organization Dashboard - Powered by AI & Advanced Analytics
🚀 Built with Gradio • 🔗 LinkedIn API • 🤖 AI Analytics • ☁️ Bubble.io Integration