from nicegui import ui from database import AppConfig from core.state import state def show(): semi_auto_initial = AppConfig.get_val('semi_auto', 'false') == 'true' with ui.grid(columns=2).classes('w-full gap-4'): # --- COLUNA DA ESQUERDA: CONTROLES --- with ui.column().classes('w-full gap-4'): # Painel de Controle with ui.card().classes('w-full p-4 border-l-4 border-indigo-500'): ui.label('🎛️ Painel de Controle').classes('text-xl font-bold mb-4 text-gray-700') # Switches is_running = state.watcher.is_running if state.watcher else False def toggle_watcher(e): if state.watcher: state.watcher.is_running = e.value state.log(f"Comando: {'INICIAR' if e.value else 'PAUSAR'}") switch_run = ui.switch('Monitoramento Ativo', value=is_running, on_change=toggle_watcher).props('color=green size=lg') def toggle_semi_auto(e): AppConfig.set_val('semi_auto', str(e.value).lower()) state.log(f"⚠️ Semi-Auto: {e.value}") switch_auto = ui.switch('Modo Semi-Automático', value=semi_auto_initial, on_change=toggle_semi_auto).props('color=amber size=lg') # Botão Cancelar Global def cancel_task(): if state.watcher: state.watcher.abort_current_task() ui.notify('Cancelando...', type='warning') btn_cancel = ui.button('CANCELAR ATUAL', on_click=cancel_task, icon='cancel').props('color=red').classes('w-full mt-4 hidden') # Terminal de Logs with ui.card().classes('w-full h-64 bg-black text-green-400 font-mono text-xs p-2 overflow-hidden flex flex-col'): ui.label('>_ Logs').classes('text-gray-500 mb-1 border-b border-gray-800 w-full') log_container = ui.scroll_area().classes('flex-grow w-full') log_content = ui.label().style('white-space: pre-wrap; font-family: monospace;') with log_container: log_content.move(log_container) # --- COLUNA DA DIREITA: LISTA DE TAREFAS (Igual ao antigo) --- with ui.card().classes('w-full h-[80vh] bg-gray-50 flex flex-col p-0'): # Cabeçalho da Lista with ui.row().classes('w-full p-4 bg-white border-b items-center justify-between'): ui.label('📋 Fila de Processamento').classes('text-lg font-bold text-gray-700') lbl_status_top = ui.label('Ocioso').classes('text-sm text-gray-400') # Container da Lista (Onde a mágica acontece) tasks_container = ui.column().classes('w-full p-2 gap-2 overflow-y-auto flex-grow') # --- RENDERIZADOR DA LISTA --- def render_tasks(): tasks_container.clear() # Se não tiver tarefas if not state.tasks: with tasks_container: ui.label('Nenhuma atividade recente.').classes('text-gray-400 italic p-4') return # Itera sobre as tarefas (reversed para mais recentes no topo) for fname, data in reversed(state.tasks.items()): status = data['status'] pct = data['progress'] label = data['label'] # Estilo baseado no status (Igual ao seu código antigo) icon = 'circle'; color = 'grey'; spin = False bg_color = 'bg-white' if status == 'pending': icon = 'hourglass_empty'; color = 'grey' elif status == 'running': icon = 'sync'; color = 'blue'; spin = True bg_color = 'bg-blue-50 border-blue-200 border' elif status == 'warning': icon = 'warning'; color = 'orange' bg_color = 'bg-orange-50 border-orange-200 border' elif status == 'done': icon = 'check_circle'; color = 'green' elif status == 'error': icon = 'error'; color = 'red' elif status == 'skipped': icon = 'block'; color = 'red' with tasks_container: with ui.card().classes(f'w-full p-2 {bg_color} flex-row items-center gap-3'): # Ícone if spin: ui.spinner(size='sm').classes('text-blue-500') else: ui.icon(icon, color=color, size='sm') # Conteúdo with ui.column().classes('flex-grow gap-0'): ui.label(label).classes('font-bold text-sm text-gray-800 truncate') ui.label(fname).classes('text-xs text-gray-500 truncate') # Barra de Progresso (Só aparece se estiver rodando) if status == 'running': with ui.row().classes('w-full items-center gap-2 mt-1'): ui.linear_progress(value=pct/100, show_value=False).classes('h-2 rounded flex-grow') ui.label(f"{int(pct)}%").classes('text-xs font-bold text-blue-600') # --- LOOP DE ATUALIZAÇÃO --- def update_ui(): # 1. Logs logs = state.get_logs() log_content.set_text("\n".join(logs[-30:])) log_container.scroll_to(percent=1.0) # 2. Re-renderiza a lista de tarefas # Nota: O NiceGUI é eficiente, mas para listas muito grandes seria melhor atualizar in-place. # Como limitamos a 20 itens no state, limpar e redesenhar é rápido e seguro. render_tasks() # 3. Controles Globais if state.watcher and state.watcher.is_running: lbl_status_top.text = "Serviço Rodando" lbl_status_top.classes(replace='text-green-500') switch_run.value = True else: lbl_status_top.text = "Serviço Pausado" lbl_status_top.classes(replace='text-red-400') switch_run.value = False # 4. Botão Cancelar if state.current_file: btn_cancel.classes(remove='hidden') else: btn_cancel.classes(add='hidden') ui.timer(1.0, update_ui) # Atualiza a cada 1 segundo