teste / src /ui /dark_theme_components.py
torxyton's picture
fix: Corrige tema escuro e melhora estabilidade Gradio
892b01b
"""Componentes de UI com suporte a tema escuro."""
from typing import Dict, Any
from config.config import AppConfig
from src.ui.theme_manager import theme_manager
from src.utils.utils import (
NumberUtils,
ConfidenceUtils,
ActionUtils,
SentimentUtils,
FormatUtils
)
class DarkThemeUIComponents:
"""Componentes da interface com suporte a tema escuro."""
@staticmethod
def create_header() -> str:
"""Cria o cabeçalho da aplicação com tema escuro."""
theme = theme_manager.get_current_theme()
colors = theme['colors']
return f"""
<div style="text-align: center; padding: 25px; background: linear-gradient(135deg, {colors['gradient_start']} 0%, {colors['gradient_end']} 100%); border-radius: 15px; margin-bottom: 25px; box-shadow: 0 8px 32px {colors['shadow']};">
<h1 style="color: white; margin: 0; font-size: 2.8em; text-shadow: 2px 2px 8px rgba(0,0,0,0.5); font-weight: 700;">
📈 {AppConfig.APP_TITLE}
</h1>
<p style="color: #f0f0f0; margin: 15px 0 0 0; font-size: 1.3em; opacity: 0.9;">
{AppConfig.APP_DESCRIPTION}
</p>
<div style="margin-top: 15px; padding: 8px 16px; background: rgba(255,255,255,0.1); border-radius: 20px; display: inline-block;">
<span style="color: white; font-size: 0.9em; font-weight: 500;">🌙 Tema Escuro Ativo</span>
</div>
</div>
"""
@staticmethod
def format_harmonic_patterns(analysis_result: Dict[str, Any]) -> str:
"""Formata padrões harmônicos com tema escuro."""
theme = theme_manager.get_current_theme()
colors = theme['colors']
harmonic_patterns = analysis_result.get('harmonic_patterns', [])
if not harmonic_patterns:
return f"""
<div style="background: {colors['card_background']}; border-radius: 12px; padding: 25px; border: 1px solid {colors['border']}; box-shadow: 0 4px 12px {colors['shadow']};">
<h3 style="color: {colors['text_primary']}; margin-top: 0; font-size: 1.4em; font-weight: 600;">🎵 Padrões Harmônicos</h3>
<div style="text-align: center; padding: 40px; color: {colors['text_muted']};">
<div style="font-size: 3em; margin-bottom: 15px; opacity: 0.6;">📊</div>
<p style="font-size: 1.1em;">Nenhum padrão harmônico detectado</p>
</div>
</div>
"""
patterns_html = ""
for pattern in harmonic_patterns:
pattern_name = pattern.get('name', 'Desconhecido')
confidence = pattern.get('confidence', 0)
direction = pattern.get('direction', 'NEUTRO')
direction_emoji = "📈" if direction == "ALTA" else "📉" if direction == "BAIXA" else "➡️"
direction_color = colors['success'] if direction == "ALTA" else colors['error'] if direction == "BAIXA" else colors['warning']
patterns_html += f"""
<div style="background: {colors['surface']}; border-radius: 10px; padding: 18px; margin-bottom: 12px; border-left: 4px solid {colors['info']}; transition: all 0.3s ease;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<div>
<strong style="color: {colors['text_primary']}; font-size: 1.1em;">{direction_emoji} {pattern_name}</strong>
<div style="color: {direction_color}; font-size: 0.95em; margin-top: 4px; font-weight: 500;">Direção: {direction}</div>
</div>
<div style="text-align: right;">
<div style="font-weight: bold; color: {colors['info']}; font-size: 1.2em;">{confidence}%</div>
<div style="color: {colors['text_secondary']}; font-size: 0.85em;">Confiança</div>
</div>
</div>
</div>
"""
return f"""
<div style="background: {colors['card_background']}; border-radius: 12px; padding: 25px; border: 1px solid {colors['border']}; box-shadow: 0 4px 12px {colors['shadow']};">
<h3 style="color: {colors['text_primary']}; margin-top: 0; border-bottom: 2px solid {colors['info']}; padding-bottom: 12px; font-size: 1.4em; font-weight: 600;">
🎵 Padrões Harmônicos Detectados
</h3>
{patterns_html}
</div>
"""
@staticmethod
def format_fibonacci_alerts(analysis_result: Dict[str, Any]) -> str:
"""Formata alertas de Fibonacci com tema escuro."""
theme = theme_manager.get_current_theme()
colors = theme['colors']
fibonacci_data = analysis_result.get('fibonacci_analysis', {})
levels = fibonacci_data.get('levels', [])
if not levels:
return f"""
<div style="background: {colors['card_background']}; border-radius: 12px; padding: 25px; border: 1px solid {colors['border']}; box-shadow: 0 4px 12px {colors['shadow']};">
<h3 style="color: {colors['text_primary']}; margin-top: 0; font-size: 1.4em; font-weight: 600;">📐 Níveis de Fibonacci</h3>
<div style="text-align: center; padding: 40px; color: {colors['text_muted']};">
<div style="font-size: 3em; margin-bottom: 15px; opacity: 0.6;">📊</div>
<p style="font-size: 1.1em;">Nenhum nível de Fibonacci calculado</p>
</div>
</div>
"""
levels_html = ""
for level in levels:
level_value = level.get('level', 0)
price = level.get('price', 0)
status = level.get('status', 'NEUTRO')
status_color = colors['success'] if status == 'SUPORTE' else colors['error'] if status == 'RESISTÊNCIA' else colors['text_secondary']
levels_html += f"""
<div style="display: flex; justify-content: space-between; align-items: center; padding: 12px 0; border-bottom: 1px solid {colors['border']}; transition: all 0.3s ease;">
<span style="color: {colors['text_secondary']}; font-weight: 500;">{level_value}%</span>
<span style="font-weight: bold; color: {colors['text_primary']}; font-size: 1.1em;">{NumberUtils.format_price(price)}</span>
<span style="color: {status_color}; font-weight: 600; padding: 4px 12px; background: {status_color}20; border-radius: 20px; font-size: 0.9em;">{status}</span>
</div>
"""
return f"""
<div style="background: {colors['card_background']}; border-radius: 12px; padding: 25px; border: 1px solid {colors['border']}; box-shadow: 0 4px 12px {colors['shadow']};">
<h3 style="color: {colors['text_primary']}; margin-top: 0; border-bottom: 2px solid {colors['warning']}; padding-bottom: 12px; font-size: 1.4em; font-weight: 600;">
📐 Níveis de Fibonacci
</h3>
<div style="background: {colors['surface']}; border-radius: 8px; padding: 15px;">
{levels_html}
</div>
</div>
"""
@staticmethod
def format_main_result(analysis_result: Dict[str, Any]) -> str:
"""Formata resultado principal com tema escuro."""
theme = theme_manager.get_current_theme()
colors = theme['colors']
action = analysis_result.get('action', 'AGUARDAR')
confidence = analysis_result.get('confidence', 0)
market_data = analysis_result.get('market_data', {})
price = market_data.get('price', 0)
variation = market_data.get('variation', 0)
# Formatação de preço e variação
formatted_price = NumberUtils.format_price(price)
formatted_variation = f"{variation:+.2f}%" if variation != 0 else "0.00%"
variation_color = colors['success'] if variation > 0 else colors['error'] if variation < 0 else colors['text_secondary']
# Nível de confiança
confidence_level = ConfidenceUtils.get_confidence_level(confidence)
confidence_bar = ConfidenceUtils.get_confidence_bar(confidence)
# Emojis e cores para ação
action_emojis = {
'COMPRAR': {'emoji': '🟢', 'color': colors['success'], 'action': 'COMPRAR'},
'VENDER': {'emoji': '🔴', 'color': colors['error'], 'action': 'VENDER'},
'AGUARDAR': {'emoji': '🟡', 'color': colors['warning'], 'action': 'AGUARDAR'}
}
action_info = action_emojis.get(action, action_emojis['AGUARDAR'])
return f"""
<div style="background: {colors['card_background']}; border-radius: 15px; padding: 30px; border: 1px solid {colors['border']}; box-shadow: 0 8px 24px {colors['shadow']};">
<div style="text-align: center; margin-bottom: 25px;">
<div style="font-size: 4em; margin-bottom: 10px;">{action_info['emoji']}</div>
<h2 style="color: {action_info['color']}; margin: 0; font-size: 2.2em; font-weight: 700; text-transform: uppercase; letter-spacing: 1px;">
{action_info['action']}
</h2>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 25px; margin-bottom: 25px;">
<div style="text-align: center; padding: 20px; background: {colors['surface']}; border-radius: 12px; box-shadow: 0 4px 8px {colors['shadow']};">
<div style="font-size: 0.95em; color: {colors['text_secondary']}; margin-bottom: 8px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">PREÇO ATUAL</div>
<div style="font-size: 2em; font-weight: bold; color: {colors['text_primary']}; margin-bottom: 5px;">{formatted_price}</div>
<div style="font-size: 1.2em; color: {variation_color}; font-weight: 600; padding: 4px 12px; background: {variation_color}20; border-radius: 20px;">{formatted_variation}</div>
</div>
<div style="text-align: center; padding: 20px; background: {colors['surface']}; border-radius: 12px; box-shadow: 0 4px 8px {colors['shadow']};">
<div style="font-size: 0.95em; color: {colors['text_secondary']}; margin-bottom: 8px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">CONFIANÇA</div>
<div style="font-size: 2em; font-weight: bold; color: {colors['text_primary']}; margin-bottom: 5px;">{confidence}%</div>
<div style="font-size: 0.95em; color: {colors['text_secondary']}; padding: 4px 12px; background: {colors['info']}20; border-radius: 20px;">{confidence_level}</div>
</div>
</div>
<div style="text-align: center; margin-top: 20px;">
<div style="font-size: 0.95em; color: {colors['text_secondary']}; margin-bottom: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">NÍVEL DE CONFIANÇA</div>
<div style="font-family: monospace; font-size: 1.3em; letter-spacing: 3px; color: {colors['info']}; background: {colors['surface']}; padding: 8px 16px; border-radius: 8px; display: inline-block;">{confidence_bar}</div>
</div>
</div>
"""
@staticmethod
def format_sentiment_analysis(analysis_result: Dict[str, Any]) -> str:
"""Formata análise de sentimento com tema escuro."""
theme = theme_manager.get_current_theme()
colors = theme['colors']
sentiment_data = analysis_result.get('sentiment_analysis', {})
label = sentiment_data.get('label', 'NEUTRO')
confidence = sentiment_data.get('confidence', 0)
# Configurações de sentimento
sentiment_config = SentimentUtils.get_sentiment_config(label)
emoji = sentiment_config['emoji']
color = colors['success'] if label == 'POSITIVO' else colors['error'] if label == 'NEGATIVO' else colors['warning']
return f"""
<div style="background: {colors['card_background']}; border-radius: 12px; padding: 25px; border: 1px solid {colors['border']}; box-shadow: 0 4px 12px {colors['shadow']};">
<h3 style="color: {colors['text_primary']}; margin-top: 0; border-bottom: 2px solid {colors['success']}; padding-bottom: 12px; font-size: 1.4em; font-weight: 600;">
🧠 Análise de Sentimento
</h3>
<div style="text-align: center; padding: 25px;">
<div style="font-size: 4em; margin-bottom: 20px;">{emoji}</div>
<h4 style="color: {color}; margin: 0; font-size: 1.8em; text-transform: uppercase; font-weight: 700; letter-spacing: 1px;">{label}</h4>
<div style="margin-top: 15px; color: {colors['text_secondary']}; font-size: 1.1em; font-weight: 500;">Confiança: {confidence:.1f}%</div>
</div>
<div style="background: {colors['surface']}; padding: 20px; border-radius: 10px; margin-top: 20px;">
<h5 style="margin-top: 0; color: {colors['text_primary']}; font-size: 1.1em; font-weight: 600;">📝 Detalhes da Análise</h5>
<p style="margin: 0; color: {colors['text_secondary']}; font-size: 1em; line-height: 1.6;">
O modelo de IA analisou o contexto do mercado e determinou um sentimento <strong style="color: {color};">{label.lower()}</strong>
com <strong style="color: {colors['info']};">{confidence:.1f}%</strong> de confiança.
</p>
</div>
</div>
"""
@staticmethod
def format_recommendations(analysis_result: Dict[str, Any]) -> str:
"""Formata recomendações com tema escuro."""
theme = theme_manager.get_current_theme()
colors = theme['colors']
action = analysis_result.get('action', 'AGUARDAR')
market_data = analysis_result.get('market_data', {})
price = market_data.get('price', 0)
# Recomendações de trading
trading_recs = FormatUtils.format_trading_recommendations(action, price)
# Direção de trading
direction = ActionUtils.get_trading_direction(action)
direction_emoji = "📈" if direction == "COMPRA" else "📉" if direction == "VENDA" else "⏸️"
direction_color = colors['success'] if direction == "COMPRA" else colors['error'] if direction == "VENDA" else colors['warning']
return f"""
<div style="background: {colors['card_background']}; border-radius: 12px; padding: 25px; border: 1px solid {colors['border']}; box-shadow: 0 4px 12px {colors['shadow']};">
<h3 style="color: {colors['text_primary']}; margin-top: 0; border-bottom: 2px solid {colors['warning']}; padding-bottom: 12px; font-size: 1.4em; font-weight: 600;">
💡 Recomendações de Trading
</h3>
<div style="background: {colors['warning']}20; border: 1px solid {colors['warning']}; border-radius: 10px; padding: 20px; margin-bottom: 25px;">
<div style="display: flex; align-items: center; gap: 15px; margin-bottom: 12px;">
<span style="font-size: 2em;">{direction_emoji}</span>
<strong style="color: {direction_color}; font-size: 1.3em; font-weight: 700;">Direção: {direction}</strong>
</div>
</div>
<div style="background: {colors['surface']}; padding: 20px; border-radius: 10px; white-space: pre-line; font-family: 'Segoe UI', sans-serif; line-height: 1.6; color: {colors['text_primary']};">
{trading_recs}
</div>
<div style="background: {colors['info']}20; border: 1px solid {colors['info']}; border-radius: 10px; padding: 20px; margin-top: 20px;">
<h5 style="margin-top: 0; color: {colors['info']}; font-size: 1.1em; font-weight: 600;">⚠️ Aviso Importante</h5>
<p style="margin: 0; color: {colors['text_secondary']}; font-size: 0.95em; line-height: 1.6;">
Esta análise é apenas para fins educacionais. Sempre faça sua própria pesquisa e
considere consultar um consultor financeiro antes de tomar decisões de investimento.
</p>
</div>
</div>
"""
@staticmethod
def _get_ai_status_html(available: bool, model_description: str = "") -> str:
"""Gera HTML para status da IA com tema escuro."""
theme = theme_manager.get_current_theme()
colors = theme['colors']
if available:
return f"""
<div style="background: {colors['success_background']}; border: 1px solid {colors['success_border']}; border-radius: 12px; padding: 15px; margin-bottom: 20px; box-shadow: 0 4px 12px {colors['shadow']};">
<div style="display: flex; align-items: center; gap: 12px;">
<span style="font-size: 1.3em;">🤖</span>
<div>
<strong style="color: {colors['success_text']}; font-size: 1.1em;">IA Ativa:</strong>
<span style="color: {colors['success_text']}; margin-left: 8px;">{model_description}</span>
</div>
</div>
</div>
"""
else:
return f"""
<div style="background: {colors['warning_background']}; border: 1px solid {colors['warning_border']}; border-radius: 12px; padding: 15px; margin-bottom: 20px; box-shadow: 0 4px 12px {colors['shadow']};">
<div style="display: flex; align-items: center; gap: 12px;">
<span style="font-size: 1.3em;">⚠️</span>
<div>
<strong style="color: {colors['warning_text']}; font-size: 1.1em;">IA Indisponível:</strong>
<span style="color: {colors['warning_text']}; margin-left: 8px;">Executando apenas análise técnica</span>
</div>
</div>
</div>
"""
@staticmethod
def create_footer(model_info: Dict[str, Any]) -> str:
"""Cria rodapé com tema escuro."""
theme = theme_manager.get_current_theme()
colors = theme['colors']
return f"""
<div style="text-align: center; padding: 20px; margin-top: 30px; background: {colors['surface']}; border-radius: 12px; border: 1px solid {colors['border']};">
<p style="color: {colors['text_secondary']}; margin: 0; font-size: 0.9em;">
🤖 Powered by <strong style="color: {colors['primary']};">FinBERT AI</strong> |
🌙 <strong style="color: {colors['text_primary']};">Tema Escuro</strong> |
📊 <strong style="color: {colors['info']};">Análise Financeira Avançada</strong>
</p>
</div>
"""