From 4451cffdb5a1f12b575ab26766a7406943ff180e Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Mon, 15 Dec 2025 16:33:23 -0300 Subject: [PATCH] Rename tasks-sentient to tasks (make sentient theme default) --- ui/suite/base-layout-preview.html | 983 +++++++++++++++++++ ui/suite/base-layout.css | 798 ++++++++++++++++ ui/suite/base-layout.html | 98 ++ ui/suite/base-layout.js | 192 ++++ ui/suite/base.html | 265 ++++++ ui/suite/css/ai-panel.css | 392 ++++++++ ui/suite/drive/drive-sentient.html | 200 ++++ ui/suite/mail/mail-sentient.css | 217 +++++ ui/suite/mail/mail-sentient.html | 232 +++++ ui/suite/mail/mail-sentient.js | 203 ++++ ui/suite/tasks/tasks.css | 1289 ++++++++++++++++++------- ui/suite/tasks/tasks.html | 1403 +++++++++++----------------- ui/suite/tasks/tasks.js | 811 ++++++++++++---- 13 files changed, 5665 insertions(+), 1418 deletions(-) create mode 100644 ui/suite/base-layout-preview.html create mode 100644 ui/suite/base-layout.css create mode 100644 ui/suite/base-layout.html create mode 100644 ui/suite/base-layout.js create mode 100644 ui/suite/css/ai-panel.css create mode 100644 ui/suite/drive/drive-sentient.html create mode 100644 ui/suite/mail/mail-sentient.css create mode 100644 ui/suite/mail/mail-sentient.html create mode 100644 ui/suite/mail/mail-sentient.js diff --git a/ui/suite/base-layout-preview.html b/ui/suite/base-layout-preview.html new file mode 100644 index 0000000..540517d --- /dev/null +++ b/ui/suite/base-layout-preview.html @@ -0,0 +1,983 @@ + + + + + + BotUI Suite - Base Layout Preview + + + + +
+ +
+
+ + +
+ + + + + + + +
+
+ +
+ + +
+
+ + +
+ +
+ +
+
+
📊
+
+
Total Records
+
12,847
+
+
+
+
+
+
Active
+
8,234
+
+
+
+
+
+
Pending
+
2,156
+
+
+
+
📈
+
+
Growth
+
+24%
+
+
+
+ + +
+
+

Files Manager

+

Manage your documents and media files

+
+
+ + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeSizeModifiedStatusActions
project-report.pdfPDF Document2.4 MBDec 13, 2025Active +
+ + + +
+
dashboard-mockup.figFigma File8.1 MBDec 12, 2025Pending +
+ + + +
+
api-documentation.mdMarkdown156 KBDec 11, 2025Active +
+ + + +
+
backup-2025-12.zipArchive45.2 MBDec 10, 2025Archived +
+ + + +
+
user-analytics.csvSpreadsheet890 KBDec 9, 2025Active +
+ + + +
+
+ +
+
+ + + +
+
+ + + + diff --git a/ui/suite/base-layout.css b/ui/suite/base-layout.css new file mode 100644 index 0000000..2475094 --- /dev/null +++ b/ui/suite/base-layout.css @@ -0,0 +1,798 @@ +/* ============================================================================= + BOTUI SUITE - BASE LAYOUT CSS + Sentient Theme with AI Assistant Panel + ============================================================================= */ + +/* ============================================================================= + LAYOUT STRUCTURE + ============================================================================= */ + +.suite-app { + display: flex; + flex-direction: column; + min-height: 100vh; + background: var(--sentient-bg-primary); + color: var(--sentient-text-primary); + font-family: var(--sentient-font-family); +} + +/* ============================================================================= + TOP HEADER BAR + ============================================================================= */ + +.suite-topbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 16px; + background: var(--sentient-bg-primary); + border-bottom: 1px solid var(--sentient-border); + height: 52px; +} + +.topbar-left { + display: flex; + align-items: center; + gap: 16px; +} + +/* Navigation Tabs */ +.topbar-tabs { + display: flex; + gap: 2px; +} + +.topbar-tab { + padding: 8px 16px; + background: transparent; + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-sm); + color: var(--sentient-text-secondary); + font-family: var(--sentient-font-family); + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; +} + +.topbar-tab:first-child { + border-radius: var(--sentient-radius-sm) 0 0 var(--sentient-radius-sm); +} + +.topbar-tab:last-child { + border-radius: 0 var(--sentient-radius-sm) var(--sentient-radius-sm) 0; + border-left: none; +} + +.topbar-tab:hover { + background: var(--sentient-bg-tertiary); + color: var(--sentient-text-primary); +} + +.topbar-tab.active { + background: var(--sentient-bg-tertiary); + border-color: var(--sentient-accent); + color: var(--sentient-accent); +} + +/* App Launcher */ +.topbar-app-launcher { + display: flex; + align-items: center; + gap: 4px; + padding: 4px 8px; + background: var(--sentient-bg-secondary); + border-radius: var(--sentient-radius-lg); + margin-left: 16px; +} + +.app-icon { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: var(--sentient-bg-tertiary); + border: none; + border-radius: var(--sentient-radius-md); + color: var(--sentient-text-primary); + font-size: 18px; + cursor: pointer; + transition: all 0.2s ease; +} + +.app-icon:hover { + background: var(--sentient-bg-hover); + transform: scale(1.05); +} + +.app-icon.active { + background: var(--sentient-accent-dim); + color: var(--sentient-accent); +} + +/* Right Actions */ +.topbar-right { + display: flex; + align-items: center; + gap: 12px; +} + +.topbar-btn-primary { + display: flex; + align-items: center; + gap: 6px; + padding: 8px 16px; + background: var(--sentient-accent); + border: none; + border-radius: var(--sentient-radius-sm); + color: #000; + font-family: var(--sentient-font-family); + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +.topbar-btn-primary:hover { + background: #d4ff4a; + box-shadow: 0 0 20px var(--sentient-accent-glow); +} + +.topbar-btn-icon { + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-sm); + color: var(--sentient-text-secondary); + font-size: 16px; + cursor: pointer; + transition: all 0.2s ease; +} + +.topbar-btn-icon:hover { + background: var(--sentient-bg-tertiary); + color: var(--sentient-text-primary); +} + +/* ============================================================================= + MAIN CONTENT AREA + ============================================================================= */ + +.suite-main { + display: flex; + flex: 1; + overflow: hidden; +} + +/* Content Panel (Left) */ +.suite-content-panel { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + padding: 20px 24px; +} + +/* ============================================================================= + AI ASSISTANT PANEL (Right) + ============================================================================= */ + +.suite-ai-panel { + width: 320px; + display: flex; + flex-direction: column; + background: var(--sentient-bg-secondary); + border-left: 1px solid var(--sentient-border); +} + +.ai-panel-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + border-bottom: 1px solid var(--sentient-border); +} + +.ai-panel-title { + display: flex; + align-items: center; + gap: 12px; +} + +.ai-avatar { + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + background: var(--sentient-accent-dim); + border-radius: var(--sentient-radius-md); + font-size: 18px; +} + +.ai-panel-title h3 { + font-size: 14px; + font-weight: 600; + margin: 0; + color: var(--sentient-text-primary); +} + +.ai-panel-title .ai-status { + font-size: 11px; + color: var(--sentient-text-muted); + margin: 2px 0 0 0; +} + +.ai-panel-close { + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + border-radius: var(--sentient-radius-sm); + color: var(--sentient-text-muted); + font-size: 16px; + cursor: pointer; + transition: all 0.2s ease; +} + +.ai-panel-close:hover { + background: var(--sentient-bg-tertiary); + color: var(--sentient-text-primary); +} + +/* AI Messages */ +.ai-panel-messages { + flex: 1; + overflow-y: auto; + padding: 16px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.ai-message { + display: flex; + flex-direction: column; + gap: 8px; +} + +.ai-message.user { + align-items: flex-end; +} + +.ai-message.assistant { + align-items: flex-start; +} + +.ai-message-bubble { + max-width: 90%; + padding: 12px 14px; + border-radius: var(--sentient-radius-md); + font-size: 13px; + line-height: 1.5; +} + +.ai-message.user .ai-message-bubble { + background: var(--sentient-accent); + color: #000; + border-bottom-right-radius: 4px; +} + +.ai-message.assistant .ai-message-bubble { + background: var(--sentient-bg-tertiary); + color: var(--sentient-text-primary); + border-bottom-left-radius: 4px; +} + +.ai-message-action { + display: inline-block; + padding: 8px 12px; + background: var(--sentient-accent); + color: #000; + border-radius: var(--sentient-radius-sm); + font-size: 12px; + font-weight: 500; + text-decoration: none; + margin-top: 4px; + cursor: pointer; + transition: all 0.2s ease; +} + +.ai-message-action:hover { + background: #d4ff4a; +} + +.ai-typing-indicator { + display: flex; + gap: 4px; + padding: 12px 14px; + background: var(--sentient-bg-tertiary); + border-radius: var(--sentient-radius-md); + width: fit-content; +} + +.ai-typing-indicator span { + width: 8px; + height: 8px; + background: var(--sentient-text-muted); + border-radius: 50%; + animation: typing 1.4s infinite; +} + +.ai-typing-indicator span:nth-child(2) { + animation-delay: 0.2s; +} + +.ai-typing-indicator span:nth-child(3) { + animation-delay: 0.4s; +} + +@keyframes typing { + 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } + 30% { transform: translateY(-4px); opacity: 1; } +} + +/* Quick Actions */ +.ai-quick-actions { + padding: 12px 16px; + border-top: 1px solid var(--sentient-border); +} + +.quick-actions-label { + display: block; + font-size: 10px; + font-weight: 600; + color: var(--sentient-text-muted); + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 10px; +} + +.quick-actions-grid { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.quick-action-btn { + padding: 6px 10px; + background: var(--sentient-bg-tertiary); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-sm); + color: var(--sentient-text-secondary); + font-family: var(--sentient-font-family); + font-size: 11px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; +} + +.quick-action-btn:hover { + background: var(--sentient-bg-hover); + border-color: var(--sentient-accent); + color: var(--sentient-accent); +} + +/* AI Input */ +.ai-panel-input { + display: flex; + gap: 8px; + padding: 12px 16px; + border-top: 1px solid var(--sentient-border); +} + +.ai-input { + flex: 1; + padding: 10px 14px; + background: var(--sentient-bg-tertiary); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-md); + color: var(--sentient-text-primary); + font-family: var(--sentient-font-family); + font-size: 13px; + transition: all 0.2s ease; +} + +.ai-input::placeholder { + color: var(--sentient-text-muted); +} + +.ai-input:focus { + outline: none; + border-color: var(--sentient-accent); +} + +.ai-send-btn { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: var(--sentient-accent); + border: none; + border-radius: var(--sentient-radius-md); + color: #000; + font-size: 16px; + cursor: pointer; + transition: all 0.2s ease; +} + +.ai-send-btn:hover { + background: #d4ff4a; +} + +/* ============================================================================= + STAT CARDS + ============================================================================= */ + +.stat-cards { + display: flex; + gap: 12px; + margin-bottom: 20px; +} + +.stat-card { + flex: 1; + display: flex; + align-items: center; + gap: 12px; + padding: 16px; + background: var(--sentient-bg-card); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-md); +} + +.stat-card-icon { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: var(--sentient-bg-tertiary); + border-radius: var(--sentient-radius-sm); + font-size: 18px; +} + +.stat-card-content { + flex: 1; +} + +.stat-card-label { + font-size: 11px; + color: var(--sentient-text-muted); + text-transform: uppercase; + letter-spacing: 0.3px; + margin-bottom: 4px; +} + +.stat-card-value { + font-size: 20px; + font-weight: 700; + color: var(--sentient-text-primary); +} + +.stat-card.highlight { + border-color: var(--sentient-accent); +} + +.stat-card.highlight .stat-card-value { + color: var(--sentient-accent); +} + +/* ============================================================================= + APP HEADER (Title + Actions) + ============================================================================= */ + +.app-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 20px; +} + +.app-title-section h1 { + font-size: 22px; + font-weight: 700; + margin: 0 0 4px 0; +} + +.app-title-section p { + font-size: 13px; + color: var(--sentient-text-muted); + margin: 0; +} + +.app-actions { + display: flex; + gap: 10px; +} + +.app-btn-primary { + display: flex; + align-items: center; + gap: 6px; + padding: 10px 16px; + background: var(--sentient-accent); + border: none; + border-radius: var(--sentient-radius-sm); + color: #000; + font-family: var(--sentient-font-family); + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +.app-btn-primary:hover { + background: #d4ff4a; +} + +.app-btn-secondary { + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + background: var(--sentient-bg-tertiary); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-sm); + color: var(--sentient-text-secondary); + font-size: 16px; + cursor: pointer; + transition: all 0.2s ease; +} + +.app-btn-secondary:hover { + background: var(--sentient-bg-hover); + color: var(--sentient-text-primary); +} + +/* ============================================================================= + DATA TABLE + ============================================================================= */ + +.data-table-container { + flex: 1; + background: var(--sentient-bg-card); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-lg); + overflow: hidden; +} + +.data-table { + width: 100%; + border-collapse: collapse; +} + +.data-table thead { + background: var(--sentient-bg-tertiary); +} + +.data-table th { + padding: 12px 16px; + text-align: left; + font-size: 11px; + font-weight: 600; + color: var(--sentient-text-muted); + text-transform: uppercase; + letter-spacing: 0.5px; + border-bottom: 1px solid var(--sentient-border); +} + +.data-table td { + padding: 14px 16px; + font-size: 13px; + color: var(--sentient-text-primary); + border-bottom: 1px solid var(--sentient-border); +} + +.data-table tbody tr:hover { + background: var(--sentient-bg-tertiary); +} + +.data-table tbody tr:last-child td { + border-bottom: none; +} + +/* Status Badge */ +.status-badge { + display: inline-block; + padding: 4px 10px; + border-radius: 12px; + font-size: 11px; + font-weight: 600; +} + +.status-badge.active { + background: rgba(34, 197, 94, 0.15); + color: var(--sentient-success); +} + +.status-badge.pending { + background: rgba(245, 158, 11, 0.15); + color: var(--sentient-warning); +} + +.status-badge.inactive { + background: rgba(239, 68, 68, 0.15); + color: var(--sentient-error); +} + +/* Action Buttons */ +.table-actions { + display: flex; + gap: 6px; +} + +.table-action-btn { + padding: 6px 12px; + background: var(--sentient-bg-tertiary); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-sm); + color: var(--sentient-text-secondary); + font-family: var(--sentient-font-family); + font-size: 12px; + cursor: pointer; + transition: all 0.2s ease; +} + +.table-action-btn:hover { + background: var(--sentient-bg-hover); + color: var(--sentient-text-primary); +} + +.table-action-btn.delete { + background: rgba(239, 68, 68, 0.1); + border-color: rgba(239, 68, 68, 0.3); + color: var(--sentient-error); +} + +.table-action-btn.delete:hover { + background: rgba(239, 68, 68, 0.2); +} + +/* Pagination */ +.data-table-footer { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + background: var(--sentient-bg-tertiary); + border-top: 1px solid var(--sentient-border); +} + +.pagination-info { + font-size: 12px; + color: var(--sentient-text-muted); +} + +.pagination-controls { + display: flex; + align-items: center; + gap: 4px; +} + +.pagination-btn { + padding: 6px 12px; + background: var(--sentient-bg-card); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-sm); + color: var(--sentient-text-secondary); + font-family: var(--sentient-font-family); + font-size: 12px; + cursor: pointer; + transition: all 0.2s ease; +} + +.pagination-btn:hover { + background: var(--sentient-bg-hover); + color: var(--sentient-text-primary); +} + +.pagination-btn.active { + background: var(--sentient-accent); + border-color: var(--sentient-accent); + color: #000; +} + +/* ============================================================================= + SCROLLBAR + ============================================================================= */ + +.suite-app ::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +.suite-app ::-webkit-scrollbar-track { + background: var(--sentient-bg-secondary); +} + +.suite-app ::-webkit-scrollbar-thumb { + background: var(--sentient-bg-tertiary); + border-radius: 3px; +} + +.suite-app ::-webkit-scrollbar-thumb:hover { + background: var(--sentient-border-hover); +} + +/* ============================================================================= + AI PANEL COLLAPSIBLE STATE + ============================================================================= */ + +.suite-ai-panel.collapsed { + width: 0; + overflow: hidden; + border-left: none; +} + +.suite-ai-panel.collapsed * { + opacity: 0; + pointer-events: none; +} + +.ai-toggle { + transition: all 0.2s ease; +} + +.ai-toggle.active { + background: var(--sentient-accent-dim); + border-color: var(--sentient-accent); + color: var(--sentient-accent); +} + +/* ============================================================================= + RESPONSIVE + ============================================================================= */ + +@media (max-width: 1200px) { + .suite-ai-panel { + width: 280px; + } +} + +@media (max-width: 1024px) { + .suite-ai-panel { + position: fixed; + right: 0; + top: 52px; + bottom: 0; + width: 320px; + transform: translateX(0); + transition: transform 0.3s ease; + z-index: 100; + } + + .suite-ai-panel.collapsed { + transform: translateX(100%); + width: 320px; + } + + .topbar-app-launcher { + display: none; + } +} + +@media (max-width: 768px) { + .stat-cards { + flex-direction: column; + } + + .app-header { + flex-direction: column; + gap: 16px; + } + + .topbar-tabs { + display: none; + } +} diff --git a/ui/suite/base-layout.html b/ui/suite/base-layout.html new file mode 100644 index 0000000..6337956 --- /dev/null +++ b/ui/suite/base-layout.html @@ -0,0 +1,98 @@ + + + + + +
+ +
+ +
+ + + +
+ + + + + + + +
+
+ + +
+ + +
+
+ + +
+ +
+ +
+ + + +
+
+ + diff --git a/ui/suite/base-layout.js b/ui/suite/base-layout.js new file mode 100644 index 0000000..35f3802 --- /dev/null +++ b/ui/suite/base-layout.js @@ -0,0 +1,192 @@ +/* ============================================================================= + BOTUI SUITE - BASE LAYOUT JAVASCRIPT + Sentient Theme with AI Assistant Panel + ============================================================================= */ + +(function() { + 'use strict'; + + // ============================================================================= + // STATE + // ============================================================================= + + const state = { + aiPanelOpen: true, + currentApp: 'dashboard', + messages: [] + }; + + // ============================================================================= + // AI PANEL + // ============================================================================= + + window.toggleAIPanel = function() { + const panel = document.getElementById('ai-panel'); + if (panel) { + panel.classList.toggle('open'); + state.aiPanelOpen = panel.classList.contains('open'); + } + }; + + window.sendAIMessage = function() { + const input = document.getElementById('ai-input'); + if (!input || !input.value.trim()) return; + + const message = input.value.trim(); + input.value = ''; + + addMessage('user', message); + showTypingIndicator(); + + // Simulate AI response + setTimeout(() => { + hideTypingIndicator(); + addMessage('assistant', `Entendido! Vou processar sua solicitação: "${message}"`); + }, 1500); + }; + + function addMessage(type, content, action) { + const container = document.getElementById('ai-messages'); + if (!container) return; + + const messageEl = document.createElement('div'); + messageEl.className = `ai-message ${type}`; + + let html = `
${content}
`; + if (action) { + html += `${action}`; + } + + messageEl.innerHTML = html; + container.appendChild(messageEl); + container.scrollTop = container.scrollHeight; + + state.messages.push({ type, content, action }); + } + + function showTypingIndicator() { + const container = document.getElementById('ai-messages'); + if (!container) return; + + const indicator = document.createElement('div'); + indicator.className = 'ai-message assistant'; + indicator.id = 'typing-indicator'; + indicator.innerHTML = ` +
+ +
+ `; + container.appendChild(indicator); + container.scrollTop = container.scrollHeight; + } + + function hideTypingIndicator() { + const indicator = document.getElementById('typing-indicator'); + if (indicator) indicator.remove(); + } + + // ============================================================================= + // APP LAUNCHER + // ============================================================================= + + function initAppLauncher() { + document.querySelectorAll('.app-icon').forEach(icon => { + icon.addEventListener('click', function() { + const app = this.dataset.app; + switchApp(app); + }); + }); + } + + function switchApp(appName) { + document.querySelectorAll('.app-icon').forEach(icon => { + icon.classList.toggle('active', icon.dataset.app === appName); + }); + state.currentApp = appName; + + // Dispatch custom event for app switching + document.dispatchEvent(new CustomEvent('app-switch', { detail: { app: appName } })); + } + + // ============================================================================= + // NAVIGATION TABS + // ============================================================================= + + function initTabs() { + document.querySelectorAll('.topbar-tab').forEach(tab => { + tab.addEventListener('click', function() { + document.querySelectorAll('.topbar-tab').forEach(t => t.classList.remove('active')); + this.classList.add('active'); + }); + }); + } + + // ============================================================================= + // QUICK ACTIONS + // ============================================================================= + + function initQuickActions() { + document.querySelectorAll('.quick-action-btn').forEach(btn => { + btn.addEventListener('click', function() { + const action = this.textContent; + addMessage('user', action); + showTypingIndicator(); + + setTimeout(() => { + hideTypingIndicator(); + addMessage('assistant', `Ação "${action}" executada com sucesso!`, 'Ver alterações'); + }, 1000); + }); + }); + } + + // ============================================================================= + // KEYBOARD SHORTCUTS + // ============================================================================= + + function initKeyboardShortcuts() { + document.addEventListener('keydown', function(e) { + // Enter to send message in AI input + if (e.key === 'Enter' && document.activeElement.id === 'ai-input') { + e.preventDefault(); + sendAIMessage(); + } + + // Escape to close AI panel on mobile + if (e.key === 'Escape' && window.innerWidth <= 1024) { + const panel = document.getElementById('ai-panel'); + if (panel && panel.classList.contains('open')) { + toggleAIPanel(); + } + } + }); + } + + // ============================================================================= + // INITIAL MESSAGES + // ============================================================================= + + function loadInitialMessages() { + addMessage('assistant', 'Olá! Sou o AI Developer. Como posso ajudar você hoje?'); + addMessage('assistant', 'Você pode me pedir para modificar campos, alterar cores, adicionar validações ou qualquer outra mudança no sistema.'); + } + + // ============================================================================= + // INITIALIZE + // ============================================================================= + + function init() { + initAppLauncher(); + initTabs(); + initQuickActions(); + initKeyboardShortcuts(); + loadInitialMessages(); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } + +})(); diff --git a/ui/suite/base.html b/ui/suite/base.html index a898256..cee8d2b 100644 --- a/ui/suite/base.html +++ b/ui/suite/base.html @@ -12,6 +12,8 @@ + + @@ -728,6 +730,44 @@
{% block content %}{% endblock %}
+ + + + + +
@@ -1139,6 +1179,231 @@ 5000, ); }); + + // ================================================================= + // AI ASSISTANT PANEL + // ================================================================= + + // Quick actions per app + const aiQuickActions = { + drive: [ + { label: 'Upload file', action: 'upload_file' }, + { label: 'Create folder', action: 'create_folder' }, + { label: 'Search files', action: 'search_files' }, + { label: 'Share', action: 'share_item' } + ], + tasks: [ + { label: 'New task', action: 'create_task' }, + { label: 'Due today', action: 'show_due_today' }, + { label: 'Summary', action: 'tasks_summary' }, + { label: 'Priorities', action: 'show_priorities' } + ], + mail: [ + { label: 'Compose', action: 'compose_email' }, + { label: 'Unread', action: 'show_unread' }, + { label: 'Search', action: 'search_mail' }, + { label: 'Summary', action: 'mail_summary' } + ], + calendar: [ + { label: 'New event', action: 'create_event' }, + { label: 'Today', action: 'show_today' }, + { label: 'This week', action: 'show_week' }, + { label: 'Find time', action: 'find_free_time' } + ], + meet: [ + { label: 'Start call', action: 'start_meeting' }, + { label: 'Schedule', action: 'schedule_meeting' }, + { label: 'Join', action: 'join_meeting' } + ], + paper: [ + { label: 'New doc', action: 'create_document' }, + { label: 'Templates', action: 'show_templates' }, + { label: 'Recent', action: 'show_recent' } + ], + research: [ + { label: 'New search', action: 'new_research' }, + { label: 'Sources', action: 'show_sources' }, + { label: 'Citations', action: 'generate_citations' } + ], + sources: [ + { label: 'Add source', action: 'add_source' }, + { label: 'Import', action: 'import_sources' }, + { label: 'Categories', action: 'show_categories' } + ], + analytics: [ + { label: 'Dashboard', action: 'show_dashboard' }, + { label: 'Reports', action: 'show_reports' }, + { label: 'Export', action: 'export_data' } + ], + admin: [ + { label: 'Users', action: 'manage_users' }, + { label: 'Settings', action: 'show_settings' }, + { label: 'Logs', action: 'show_logs' } + ], + monitoring: [ + { label: 'Status', action: 'show_status' }, + { label: 'Alerts', action: 'show_alerts' }, + { label: 'Metrics', action: 'show_metrics' } + ], + default: [ + { label: 'Help', action: 'show_help' }, + { label: 'Shortcuts', action: 'show_shortcuts' }, + { label: 'Settings', action: 'open_settings' } + ] + }; + + // Get current app from URL or hash + function getCurrentApp() { + const hash = window.location.hash.replace('#', ''); + const path = window.location.pathname; + if (hash) return hash; + const match = path.match(/\/([a-z]+)\//); + return match ? match[1] : 'default'; + } + + // Update body data-app attribute + function updateCurrentApp() { + const app = getCurrentApp(); + document.body.setAttribute('data-app', app); + loadQuickActions(app); + } + + // Load quick actions for current app + function loadQuickActions(app) { + const container = document.getElementById('ai-quick-actions'); + if (!container) return; + + const actions = aiQuickActions[app] || aiQuickActions.default; + container.innerHTML = actions.map(a => + `` + ).join(''); + } + + // Handle quick action click + function handleQuickAction(action) { + const input = document.getElementById('ai-input'); + const actionMessages = { + upload_file: 'Help me upload a file', + create_folder: 'Create a new folder', + search_files: 'Search for files', + share_item: 'Help me share this item', + create_task: 'Create a new task', + show_due_today: 'Show tasks due today', + tasks_summary: 'Give me a summary of my tasks', + show_priorities: 'Show my priority tasks', + compose_email: 'Help me compose an email', + show_unread: 'Show unread emails', + search_mail: 'Search my emails', + mail_summary: 'Summarize my inbox', + create_event: 'Create a calendar event', + show_today: 'Show today\'s schedule', + show_week: 'Show this week\'s events', + find_free_time: 'Find free time slots', + start_meeting: 'Start a new meeting', + schedule_meeting: 'Schedule a meeting', + join_meeting: 'Join a meeting', + create_document: 'Create a new document', + show_templates: 'Show document templates', + show_recent: 'Show recent documents', + new_research: 'Start new research', + show_sources: 'Show my sources', + generate_citations: 'Generate citations', + add_source: 'Add a new source', + import_sources: 'Import sources', + show_categories: 'Show categories', + show_dashboard: 'Show analytics dashboard', + show_reports: 'Show reports', + export_data: 'Export analytics data', + manage_users: 'Manage users', + show_settings: 'Show admin settings', + show_logs: 'Show system logs', + show_status: 'Show system status', + show_alerts: 'Show active alerts', + show_metrics: 'Show performance metrics', + show_help: 'Help me get started', + show_shortcuts: 'Show keyboard shortcuts', + open_settings: 'Open settings' + }; + + if (input && actionMessages[action]) { + input.value = actionMessages[action]; + sendAIMessage(); + } + } + + // Toggle AI panel + function toggleAIPanel() { + document.body.classList.toggle('ai-panel-collapsed'); + localStorage.setItem('ai-panel-collapsed', document.body.classList.contains('ai-panel-collapsed')); + } + + // Send AI message + function sendAIMessage() { + const input = document.getElementById('ai-input'); + const messagesContainer = document.getElementById('ai-messages'); + const message = input?.value?.trim(); + + if (!message || !messagesContainer) return; + + // Add user message + const userMsg = document.createElement('div'); + userMsg.className = 'ai-message user'; + userMsg.innerHTML = `
${escapeHtml(message)}
`; + messagesContainer.appendChild(userMsg); + + // Clear input + input.value = ''; + + // Scroll to bottom + messagesContainer.scrollTop = messagesContainer.scrollHeight; + + // Show typing indicator + const typing = document.createElement('div'); + typing.className = 'ai-message assistant'; + typing.id = 'ai-typing'; + typing.innerHTML = `
`; + messagesContainer.appendChild(typing); + messagesContainer.scrollTop = messagesContainer.scrollHeight; + + // Simulate AI response (replace with actual API call) + setTimeout(() => { + typing.remove(); + const aiMsg = document.createElement('div'); + aiMsg.className = 'ai-message assistant'; + aiMsg.innerHTML = `
Entendi! Estou processando sua solicitação: "${escapeHtml(message)}". Como posso ajudar mais?
`; + messagesContainer.appendChild(aiMsg); + messagesContainer.scrollTop = messagesContainer.scrollHeight; + }, 1500); + } + + // Escape HTML + function escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + } + + // Restore AI panel state on load + function initAIPanel() { + const collapsed = localStorage.getItem('ai-panel-collapsed') === 'true'; + if (collapsed) { + document.body.classList.add('ai-panel-collapsed'); + } + updateCurrentApp(); + } + + // Initialize on DOM ready + document.addEventListener('DOMContentLoaded', initAIPanel); + + // Update app on navigation + document.body.addEventListener('htmx:afterSwap', function(e) { + if (e.detail.target.id === 'main-content') { + updateCurrentApp(); + } + }); + + // Also track hash changes + window.addEventListener('hashchange', updateCurrentApp); {% block scripts %}{% endblock %} diff --git a/ui/suite/css/ai-panel.css b/ui/suite/css/ai-panel.css new file mode 100644 index 0000000..822931d --- /dev/null +++ b/ui/suite/css/ai-panel.css @@ -0,0 +1,392 @@ +/* ============================================================================= + AI ASSISTANT PANEL - Global Styles + Collapsible panel that appears on all screens except Chat + ============================================================================= */ + +/* Main container adjustment */ +.app-main { + display: flex; + position: relative; +} + +#main-content { + flex: 1; + overflow: auto; + transition: margin-right 0.3s ease; +} + +/* When AI panel is open */ +body:not(.ai-panel-collapsed) #main-content { + margin-right: 320px; +} + +/* ============================================================================= + AI PANEL + ============================================================================= */ + +.ai-assistant-panel { + position: fixed; + right: 0; + top: 56px; /* Below header */ + bottom: 0; + width: 320px; + display: flex; + flex-direction: column; + background: var(--surface, #111111); + border-left: 1px solid var(--border, #2a2a2a); + z-index: 50; + transition: transform 0.3s ease; +} + +/* Collapsed state */ +body.ai-panel-collapsed .ai-assistant-panel { + transform: translateX(100%); +} + +body.ai-panel-collapsed #main-content { + margin-right: 0; +} + +/* Hide on Chat app */ +body[data-app="chat"] .ai-assistant-panel, +body[data-app="chat"] .ai-panel-fab { + display: none !important; +} + +/* ============================================================================= + AI PANEL HEADER + ============================================================================= */ + +.ai-panel-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + border-bottom: 1px solid var(--border, #2a2a2a); + background: var(--surface, #111111); +} + +.ai-panel-title { + display: flex; + align-items: center; + gap: 12px; +} + +.ai-panel-title .ai-avatar { + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + background: var(--primary-light, rgba(59, 130, 246, 0.15)); + border-radius: 10px; + font-size: 18px; +} + +.ai-panel-title h3 { + font-size: 14px; + font-weight: 600; + margin: 0; + color: var(--text, #ffffff); +} + +.ai-panel-title .ai-status { + font-size: 11px; + color: var(--text-secondary, #6b6b6b); + margin: 2px 0 0 0; +} + +.ai-panel-toggle { + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + border-radius: 6px; + color: var(--text-secondary, #6b6b6b); + font-size: 16px; + cursor: pointer; + transition: all 0.2s ease; +} + +.ai-panel-toggle:hover { + background: var(--surface-hover, #1a1a1a); + color: var(--text, #ffffff); +} + +/* ============================================================================= + AI MESSAGES + ============================================================================= */ + +.ai-panel-messages { + flex: 1; + overflow-y: auto; + padding: 16px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.ai-message { + display: flex; + flex-direction: column; + gap: 8px; +} + +.ai-message.user { + align-items: flex-end; +} + +.ai-message.assistant { + align-items: flex-start; +} + +.ai-message-bubble { + max-width: 90%; + padding: 12px 14px; + border-radius: 10px; + font-size: 13px; + line-height: 1.5; +} + +.ai-message.user .ai-message-bubble { + background: var(--primary, #3b82f6); + color: #ffffff; + border-bottom-right-radius: 4px; +} + +.ai-message.assistant .ai-message-bubble { + background: var(--surface-hover, #1a1a1a); + color: var(--text, #ffffff); + border-bottom-left-radius: 4px; +} + +.ai-message-action { + display: inline-block; + padding: 8px 12px; + background: var(--primary, #3b82f6); + color: #ffffff; + border-radius: 6px; + font-size: 12px; + font-weight: 500; + text-decoration: none; + cursor: pointer; + transition: all 0.2s ease; +} + +.ai-message-action:hover { + opacity: 0.9; +} + +.ai-typing-indicator { + display: flex; + gap: 4px; + padding: 12px 14px; + background: var(--surface-hover, #1a1a1a); + border-radius: 10px; + width: fit-content; +} + +.ai-typing-indicator span { + width: 8px; + height: 8px; + background: var(--text-secondary, #6b6b6b); + border-radius: 50%; + animation: ai-typing 1.4s infinite; +} + +.ai-typing-indicator span:nth-child(2) { + animation-delay: 0.2s; +} + +.ai-typing-indicator span:nth-child(3) { + animation-delay: 0.4s; +} + +@keyframes ai-typing { + 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } + 30% { transform: translateY(-4px); opacity: 1; } +} + +/* ============================================================================= + QUICK ACTIONS + ============================================================================= */ + +.ai-quick-actions { + padding: 12px 16px; + border-top: 1px solid var(--border, #2a2a2a); +} + +.ai-quick-actions .quick-actions-label { + display: block; + font-size: 10px; + font-weight: 600; + color: var(--text-secondary, #6b6b6b); + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 10px; +} + +.ai-quick-actions .quick-actions-grid { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.ai-quick-actions .quick-action-btn { + padding: 6px 10px; + background: var(--surface-hover, #1a1a1a); + border: 1px solid var(--border, #2a2a2a); + border-radius: 6px; + color: var(--text-secondary, #a1a1a1); + font-family: inherit; + font-size: 11px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; +} + +.ai-quick-actions .quick-action-btn:hover { + background: var(--surface, #111111); + border-color: var(--primary, #3b82f6); + color: var(--primary, #3b82f6); +} + +/* ============================================================================= + AI INPUT + ============================================================================= */ + +.ai-panel-input { + display: flex; + gap: 8px; + padding: 12px 16px; + border-top: 1px solid var(--border, #2a2a2a); + background: var(--surface, #111111); +} + +.ai-panel-input .ai-input { + flex: 1; + padding: 10px 14px; + background: var(--surface-hover, #1a1a1a); + border: 1px solid var(--border, #2a2a2a); + border-radius: 10px; + color: var(--text, #ffffff); + font-family: inherit; + font-size: 13px; + transition: all 0.2s ease; +} + +.ai-panel-input .ai-input::placeholder { + color: var(--text-secondary, #6b6b6b); +} + +.ai-panel-input .ai-input:focus { + outline: none; + border-color: var(--primary, #3b82f6); +} + +.ai-panel-input .ai-send-btn { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: var(--primary, #3b82f6); + border: none; + border-radius: 10px; + color: #ffffff; + font-size: 16px; + cursor: pointer; + transition: all 0.2s ease; +} + +.ai-panel-input .ai-send-btn:hover { + opacity: 0.9; +} + +/* ============================================================================= + FAB (Floating Action Button) - Toggle when collapsed + ============================================================================= */ + +.ai-panel-fab { + position: fixed; + right: 20px; + bottom: 20px; + width: 56px; + height: 56px; + display: none; + align-items: center; + justify-content: center; + background: var(--primary, #3b82f6); + border: none; + border-radius: 50%; + color: #ffffff; + font-size: 24px; + cursor: pointer; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + z-index: 100; + transition: all 0.2s ease; +} + +.ai-panel-fab:hover { + transform: scale(1.1); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4); +} + +body.ai-panel-collapsed .ai-panel-fab { + display: flex; +} + +/* ============================================================================= + SCROLLBAR + ============================================================================= */ + +.ai-panel-messages::-webkit-scrollbar { + width: 6px; +} + +.ai-panel-messages::-webkit-scrollbar-track { + background: var(--surface, #111111); +} + +.ai-panel-messages::-webkit-scrollbar-thumb { + background: var(--border, #2a2a2a); + border-radius: 3px; +} + +.ai-panel-messages::-webkit-scrollbar-thumb:hover { + background: var(--text-secondary, #6b6b6b); +} + +/* ============================================================================= + RESPONSIVE + ============================================================================= */ + +@media (max-width: 1024px) { + .ai-assistant-panel { + width: 100%; + max-width: 320px; + } + + body:not(.ai-panel-collapsed) #main-content { + margin-right: 0; + } +} + +@media (max-width: 768px) { + .ai-assistant-panel { + width: 100%; + max-width: none; + top: 56px; + } + + .ai-panel-fab { + right: 16px; + bottom: 16px; + width: 48px; + height: 48px; + font-size: 20px; + } +} diff --git a/ui/suite/drive/drive-sentient.html b/ui/suite/drive/drive-sentient.html new file mode 100644 index 0000000..f506705 --- /dev/null +++ b/ui/suite/drive/drive-sentient.html @@ -0,0 +1,200 @@ + + + + + + Drive - BotUI Suite + + + + + + +
+ +
+
+ + +
+ + + + + + + +
+
+ +
+ + + +
+
+ + +
+ +
+ +
+
+
💾
+
+
Storage Used
+
12.4 GB
+
+
+
+
📄
+
+
Files
+
1,847
+
+
+
+
📂
+
+
Folders
+
156
+
+
+
+
🔗
+
+
Shared
+
42
+
+
+
+ + +
+
+

Drive

+

Manage your files and folders

+
+
+ + + +
+
+ + + + + +
+ +
+
📁
+
Documents
+
12 items
+
+
+
📁
+
Images
+
45 items
+
+
+
📁
+
Source Code
+
89 items
+
+
+
📁
+
Backups
+
8 items
+
+ + +
+
📄
+
project-report.pdf
+
2.4 MB
+
+
+
🖼️
+
dashboard-mockup.png
+
1.8 MB
+
+
+
📊
+
analytics-2025.xlsx
+
890 KB
+
+
+
📝
+
meeting-notes.md
+
45 KB
+
+
+
🎬
+
demo-video.mp4
+
125 MB
+
+
+
📦
+
release-v2.0.zip
+
45 MB
+
+
+
+ + + +
+
+ + + + diff --git a/ui/suite/mail/mail-sentient.css b/ui/suite/mail/mail-sentient.css new file mode 100644 index 0000000..9ab40fc --- /dev/null +++ b/ui/suite/mail/mail-sentient.css @@ -0,0 +1,217 @@ +/* ============================================================================= + MAIL APP - SENTIENT THEME + ============================================================================= */ + +/* Search Box */ +.search-box { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 14px; + background: var(--sentient-bg-tertiary); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-md); + min-width: 250px; +} + +.search-icon { + font-size: 14px; + color: var(--sentient-text-muted); +} + +.search-input { + flex: 1; + background: transparent; + border: none; + color: var(--sentient-text-primary); + font-family: var(--sentient-font-family); + font-size: 13px; +} + +.search-input::placeholder { + color: var(--sentient-text-muted); +} + +.search-input:focus { + outline: none; +} + +/* Email List Container */ +.email-list-container { + flex: 1; + background: var(--sentient-bg-card); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-lg); + overflow: hidden; + display: flex; + flex-direction: column; +} + +.email-list { + flex: 1; + overflow-y: auto; +} + +/* Email Item */ +.email-item { + display: flex; + align-items: center; + gap: 12px; + padding: 14px 16px; + border-bottom: 1px solid var(--sentient-border); + cursor: pointer; + transition: all 0.2s ease; +} + +.email-item:hover { + background: var(--sentient-bg-tertiary); +} + +.email-item:last-child { + border-bottom: none; +} + +.email-item.unread { + background: rgba(197, 248, 42, 0.03); +} + +.email-item.unread .email-sender, +.email-item.unread .email-subject { + font-weight: 600; +} + +/* Email Checkbox */ +.email-checkbox { + width: 18px; + height: 18px; + accent-color: var(--sentient-accent); + cursor: pointer; +} + +/* Email Star */ +.email-star { + background: transparent; + border: none; + font-size: 16px; + cursor: pointer; + color: var(--sentient-text-muted); + transition: all 0.2s ease; +} + +.email-star:hover { + color: var(--sentient-warning); +} + +.email-star.starred { + color: var(--sentient-warning); +} + +/* Email Sender */ +.email-sender { + min-width: 140px; + font-size: 13px; + color: var(--sentient-text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Email Content */ +.email-content { + flex: 1; + display: flex; + align-items: center; + gap: 4px; + overflow: hidden; +} + +.email-subject { + font-size: 13px; + color: var(--sentient-text-primary); + white-space: nowrap; +} + +.email-preview { + font-size: 13px; + color: var(--sentient-text-muted); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Email Labels */ +.email-labels { + display: flex; + gap: 6px; + min-width: 80px; +} + +.email-label { + padding: 3px 8px; + border-radius: 10px; + font-size: 10px; + font-weight: 600; + text-transform: uppercase; +} + +.email-label.urgent { + background: rgba(239, 68, 68, 0.15); + color: var(--sentient-error); +} + +.email-label.meeting { + background: rgba(59, 130, 246, 0.15); + color: var(--sentient-info); +} + +.email-label.notification { + background: rgba(139, 92, 246, 0.15); + color: var(--sentient-paused); +} + +.email-label.work { + background: var(--sentient-accent-dim); + color: var(--sentient-accent); +} + +.email-label.success { + background: rgba(34, 197, 94, 0.15); + color: var(--sentient-success); +} + +/* Email Date */ +.email-date { + min-width: 60px; + font-size: 12px; + color: var(--sentient-text-muted); + text-align: right; +} + +/* Email List Footer */ +.email-list-footer { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + background: var(--sentient-bg-tertiary); + border-top: 1px solid var(--sentient-border); +} + +/* Mobile Responsive */ +@media (max-width: 768px) { + .email-sender { + min-width: 100px; + } + + .email-preview { + display: none; + } + + .email-labels { + display: none; + } + + .search-box { + min-width: 150px; + } +} diff --git a/ui/suite/mail/mail-sentient.html b/ui/suite/mail/mail-sentient.html new file mode 100644 index 0000000..a886200 --- /dev/null +++ b/ui/suite/mail/mail-sentient.html @@ -0,0 +1,232 @@ + + + + + + Email - BotUI Suite + + + + + + +
+ +
+
+ + +
+ + + + + + + +
+
+ +
+ + + +
+
+ + +
+ +
+ +
+
+
📥
+
+
Inbox
+
234
+
+
+
+
+
+
Starred
+
18
+
+
+
+
📤
+
+
Sent
+
156
+
+
+
+
📋
+
+
Drafts
+
5
+
+
+
+ + +
+
+

Email

+

Manage your inbox and communications

+
+
+ + + +
+
+ + + +
+ + + +
+
+ + + + diff --git a/ui/suite/mail/mail-sentient.js b/ui/suite/mail/mail-sentient.js new file mode 100644 index 0000000..fc41fcf --- /dev/null +++ b/ui/suite/mail/mail-sentient.js @@ -0,0 +1,203 @@ +/* ============================================================================= + MAIL APP - SENTIENT THEME JAVASCRIPT + ============================================================================= */ + +(function() { + 'use strict'; + + // ============================================================================= + // AI PANEL (Collapsible, Mobile-friendly) + // ============================================================================= + + window.toggleAIPanel = function() { + const panel = document.getElementById('ai-panel'); + const toggle = document.querySelector('.ai-toggle'); + if (panel) { + panel.classList.toggle('collapsed'); + if (toggle) { + toggle.classList.toggle('active', !panel.classList.contains('collapsed')); + } + localStorage.setItem('aiPanelCollapsed', panel.classList.contains('collapsed')); + } + }; + + window.sendAIMessage = function() { + const input = document.getElementById('ai-input'); + if (!input || !input.value.trim()) return; + + const message = input.value.trim(); + input.value = ''; + + addMessage('user', message); + showTypingIndicator(); + + setTimeout(() => { + hideTypingIndicator(); + addMessage('assistant', `Processando: "${message}". Como posso ajudar mais?`); + }, 1500); + }; + + window.aiAction = function(action) { + const actions = { + 'summarize': 'Resumindo o email selecionado...', + 'reply': 'Gerando uma resposta profissional...', + 'translate': 'Traduzindo o conteúdo...', + 'organize': 'Organizando sua caixa de entrada...' + }; + + addMessage('assistant', actions[action] || 'Processando...'); + + setTimeout(() => { + addMessage('assistant', 'Ação concluída com sucesso! O que mais posso fazer?'); + }, 2000); + }; + + function addMessage(type, content) { + const container = document.getElementById('ai-messages'); + if (!container) return; + + const messageEl = document.createElement('div'); + messageEl.className = `ai-message ${type}`; + messageEl.innerHTML = `
${content}
`; + container.appendChild(messageEl); + container.scrollTop = container.scrollHeight; + } + + function showTypingIndicator() { + const container = document.getElementById('ai-messages'); + if (!container) return; + + const indicator = document.createElement('div'); + indicator.className = 'ai-message assistant'; + indicator.id = 'typing-indicator'; + indicator.innerHTML = ` +
+ +
+ `; + container.appendChild(indicator); + container.scrollTop = container.scrollHeight; + } + + function hideTypingIndicator() { + const indicator = document.getElementById('typing-indicator'); + if (indicator) indicator.remove(); + } + + // ============================================================================= + // EMAIL FUNCTIONS + // ============================================================================= + + window.composeEmail = function() { + addMessage('assistant', 'Abrindo composer de email. Deseja que eu escreva um rascunho?'); + }; + + function initEmailList() { + document.querySelectorAll('.email-item').forEach(item => { + item.addEventListener('click', function(e) { + if (e.target.type === 'checkbox' || e.target.classList.contains('email-star')) return; + + document.querySelectorAll('.email-item').forEach(i => i.classList.remove('selected')); + this.classList.add('selected'); + this.classList.remove('unread'); + }); + }); + + document.querySelectorAll('.email-star').forEach(star => { + star.addEventListener('click', function(e) { + e.stopPropagation(); + this.classList.toggle('starred'); + this.textContent = this.classList.contains('starred') ? '⭐' : '☆'; + }); + }); + } + + // ============================================================================= + // APP NAVIGATION + // ============================================================================= + + function initAppLauncher() { + document.querySelectorAll('.app-icon').forEach(icon => { + icon.addEventListener('click', function() { + const app = this.dataset.app; + if (app === 'chat') { + window.location.href = '/suite/chat/chat-sentient.html'; + } else if (app === 'drive') { + window.location.href = '/suite/drive/drive-sentient.html'; + } else if (app === 'tasks') { + window.location.href = '/suite/tasks/tasks-sentient.html'; + } else if (app === 'calendar') { + window.location.href = '/suite/calendar/calendar-sentient.html'; + } else if (app === 'meet') { + window.location.href = '/suite/meet/meet-sentient.html'; + } else if (app === 'paper') { + window.location.href = '/suite/paper/paper-sentient.html'; + } + }); + }); + } + + function initTabs() { + document.querySelectorAll('.topbar-tab').forEach(tab => { + tab.addEventListener('click', function() { + document.querySelectorAll('.topbar-tab').forEach(t => t.classList.remove('active')); + this.classList.add('active'); + }); + }); + } + + // ============================================================================= + // KEYBOARD SHORTCUTS + // ============================================================================= + + function initKeyboardShortcuts() { + document.addEventListener('keydown', function(e) { + if (e.key === 'Enter' && document.activeElement.id === 'ai-input') { + e.preventDefault(); + sendAIMessage(); + } + + // Ctrl+Shift+A to toggle AI panel + if (e.ctrlKey && e.shiftKey && e.key === 'A') { + e.preventDefault(); + toggleAIPanel(); + } + }); + } + + // ============================================================================= + // RESTORE AI PANEL STATE + // ============================================================================= + + function restoreAIPanelState() { + const collapsed = localStorage.getItem('aiPanelCollapsed') === 'true'; + const panel = document.getElementById('ai-panel'); + const toggle = document.querySelector('.ai-toggle'); + + if (panel && collapsed) { + panel.classList.add('collapsed'); + } + if (toggle) { + toggle.classList.toggle('active', !collapsed); + } + } + + // ============================================================================= + // INITIALIZE + // ============================================================================= + + function init() { + initEmailList(); + initAppLauncher(); + initTabs(); + initKeyboardShortcuts(); + restoreAIPanelState(); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } + +})(); diff --git a/ui/suite/tasks/tasks.css b/ui/suite/tasks/tasks.css index d7c1d84..6b45529 100644 --- a/ui/suite/tasks/tasks.css +++ b/ui/suite/tasks/tasks.css @@ -1,484 +1,1049 @@ -/* Tasks page styles */ +/* ============================================================================= + TASKS APP + Automated Intelligent Task Management Interface + ============================================================================= */ -/* Container */ -.tasks-container { - max-width: 800px; - margin: 0 auto; - padding: 2rem; - min-height: 100vh; -} +/* ============================================================================= + LAYOUT STRUCTURE + ============================================================================= */ -/* Header */ -.tasks-header { - margin-bottom: 2rem; -} - -.header-content { - display: flex; - justify-content: space-between; - align-items: center; -} - -.tasks-title { - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 2rem; - color: #f1f5f9; -} - -.tasks-icon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 48px; - height: 48px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 12px; - font-size: 1.5rem; -} - -.header-stats { - display: flex; - gap: 2rem; -} - -.stat-item { +.tasks-app { display: flex; flex-direction: column; - align-items: center; + min-height: 100vh; + background: var(--sentient-bg-primary); + color: var(--sentient-text-primary); + font-family: var(--sentient-font-family); } -.stat-value { - font-size: 1.5rem; - font-weight: 700; - color: #f1f5f9; -} +/* ============================================================================= + TOP HEADER BAR + ============================================================================= */ -.stat-label { - font-size: 0.75rem; - text-transform: uppercase; - color: #64748b; - margin-top: 0.25rem; -} - -/* Add Task Section */ -.add-task-section { - background: linear-gradient(135deg, #1e293b 0%, #334155 100%); - border-radius: 16px; - padding: 1.5rem; - margin-bottom: 2rem; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); -} - -.add-task-form { +.tasks-topbar { display: flex; - gap: 0.75rem; align-items: center; + justify-content: space-between; + padding: 16px 24px; + background: var(--sentient-bg-secondary); + border-bottom: 1px solid var(--sentient-border); +} + +.topbar-left { + display: flex; + align-items: center; + gap: 24px; +} + +.topbar-logo { + display: flex; + align-items: center; + gap: 10px; + font-size: 18px; + font-weight: 700; + color: var(--sentient-text-primary); +} + +.topbar-logo-icon { + width: 32px; + height: 32px; + background: var(--sentient-accent); + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + color: #000; + font-size: 16px; +} + +.topbar-nav { + display: flex; + gap: 4px; +} + +.topbar-nav-item { + padding: 8px 16px; + background: transparent; + border: none; + border-radius: var(--sentient-radius-sm); + color: var(--sentient-text-secondary); + font-family: var(--sentient-font-family); + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; +} + +.topbar-nav-item:hover { + background: var(--sentient-bg-tertiary); + color: var(--sentient-text-primary); +} + +.topbar-nav-item.active { + background: var(--sentient-bg-tertiary); + color: var(--sentient-accent); +} + +.topbar-center { + flex: 1; + max-width: 480px; + margin: 0 24px; +} + +.topbar-search { + position: relative; + width: 100%; +} + +.topbar-search-icon { + position: absolute; + left: 14px; + top: 50%; + transform: translateY(-50%); + color: var(--sentient-text-muted); + font-size: 16px; + pointer-events: none; +} + +.topbar-search-input { + width: 100%; + padding: 10px 16px 10px 44px; + background: var(--sentient-bg-tertiary); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-md); + color: var(--sentient-text-primary); + font-size: 14px; + transition: all 0.2s ease; +} + +.topbar-search-input::placeholder { + color: var(--sentient-text-muted); +} + +.topbar-search-input:focus { + outline: none; + border-color: var(--sentient-accent); + box-shadow: 0 0 0 3px var(--sentient-accent-dim); +} + +.topbar-right { + display: flex; + align-items: center; + gap: 12px; +} + +.topbar-icon-btn { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-sm); + color: var(--sentient-text-secondary); + font-size: 18px; + cursor: pointer; + transition: all 0.2s ease; + position: relative; +} + +.topbar-icon-btn:hover { + background: var(--sentient-bg-tertiary); + color: var(--sentient-text-primary); +} + +.topbar-icon-btn .notification-dot { + position: absolute; + top: 8px; + right: 8px; + width: 8px; + height: 8px; + background: var(--sentient-error); + border-radius: 50%; +} + +.topbar-profile { + display: flex; + align-items: center; + gap: 10px; + padding: 6px 12px 6px 6px; + background: var(--sentient-bg-tertiary); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-md); + cursor: pointer; + transition: all 0.2s ease; +} + +.topbar-profile:hover { + border-color: var(--sentient-border-hover); +} + +.topbar-avatar { + width: 28px; + height: 28px; + background: linear-gradient(135deg, var(--sentient-accent), #a5d622); + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + color: #000; + font-size: 12px; + font-weight: 600; +} + +.topbar-profile-name { + font-size: 13px; + font-weight: 500; + color: var(--sentient-text-primary); +} + +.topbar-profile-arrow { + color: var(--sentient-text-muted); + font-size: 12px; +} + +/* ============================================================================= + MAIN CONTENT AREA + ============================================================================= */ + +.tasks-main { + display: flex; + flex: 1; + overflow: hidden; +} + +/* ============================================================================= + TASK LIST PANEL (LEFT) + ============================================================================= */ + +.tasks-list-panel { + flex: 1; + display: flex; + flex-direction: column; + border-right: 1px solid var(--sentient-border); + overflow: hidden; +} + +.tasks-list-header { + padding: 20px 24px; + border-bottom: 1px solid var(--sentient-border); +} + +.tasks-list-title { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; +} + +.tasks-list-title h1 { + font-size: 24px; + font-weight: 700; + color: var(--sentient-text-primary); + margin: 0; +} + +.tasks-count { + font-size: 14px; + color: var(--sentient-text-muted); +} + +/* Status Filter Pills */ +.status-filters { + display: flex; + gap: 8px; flex-wrap: wrap; } -.task-input { - flex: 1; - min-width: 200px; - padding: 0.75rem 1rem; - background: rgba(15, 23, 42, 0.5); - border: 1px solid #475569; - border-radius: 8px; - color: #f1f5f9; - font-size: 1rem; - transition: all 0.2s; -} - -.task-input:focus { - outline: none; - border-color: #667eea; - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); -} - -.task-category-select, -.task-date-input { - padding: 0.75rem; - background: rgba(15, 23, 42, 0.5); - border: 1px solid #475569; - border-radius: 8px; - color: #f1f5f9; - transition: all 0.2s; -} - -.task-category-select:focus, -.task-date-input:focus { - outline: none; - border-color: #667eea; -} - -.button-primary { - display: flex; +.status-pill { + display: inline-flex; align-items: center; - gap: 0.5rem; - padding: 0.75rem 1.5rem; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - border: none; - border-radius: 8px; - font-weight: 600; - cursor: pointer; - transition: all 0.2s; -} - -.button-primary:hover { - transform: translateY(-2px); - box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); -} - -.button-primary:disabled { - opacity: 0.5; - cursor: not-allowed; - transform: none; -} - -.plus-icon { - font-size: 1.25rem; -} - -/* Filter Tabs */ -.filter-tabs { - display: flex; - gap: 0.5rem; - padding: 0.5rem; - background: #1e293b; - border-radius: 12px; - margin-bottom: 1.5rem; -} - -.filter-tab { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - padding: 0.75rem; - background: transparent; - border: none; - border-radius: 8px; - color: #94a3b8; + gap: 8px; + padding: 8px 14px; + background: var(--sentient-bg-tertiary); + border: 1px solid var(--sentient-border); + border-radius: 20px; + color: var(--sentient-text-secondary); + font-size: 13px; font-weight: 500; cursor: pointer; - transition: all 0.2s; + transition: all 0.2s ease; } -.filter-tab:hover { - background: rgba(100, 116, 139, 0.1); - color: #cbd5e1; +.status-pill:hover { + border-color: var(--sentient-border-hover); + color: var(--sentient-text-primary); } -.filter-tab.active { - background: #334155; - color: #f1f5f9; +.status-pill.active { + background: var(--sentient-accent-dim); + border-color: var(--sentient-accent); + color: var(--sentient-accent); } -.priority-tab.active { - background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%); - color: #0f172a; +.status-pill.complete.active { + background: rgba(34, 197, 94, 0.15); + border-color: var(--sentient-success); + color: var(--sentient-success); } -.tab-icon { - font-size: 1.125rem; +.status-pill.active-intents.active { + background: rgba(59, 130, 246, 0.15); + border-color: var(--sentient-info); + color: var(--sentient-info); } -.tab-count { - padding: 0.125rem 0.5rem; - background: rgba(100, 116, 139, 0.2); - border-radius: 999px; - font-size: 0.75rem; +.status-pill.awaiting.active { + background: rgba(245, 158, 11, 0.15); + border-color: var(--sentient-warning); + color: var(--sentient-warning); +} + +.status-pill.paused.active { + background: rgba(139, 92, 246, 0.15); + border-color: var(--sentient-paused); + color: var(--sentient-paused); +} + +.status-pill.blocked.active { + background: rgba(239, 68, 68, 0.15); + border-color: var(--sentient-error); + color: var(--sentient-error); +} + +.status-pill .pill-count { + padding: 2px 8px; + background: rgba(255, 255, 255, 0.1); + border-radius: 10px; + font-size: 11px; font-weight: 600; } -.filter-tab.active .tab-count { - background: rgba(241, 245, 249, 0.2); +.status-pill.active .pill-count { + background: rgba(255, 255, 255, 0.15); } -/* Task List */ -.task-list-container { - min-height: 300px; - margin-bottom: 2rem; +/* Task List Scroll Container */ +.tasks-list-scroll { + flex: 1; + overflow-y: auto; + padding: 16px; } -.task-list { - display: flex; - flex-direction: column; - gap: 0.75rem; +/* Task Card */ +.task-card { + background: var(--sentient-bg-card); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-lg); + padding: 16px; + margin-bottom: 12px; + cursor: pointer; + transition: all 0.2s ease; } -.task-item { - display: flex; - align-items: center; - gap: 1rem; - padding: 1rem 1.25rem; - background: #1e293b; - border-radius: 12px; - transition: all 0.2s; - animation: slideIn 0.3s ease; -} - -@keyframes slideIn { - from { - opacity: 0; - transform: translateY(-10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.task-item:hover { - background: #334155; +.task-card:hover { + border-color: var(--sentient-border-hover); transform: translateX(4px); } -.task-item.completed { - opacity: 0.6; +.task-card.selected { + border-color: var(--sentient-accent); + background: var(--sentient-accent-dim); } -.task-item.completed .task-text { - text-decoration: line-through; - color: #64748b; +.task-card-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 12px; } -.task-checkbox { +.task-card-title { + font-size: 15px; + font-weight: 600; + color: var(--sentient-text-primary); + margin: 0 0 4px 0; + line-height: 1.4; +} + +.task-card-subtitle { + font-size: 13px; + color: var(--sentient-text-muted); +} + +.task-card-status { + padding: 4px 10px; + border-radius: 12px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.3px; +} + +.task-card-status.running { + background: rgba(59, 130, 246, 0.15); + color: var(--sentient-info); +} + +.task-card-status.complete { + background: rgba(34, 197, 94, 0.15); + color: var(--sentient-success); +} + +.task-card-status.awaiting { + background: rgba(245, 158, 11, 0.15); + color: var(--sentient-warning); +} + +.task-card-status.paused { + background: rgba(139, 92, 246, 0.15); + color: var(--sentient-paused); +} + +.task-card-status.blocked { + background: rgba(239, 68, 68, 0.15); + color: var(--sentient-error); +} + +/* Task Progress */ +.task-card-progress { + margin-bottom: 12px; +} + +.task-progress-bar { + height: 6px; + background: var(--sentient-bg-tertiary); + border-radius: 3px; + overflow: hidden; + margin-bottom: 6px; +} + +.task-progress-fill { + height: 100%; + background: var(--sentient-accent); + border-radius: 3px; + transition: width 0.3s ease; +} + +.task-progress-fill.success { + background: var(--sentient-success); +} + +.task-progress-info { + display: flex; + justify-content: space-between; + font-size: 12px; +} + +.task-progress-percent { + color: var(--sentient-accent); + font-weight: 600; +} + +.task-progress-steps { + color: var(--sentient-text-muted); +} + +/* Task Card Meta */ +.task-card-meta { + display: flex; + align-items: center; + gap: 12px; + font-size: 12px; + color: var(--sentient-text-muted); +} + +.task-card-meta-item { + display: flex; + align-items: center; + gap: 4px; +} + +/* ============================================================================= + TASK DETAIL PANEL (RIGHT) + ============================================================================= */ + +.task-detail-panel { + width: 480px; + display: flex; + flex-direction: column; + background: var(--sentient-bg-secondary); + overflow: hidden; +} + +.task-detail-header { + padding: 20px 24px; + border-bottom: 1px solid var(--sentient-border); +} + +.task-detail-title-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 12px; +} + +.task-detail-title { + font-size: 18px; + font-weight: 600; + color: var(--sentient-text-primary); + margin: 0; + line-height: 1.4; +} + +.task-detail-actions { + display: flex; + gap: 8px; +} + +.task-detail-action-btn { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + background: var(--sentient-bg-tertiary); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-sm); + color: var(--sentient-text-secondary); + font-size: 14px; + cursor: pointer; + transition: all 0.2s ease; +} + +.task-detail-action-btn:hover { + background: var(--sentient-bg-hover); + color: var(--sentient-text-primary); +} + +.task-detail-meta { + display: flex; + gap: 16px; + flex-wrap: wrap; +} + +.task-detail-meta-item { + display: flex; + align-items: center; + gap: 6px; + font-size: 13px; + color: var(--sentient-text-secondary); +} + +.task-detail-meta-item .icon { + color: var(--sentient-text-muted); +} + +/* Task Detail Scroll */ +.task-detail-scroll { + flex: 1; + overflow-y: auto; + padding: 20px 24px; +} + +/* Decision Required Section */ +.decision-required-section { + background: rgba(245, 158, 11, 0.08); + border: 1px solid rgba(245, 158, 11, 0.25); + border-radius: var(--sentient-radius-lg); + padding: 16px; + margin-bottom: 20px; +} + +.decision-required-header { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 12px; +} + +.decision-required-icon { + width: 28px; + height: 28px; + background: rgba(245, 158, 11, 0.2); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: var(--sentient-warning); + font-size: 14px; +} + +.decision-required-title { + font-size: 14px; + font-weight: 600; + color: var(--sentient-warning); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.decision-required-description { + font-size: 14px; + color: var(--sentient-text-secondary); + margin-bottom: 16px; + line-height: 1.5; +} + +.decision-options { + display: flex; + flex-direction: column; + gap: 8px; +} + +.decision-option { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 14px; + background: var(--sentient-bg-tertiary); + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-md); + cursor: pointer; + transition: all 0.2s ease; +} + +.decision-option:hover { + border-color: var(--sentient-accent); + background: var(--sentient-accent-dim); +} + +.decision-option.selected { + border-color: var(--sentient-accent); + background: var(--sentient-accent-dim); +} + +.decision-option-radio { + width: 18px; + height: 18px; + border: 2px solid var(--sentient-border-hover); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.decision-option.selected .decision-option-radio { + border-color: var(--sentient-accent); +} + +.decision-option.selected .decision-option-radio::after { + content: ""; + width: 8px; + height: 8px; + background: var(--sentient-accent); + border-radius: 50%; +} + +.decision-option-content { + flex: 1; +} + +.decision-option-label { + font-size: 14px; + font-weight: 500; + color: var(--sentient-text-primary); + margin-bottom: 2px; +} + +.decision-option-desc { + font-size: 12px; + color: var(--sentient-text-muted); +} + +.decision-actions { + display: flex; + gap: 10px; + margin-top: 16px; +} + +.decision-btn { + flex: 1; + padding: 10px 16px; + border-radius: var(--sentient-radius-md); + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +.decision-btn-primary { + background: var(--sentient-accent); + border: none; + color: #000; +} + +.decision-btn-primary:hover { + background: #d4ff4a; + box-shadow: var(--sentient-shadow-glow); +} + +.decision-btn-secondary { + background: transparent; + border: 1px solid var(--sentient-border); + color: var(--sentient-text-secondary); +} + +.decision-btn-secondary:hover { + background: var(--sentient-bg-tertiary); + color: var(--sentient-text-primary); +} + +/* Progress Log Section */ +.progress-log-section { + margin-bottom: 20px; +} + +.progress-log-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; +} + +.progress-log-title { + font-size: 14px; + font-weight: 600; + color: var(--sentient-text-primary); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.progress-log-toggle { + background: none; + border: none; + color: var(--sentient-text-muted); + font-size: 12px; + cursor: pointer; + transition: color 0.2s ease; +} + +.progress-log-toggle:hover { + color: var(--sentient-text-primary); +} + +/* Step Items */ +.step-list { + display: flex; + flex-direction: column; + gap: 2px; +} + +.step-item { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 12px 14px; + background: var(--sentient-bg-tertiary); + border-radius: var(--sentient-radius-sm); + transition: all 0.2s ease; +} + +.step-item:first-child { + border-radius: var(--sentient-radius-md) var(--sentient-radius-md) + var(--sentient-radius-sm) var(--sentient-radius-sm); +} + +.step-item:last-child { + border-radius: var(--sentient-radius-sm) var(--sentient-radius-sm) + var(--sentient-radius-md) var(--sentient-radius-md); +} + +.step-item:only-child { + border-radius: var(--sentient-radius-md); +} + +.step-icon { width: 24px; height: 24px; - border-radius: 6px; - border: 2px solid #475569; - cursor: pointer; - transition: all 0.2s; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + font-size: 12px; + flex-shrink: 0; } -.task-checkbox:checked { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-color: transparent; +.step-item.completed .step-icon { + background: var(--sentient-success); + color: #000; } -.task-content { +.step-item.active .step-icon { + background: var(--sentient-accent); + color: #000; + animation: pulse 2s infinite; +} + +.step-item.pending .step-icon { + background: var(--sentient-bg-hover); + color: var(--sentient-text-muted); +} + +.step-item.error .step-icon { + background: var(--sentient-error); + color: #fff; +} + +.step-content { flex: 1; - display: flex; - flex-direction: column; - gap: 0.25rem; + min-width: 0; } -.task-text-wrapper { - display: flex; - flex-direction: column; - gap: 0.25rem; +.step-name { + font-size: 14px; + font-weight: 500; + color: var(--sentient-text-primary); + margin-bottom: 2px; } -.task-text { - color: #f1f5f9; - font-size: 1rem; +.step-item.pending .step-name { + color: var(--sentient-text-muted); } -.task-meta { - display: flex; - gap: 0.75rem; - align-items: center; +.step-detail { + font-size: 12px; + color: var(--sentient-text-muted); } -.task-category { - padding: 0.25rem 0.75rem; - background: rgba(100, 116, 139, 0.2); - border-radius: 999px; - font-size: 0.75rem; - color: #94a3b8; +.step-time { + font-size: 11px; + color: var(--sentient-text-muted); + flex-shrink: 0; } -.task-due-date { +/* ============================================================================= + LIVE AGENT ACTIVITY (TERMINAL) + ============================================================================= */ + +.agent-activity-section { + border-top: 1px solid var(--sentient-border); + background: var(--sentient-bg-primary); +} + +.agent-activity-header { display: flex; align-items: center; - gap: 0.25rem; - font-size: 0.75rem; - color: #94a3b8; + justify-content: space-between; + padding: 12px 24px; + background: var(--sentient-bg-secondary); + border-bottom: 1px solid var(--sentient-border); } -.task-due-date.overdue { - color: #ef4444; -} - -.task-actions { +.agent-activity-title { display: flex; - gap: 0.5rem; + align-items: center; + gap: 10px; + font-size: 12px; + font-weight: 600; + color: var(--sentient-text-secondary); + text-transform: uppercase; + letter-spacing: 0.5px; } -.action-btn { - padding: 0.5rem; +.agent-status-dot { + width: 8px; + height: 8px; + background: var(--sentient-accent); + border-radius: 50%; + animation: pulse 2s infinite; +} + +.agent-activity-actions { + display: flex; + gap: 8px; +} + +.agent-activity-btn { + padding: 4px 8px; background: transparent; - border: none; - border-radius: 6px; - color: #64748b; + border: 1px solid var(--sentient-border); + border-radius: var(--sentient-radius-sm); + color: var(--sentient-text-muted); + font-size: 11px; cursor: pointer; - transition: all 0.2s; + transition: all 0.2s ease; } -.action-btn:hover { - background: rgba(100, 116, 139, 0.1); - color: #cbd5e1; +.agent-activity-btn:hover { + background: var(--sentient-bg-tertiary); + color: var(--sentient-text-primary); } -.priority-btn.active { - color: #fbbf24; +.agent-activity-log { + padding: 16px 24px; + max-height: 180px; + overflow-y: auto; + font-family: var(--sentient-font-mono); + font-size: 12px; + line-height: 1.6; } -.edit-btn:hover { - color: #3b82f6; +.activity-line { + display: flex; + gap: 12px; + padding: 3px 0; } -.delete-btn:hover { - color: #ef4444; +.activity-timestamp { + color: var(--sentient-text-muted); + min-width: 65px; + flex-shrink: 0; } -/* Task input for editing */ -.task-edit-input { - flex: 1; - padding: 0.5rem; - background: rgba(15, 23, 42, 0.5); - border: 1px solid #475569; - border-radius: 6px; - color: #f1f5f9; - font-size: 1rem; +.activity-message { + color: var(--sentient-text-secondary); } -/* Empty state */ +.activity-line.success .activity-message { + color: var(--sentient-success); +} + +.activity-line.warning .activity-message { + color: var(--sentient-warning); +} + +.activity-line.error .activity-message { + color: var(--sentient-error); +} + +.activity-line.accent .activity-message { + color: var(--sentient-accent); +} + +.activity-line.info .activity-message { + color: var(--sentient-info); +} + +/* ============================================================================= + EMPTY STATE + ============================================================================= */ + .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; - padding: 4rem 2rem; + padding: 60px 40px; text-align: center; } -.empty-state svg { - color: #475569; - margin-bottom: 1rem; +.empty-state-icon { + font-size: 48px; + margin-bottom: 16px; + opacity: 0.5; } -.empty-state h3 { - font-size: 1.25rem; - color: #94a3b8; - margin-bottom: 0.5rem; +.empty-state-title { + font-size: 18px; + font-weight: 600; + color: var(--sentient-text-primary); + margin-bottom: 8px; } -.empty-state p { - color: #64748b; +.empty-state-description { + font-size: 14px; + color: var(--sentient-text-muted); + max-width: 280px; } -/* Loading state */ +/* ============================================================================= + LOADING STATE + ============================================================================= */ + .loading-state { display: flex; flex-direction: column; align-items: center; justify-content: center; - padding: 4rem; - color: #64748b; + padding: 60px 40px; } -/* Footer */ -.tasks-footer { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1.5rem; - background: #1e293b; - border-radius: 12px; +.loading-spinner { + width: 40px; + height: 40px; + border: 3px solid var(--sentient-bg-tertiary); + border-top-color: var(--sentient-accent); + border-radius: 50%; + animation: spin 0.8s linear infinite; + margin-bottom: 16px; } -.footer-info { - color: #94a3b8; -} - -.footer-actions { - display: flex; - gap: 0.75rem; -} - -.button-secondary { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.5rem 1rem; - background: transparent; - border: 1px solid #475569; - border-radius: 8px; - color: #94a3b8; - cursor: pointer; - transition: all 0.2s; -} - -.button-secondary:hover { - background: #334155; - color: #f1f5f9; - border-color: #475569; -} - -/* Spinner */ @keyframes spin { to { transform: rotate(360deg); } } -.spinner { - width: 2rem; - height: 2rem; - border: 3px solid #334155; - border-top-color: #667eea; - border-radius: 50%; - animation: spin 0.8s linear infinite; - margin-bottom: 1rem; +.loading-text { + font-size: 14px; + color: var(--sentient-text-muted); } -/* HTMX loading states */ -.htmx-request .task-item { +/* ============================================================================= + RESPONSIVE + ============================================================================= */ + +@media (max-width: 1024px) { + .task-detail-panel { + width: 380px; + } +} + +@media (max-width: 768px) { + .tasks-main { + flex-direction: column; + } + + .tasks-list-panel { + border-right: none; + border-bottom: 1px solid var(--sentient-border); + max-height: 50vh; + } + + .task-detail-panel { + width: 100%; + flex: 1; + } + + .topbar-center { + display: none; + } + + .topbar-nav { + display: none; + } +} + +/* ============================================================================= + HTMX LOADING STATES + ============================================================================= */ + +.htmx-request .task-card { opacity: 0.6; } -.htmx-request.filter-tab { +.htmx-request .status-pill { pointer-events: none; opacity: 0.7; } -/* Responsive */ -@media (max-width: 640px) { - .tasks-container { - padding: 1rem; - } +/* ============================================================================= + ANIMATIONS + ============================================================================= */ - .header-content { - flex-direction: column; - gap: 1rem; - text-align: center; - } +.task-card { + animation: fadeIn 0.3s ease; +} - .header-stats { - width: 100%; - justify-content: space-around; +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(8px); } - - .add-task-form { - flex-direction: column; - } - - .task-input { - width: 100%; - } - - .filter-tabs { - flex-wrap: wrap; - } - - .tasks-footer { - flex-direction: column; - gap: 1rem; - text-align: center; + to { + opacity: 1; + transform: translateY(0); } } diff --git a/ui/suite/tasks/tasks.html b/ui/suite/tasks/tasks.html index 22d5c18..e240ff0 100644 --- a/ui/suite/tasks/tasks.html +++ b/ui/suite/tasks/tasks.html @@ -1,878 +1,549 @@ -
- -
-
-

- - Tasks -

-
- - 0 - Total - - - 0 - Active - - - 0 - Completed - + + + + + +
+ +
+
+ + +
+ +
+
-
- -
-
- - - - -
-
- - -
- - - - -
- - -
-
- -
-
-

Loading tasks...

+ +
+
JD
+ John Doe +
-
+ - - + + +
- Clear Completed - - -
-
+
+
+

+ Generate Q4 Financial Report +

+

+ Automated data aggregation and formatting +

+
+ Complete +
+
+
+
+
+
+ 100% + 8/8 steps +
+
+
+ ⏱️ 2h 34m + 📅 Completed today +
+ + + +
+
+
+

+ Customer Data Migration +

+

+ Migrating 50,000 customer records +

+
+ Awaiting +
+
+
+
+
+
+ 67% + 4/6 steps +
+
+
+ ⚠️ Decision required + ⏱️ Running 45m +
+
+ + +
+
+
+

+ API Integration Testing +

+

+ Running automated test suite +

+
+ Running +
+
+
+
+
+
+ 45% + 9/20 tests +
+
+
+ ⏱️ Running 12m + 🔄 Auto-retry enabled +
+
+ + +
+
+
+

+ Email Campaign Deployment +

+

+ Scheduled marketing campaign +

+
+ Paused +
+
+
+
+
+
+ 30% + 3/10 steps +
+
+
+ ⏸️ Paused by user + 📅 Resume: Tomorrow 9am +
+
+ + +
+
+
+

Database Backup

+

+ Automated nightly backup +

+
+ Blocked +
+
+
+
+
+
+ 15% + 1/7 steps +
+
+
+ ❌ Storage quota exceeded + 🔧 Action required +
+
+
+ + + + +
- - - + diff --git a/ui/suite/tasks/tasks.js b/ui/suite/tasks/tasks.js index 0219c73..4e12284 100644 --- a/ui/suite/tasks/tasks.js +++ b/ui/suite/tasks/tasks.js @@ -1,226 +1,657 @@ -/* Tasks page JavaScript */ +/* ============================================================================= + TASKS APP JAVASCRIPT + Automated Intelligent Task Management Interface + ============================================================================= */ -// Set active tab -function setActiveTab(button) { - document.querySelectorAll(".filter-tab").forEach((tab) => { - tab.classList.remove("active"); +// ============================================================================= +// STATE MANAGEMENT +// ============================================================================= + +const TasksState = { + selectedTaskId: 2, // Default selected task + currentFilter: "complete", + tasks: [], + wsConnection: null, + agentLogPaused: false, +}; + +// ============================================================================= +// INITIALIZATION +// ============================================================================= + +document.addEventListener("DOMContentLoaded", function () { + initTasksApp(); +}); + +function initTasksApp() { + // Initialize WebSocket for real-time updates + initWebSocket(); + + // Setup event listeners + setupEventListeners(); + + // Setup keyboard shortcuts + setupKeyboardShortcuts(); + + // Auto-scroll agent log to bottom + scrollAgentLogToBottom(); + + console.log("[Tasks] Initialized"); +} + +// ============================================================================= +// WEBSOCKET CONNECTION +// ============================================================================= + +function initWebSocket() { + const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; + const wsUrl = `${protocol}//${window.location.host}/ws/tasks`; + + try { + TasksState.wsConnection = new WebSocket(wsUrl); + + TasksState.wsConnection.onopen = function () { + console.log("[Sentient Tasks] WebSocket connected"); + addAgentLog("info", "[SYSTEM] Connected to task orchestrator"); + }; + + TasksState.wsConnection.onmessage = function (event) { + handleWebSocketMessage(JSON.parse(event.data)); + }; + + TasksState.wsConnection.onclose = function () { + console.log("[Sentient Tasks] WebSocket disconnected, reconnecting..."); + setTimeout(initWebSocket, 5000); + }; + + TasksState.wsConnection.onerror = function (error) { + console.error("[Sentient Tasks] WebSocket error:", error); + }; + } catch (e) { + console.warn("[Sentient Tasks] WebSocket not available"); + } +} + +function handleWebSocketMessage(data) { + switch (data.type) { + case "task_update": + updateTaskCard(data.task); + if (data.task.id === TasksState.selectedTaskId) { + updateTaskDetail(data.task); + } + break; + case "step_progress": + updateStepProgress(data.taskId, data.step); + break; + case "agent_log": + addAgentLog(data.level, data.message); + break; + case "decision_required": + showDecisionRequired(data.decision); + break; + case "task_completed": + onTaskCompleted(data.task); + break; + case "task_failed": + onTaskFailed(data.task, data.error); + break; + } +} + +// ============================================================================= +// EVENT LISTENERS +// ============================================================================= + +function setupEventListeners() { + // Filter pills + document.querySelectorAll(".status-pill").forEach((pill) => { + pill.addEventListener("click", function (e) { + e.preventDefault(); + const filter = this.dataset.filter; + setActiveFilter(filter, this); }); - button.classList.add("active"); + }); + + // Search input + const searchInput = document.querySelector(".topbar-search-input"); + if (searchInput) { + searchInput.addEventListener( + "input", + debounce(function (e) { + searchTasks(e.target.value); + }, 300), + ); + } + + // Nav items + document.querySelectorAll(".topbar-nav-item").forEach((item) => { + item.addEventListener("click", function () { + document + .querySelectorAll(".topbar-nav-item") + .forEach((i) => i.classList.remove("active")); + this.classList.add("active"); + }); + }); + + // Progress log toggle + const logToggle = document.querySelector(".progress-log-toggle"); + if (logToggle) { + logToggle.addEventListener("click", toggleProgressLog); + } } -// Export tasks as JSON -function exportTasks() { - fetch("/api/tasks?format=json") - .then((response) => response.json()) - .then((tasks) => { - const dataStr = JSON.stringify(tasks, null, 2); - const dataUri = - "data:application/json;charset=utf-8," + - encodeURIComponent(dataStr); +function setupKeyboardShortcuts() { + document.addEventListener("keydown", function (e) { + // Escape: Deselect task + if (e.key === "Escape") { + deselectTask(); + } - const exportFileDefaultName = `tasks-${new Date().toISOString().split("T")[0]}.json`; + // Cmd/Ctrl + K: Focus search + if ((e.metaKey || e.ctrlKey) && e.key === "k") { + e.preventDefault(); + document.querySelector(".topbar-search-input")?.focus(); + } - const linkElement = document.createElement("a"); - linkElement.setAttribute("href", dataUri); - linkElement.setAttribute("download", exportFileDefaultName); - linkElement.click(); - }); + // Arrow keys: Navigate tasks + if (e.key === "ArrowDown" || e.key === "ArrowUp") { + e.preventDefault(); + navigateTasks(e.key === "ArrowDown" ? 1 : -1); + } + + // Enter: Submit decision if in decision mode + if ( + e.key === "Enter" && + document.querySelector(".decision-option.selected") + ) { + submitDecision(); + } + + // 1-5: Quick filter + if (e.key >= "1" && e.key <= "5" && !e.target.matches("input, textarea")) { + const pills = document.querySelectorAll(".status-pill"); + const index = parseInt(e.key) - 1; + if (pills[index]) { + pills[index].click(); + } + } + }); } -// Update task statistics -function updateStats() { - fetch("/api/tasks/stats") - .then((response) => response.json()) - .then((stats) => { - // Update header stats - document.querySelector( - ".stat-item:nth-child(1) .stat-value", - ).textContent = stats.total || 0; - document.querySelector( - ".stat-item:nth-child(2) .stat-value", - ).textContent = stats.active || 0; - document.querySelector( - ".stat-item:nth-child(3) .stat-value", - ).textContent = stats.completed || 0; +// ============================================================================= +// TASK SELECTION & FILTERING +// ============================================================================= - // Update tab counts - document.getElementById("count-all").textContent = - stats.total || 0; - document.getElementById("count-active").textContent = - stats.active || 0; - document.getElementById("count-completed").textContent = - stats.completed || 0; - document.getElementById("count-priority").textContent = - stats.priority || 0; +function selectTask(taskId) { + TasksState.selectedTaskId = taskId; - // Update footer text - const footerText = document.getElementById("footer-text"); - if (stats.active === 0) { - footerText.innerHTML = "All tasks completed! 🎉"; - } else { - footerText.innerHTML = `${stats.active} ${stats.active === 1 ? "task" : "tasks"} remaining`; - } + // Update selected state in list + document.querySelectorAll(".task-card").forEach((card) => { + card.classList.toggle("selected", card.dataset.taskId == taskId); + }); - // Show/hide footer - const footer = document.getElementById("task-footer"); - footer.style.display = stats.total > 0 ? "flex" : "none"; - }); + // Load task details (in real app, this would fetch from API) + loadTaskDetails(taskId); } -// Handle checkbox changes -document.addEventListener("change", function (e) { - if (e.target.classList.contains("task-checkbox")) { - const taskId = e.target.dataset.taskId; - const completed = e.target.checked; +function deselectTask() { + TasksState.selectedTaskId = null; + document.querySelectorAll(".task-card").forEach((card) => { + card.classList.remove("selected"); + }); +} - fetch(`/api/tasks/${taskId}/status`, { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ completed }), - }).then(() => { - const taskItem = e.target.closest(".task-item"); - if (completed) { - taskItem.classList.add("completed"); - } else { - taskItem.classList.remove("completed"); - } - updateStats(); - }); - } -}); +function navigateTasks(direction) { + const cards = Array.from(document.querySelectorAll(".task-card")); + if (cards.length === 0) return; -// Handle task actions -document.addEventListener("click", function (e) { - // Priority toggle - if (e.target.closest('[data-action="priority"]')) { - const btn = e.target.closest('[data-action="priority"]'); - const taskId = btn.dataset.taskId; - const priority = !btn.classList.contains("active"); + const currentIndex = cards.findIndex((c) => c.classList.contains("selected")); + let newIndex; - fetch(`/api/tasks/${taskId}/priority`, { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ priority }), - }).then(() => { - btn.classList.toggle("active"); - updateStats(); - }); - } + if (currentIndex === -1) { + newIndex = direction === 1 ? 0 : cards.length - 1; + } else { + newIndex = currentIndex + direction; + if (newIndex < 0) newIndex = cards.length - 1; + if (newIndex >= cards.length) newIndex = 0; + } - // Edit task - if (e.target.closest('[data-action="edit"]')) { - const btn = e.target.closest('[data-action="edit"]'); - const taskId = btn.dataset.taskId; - const taskItem = btn.closest(".task-item"); - const taskText = taskItem.querySelector(".task-text"); - const currentText = taskText.textContent; + const taskId = cards[newIndex].dataset.taskId; + selectTask(taskId); + cards[newIndex].scrollIntoView({ behavior: "smooth", block: "nearest" }); +} - const input = document.createElement("input"); - input.type = "text"; - input.className = "task-edit-input"; - input.value = currentText; +function setActiveFilter(filter, button) { + TasksState.currentFilter = filter; - taskText.replaceWith(input); - input.focus(); - input.select(); + // Update active pill + document.querySelectorAll(".status-pill").forEach((pill) => { + pill.classList.remove("active"); + }); + button.classList.add("active"); - input.addEventListener("blur", function () { - saveEdit(); - }); + // Filter will be handled by HTMX, but we track state + addAgentLog("info", `[FILTER] Showing ${filter} tasks`); +} - input.addEventListener("keydown", function (e) { - if (e.key === "Enter") { - saveEdit(); - } else if (e.key === "Escape") { - cancelEdit(); - } - }); +function searchTasks(query) { + if (query.length > 0) { + addAgentLog("info", `[SEARCH] Searching: "${query}"`); + } - function saveEdit() { - const newText = input.value.trim(); - if (newText && newText !== currentText) { - fetch(`/api/tasks/${taskId}`, { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ text: newText }), - }).then(() => { - const span = document.createElement("span"); - span.className = "task-text"; - span.textContent = newText; - input.replaceWith(span); - }); - } else { - cancelEdit(); - } + // In real app, this would filter via API + // For demo, we'll do client-side filtering + const cards = document.querySelectorAll(".task-card"); + cards.forEach((card) => { + const title = + card.querySelector(".task-card-title")?.textContent.toLowerCase() || ""; + const subtitle = + card.querySelector(".task-card-subtitle")?.textContent.toLowerCase() || + ""; + const matches = + title.includes(query.toLowerCase()) || + subtitle.includes(query.toLowerCase()); + card.style.display = matches || query === "" ? "block" : "none"; + }); +} + +// ============================================================================= +// TASK DETAILS +// ============================================================================= + +function loadTaskDetails(taskId) { + // In real app, fetch from API + // htmx.ajax('GET', `/api/tasks/${taskId}`, {target: '#task-detail-panel', swap: 'innerHTML'}); + + addAgentLog("info", `[LOAD] Loading task #${taskId} details`); +} + +function updateTaskCard(task) { + const card = document.querySelector(`[data-task-id="${task.id}"]`); + if (!card) return; + + // Update progress + const progressFill = card.querySelector(".task-progress-fill"); + const progressPercent = card.querySelector(".task-progress-percent"); + const progressSteps = card.querySelector(".task-progress-steps"); + + if (progressFill) progressFill.style.width = `${task.progress}%`; + if (progressPercent) progressPercent.textContent = `${task.progress}%`; + if (progressSteps) + progressSteps.textContent = `${task.currentStep}/${task.totalSteps} steps`; + + // Update status badge + const statusBadge = card.querySelector(".task-card-status"); + if (statusBadge) { + statusBadge.className = `task-card-status ${task.status}`; + statusBadge.textContent = formatStatus(task.status); + } +} + +function updateTaskDetail(task) { + // Update detail panel with task data + const detailTitle = document.querySelector(".task-detail-title"); + if (detailTitle) detailTitle.textContent = task.title; +} + +// ============================================================================= +// DECISION HANDLING +// ============================================================================= + +function selectDecision(element, value) { + // Remove selected from all options + document.querySelectorAll(".decision-option").forEach((opt) => { + opt.classList.remove("selected"); + }); + + // Add selected to clicked option + element.classList.add("selected"); + + // Store selected value + TasksState.selectedDecision = value; + + addAgentLog("info", `[DECISION] Selected: ${value}`); +} + +function submitDecision() { + const selectedOption = document.querySelector(".decision-option.selected"); + if (!selectedOption) { + showToast("Please select an option", "warning"); + return; + } + + const value = TasksState.selectedDecision; + const taskId = TasksState.selectedTaskId; + + addAgentLog("accent", `[AGENT] Applying decision: ${value}`); + addAgentLog("info", `[TASK] Resuming task #${taskId}...`); + + // In real app, send to API + fetch(`/api/tasks/${taskId}/decide`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ decision: value }), + }) + .then((response) => response.json()) + .then((result) => { + if (result.success) { + showToast("Decision applied successfully", "success"); + addAgentLog("success", `[OK] Decision applied, task resuming`); + + // Hide decision section (in real app, would update via HTMX) + const decisionSection = document.querySelector( + ".decision-required-section", + ); + if (decisionSection) { + decisionSection.style.display = "none"; } + } else { + showToast("Failed to apply decision", "error"); + addAgentLog( + "error", + `[ERROR] Failed to apply decision: ${result.error}`, + ); + } + }) + .catch((error) => { + // For demo, simulate success + showToast("Decision applied successfully", "success"); + addAgentLog("success", `[OK] Decision applied, task resuming`); - function cancelEdit() { - const span = document.createElement("span"); - span.className = "task-text"; - span.textContent = currentText; - input.replaceWith(span); + const decisionSection = document.querySelector( + ".decision-required-section", + ); + if (decisionSection) { + decisionSection.style.opacity = "0.5"; + setTimeout(() => { + decisionSection.style.display = "none"; + }, 500); + } + + // Update step status + const activeStep = document.querySelector(".step-item.active"); + if (activeStep) { + activeStep.classList.remove("active"); + activeStep.classList.add("completed"); + activeStep.querySelector(".step-icon").textContent = "✓"; + activeStep.querySelector(".step-detail").textContent = + "Completed with merge strategy"; + + const nextStep = activeStep.nextElementSibling; + if (nextStep && nextStep.classList.contains("pending")) { + nextStep.classList.remove("pending"); + nextStep.classList.add("active"); + nextStep.querySelector(".step-icon").textContent = "●"; + nextStep.querySelector(".step-time").textContent = "Now"; } + } + }); +} + +function showDecisionRequired(decision) { + addAgentLog("warning", `[ALERT] Decision required: ${decision.title}`); + showToast(`Decision required: ${decision.title}`, "warning"); +} + +// ============================================================================= +// PROGRESS LOG +// ============================================================================= + +function toggleProgressLog() { + const stepList = document.querySelector(".step-list"); + const toggle = document.querySelector(".progress-log-toggle"); + + if (stepList.style.display === "none") { + stepList.style.display = "flex"; + toggle.textContent = "Collapse"; + } else { + stepList.style.display = "none"; + toggle.textContent = "Expand"; + } +} + +function updateStepProgress(taskId, step) { + if (taskId !== TasksState.selectedTaskId) return; + + const stepItems = document.querySelectorAll(".step-item"); + stepItems.forEach((item, index) => { + if (index < step.index) { + item.classList.remove("active", "pending"); + item.classList.add("completed"); + item.querySelector(".step-icon").textContent = "✓"; + } else if (index === step.index) { + item.classList.remove("completed", "pending"); + item.classList.add("active"); + item.querySelector(".step-icon").textContent = "●"; + item.querySelector(".step-name").textContent = step.name; + item.querySelector(".step-detail").textContent = step.detail; + item.querySelector(".step-time").textContent = "Now"; + } else { + item.classList.remove("completed", "active"); + item.classList.add("pending"); + item.querySelector(".step-icon").textContent = "○"; } + }); +} - // Delete task - if (e.target.closest('[data-action="delete"]')) { - const btn = e.target.closest('[data-action="delete"]'); - const taskId = btn.dataset.taskId; +// ============================================================================= +// AGENT ACTIVITY LOG +// ============================================================================= - if (confirm("Delete this task?")) { - fetch(`/api/tasks/${taskId}`, { - method: "DELETE", - }).then(() => { - const taskItem = btn.closest(".task-item"); - taskItem.style.animation = "slideOut 0.3s ease"; - setTimeout(() => { - taskItem.remove(); - updateStats(); - }, 300); - }); - } - } -}); +function addAgentLog(level, message) { + if (TasksState.agentLogPaused) return; + + const log = document.getElementById("agent-log"); + if (!log) return; + + const now = new Date(); + const timestamp = now.toTimeString().split(" ")[0].substring(0, 8); + + const line = document.createElement("div"); + line.className = `activity-line ${level}`; + line.innerHTML = ` + ${timestamp} + ${message} + `; + + // Insert at the top + log.insertBefore(line, log.firstChild); + + // Limit log entries + while (log.children.length > 100) { + log.removeChild(log.lastChild); + } +} + +function scrollAgentLogToBottom() { + const log = document.getElementById("agent-log"); + if (log) { + log.scrollTop = 0; // Since newest is at top + } +} + +function clearAgentLog() { + const log = document.getElementById("agent-log"); + if (log) { + log.innerHTML = ""; + addAgentLog("info", "[SYSTEM] Log cleared"); + } +} + +function toggleAgentLogPause() { + TasksState.agentLogPaused = !TasksState.agentLogPaused; + const pauseBtn = document.querySelector(".agent-activity-btn:last-child"); + if (pauseBtn) { + pauseBtn.textContent = TasksState.agentLogPaused ? "Resume" : "Pause"; + } + addAgentLog( + "info", + TasksState.agentLogPaused ? "[SYSTEM] Log paused" : "[SYSTEM] Log resumed", + ); +} + +// ============================================================================= +// TASK LIFECYCLE +// ============================================================================= + +function onTaskCompleted(task) { + showToast(`Task completed: ${task.title}`, "success"); + addAgentLog("success", `[COMPLETE] Task #${task.id}: ${task.title}`); + updateTaskCard(task); +} + +function onTaskFailed(task, error) { + showToast(`Task failed: ${task.title}`, "error"); + addAgentLog("error", `[FAILED] Task #${task.id}: ${error}`); + updateTaskCard(task); +} + +// ============================================================================= +// TOAST NOTIFICATIONS +// ============================================================================= + +function showToast(message, type = "info") { + let container = document.getElementById("toast-container"); + if (!container) { + container = document.createElement("div"); + container.id = "toast-container"; + container.style.cssText = ` + position: fixed; + bottom: 24px; + right: 24px; + z-index: 10000; + display: flex; + flex-direction: column; + gap: 8px; + `; + document.body.appendChild(container); + } + + const toast = document.createElement("div"); + const bgColors = { + success: "rgba(34, 197, 94, 0.95)", + error: "rgba(239, 68, 68, 0.95)", + warning: "rgba(245, 158, 11, 0.95)", + info: "rgba(59, 130, 246, 0.95)", + }; + + const icons = { + success: "✓", + error: "✕", + warning: "⚠", + info: "ℹ", + }; + + toast.style.cssText = ` + display: flex; + align-items: center; + gap: 10px; + padding: 12px 16px; + background: ${bgColors[type] || bgColors.info}; + border-radius: 10px; + color: white; + font-size: 14px; + font-weight: 500; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + animation: slideIn 0.3s ease; + `; + + toast.innerHTML = ` + ${icons[type] || icons.info} + ${message} + `; + + container.appendChild(toast); + + setTimeout(() => { + toast.style.animation = "fadeOut 0.3s ease forwards"; + setTimeout(() => toast.remove(), 300); + }, 4000); +} + +// ============================================================================= +// UTILITY FUNCTIONS +// ============================================================================= + +function debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; +} + +function formatStatus(status) { + const statusMap = { + complete: "Complete", + running: "Running", + awaiting: "Awaiting", + paused: "Paused", + blocked: "Blocked", + }; + return statusMap[status] || status; +} + +function formatTime(seconds) { + if (seconds < 60) return `${seconds}s`; + if (seconds < 3600) { + const mins = Math.floor(seconds / 60); + return `${mins}m`; + } + const hours = Math.floor(seconds / 3600); + const mins = Math.floor((seconds % 3600) / 60); + return `${hours}h ${mins}m`; +} + +// ============================================================================= +// GLOBAL STYLES FOR TOAST ANIMATIONS +// ============================================================================= -// Animation for removing tasks const style = document.createElement("style"); style.textContent = ` -@keyframes slideOut { - to { - opacity: 0; - transform: translateX(-100%); + @keyframes slideIn { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } + } + + @keyframes fadeOut { + from { + opacity: 1; + transform: translateX(0); + } + to { + opacity: 0; + transform: translateX(20px); + } } -} `; document.head.appendChild(style); -// Update stats after any HTMX request -document.body.addEventListener("htmx:afterSwap", function (evt) { - if (evt.detail.target.id === "task-list") { - updateStats(); - } -}); +// ============================================================================= +// DEMO: Simulate real-time activity +// ============================================================================= -// Initial stats load -document.addEventListener("DOMContentLoaded", function () { - updateStats(); -}); - -// Keyboard shortcuts -document.addEventListener("keydown", function (e) { - // Alt + N for new task - if (e.altKey && e.key === "n") { - e.preventDefault(); - document.querySelector(".task-input").focus(); - } - - // Alt + 1-4 for filter tabs - if (e.altKey && e.key >= "1" && e.key <= "4") { - e.preventDefault(); - const tabs = document.querySelectorAll(".filter-tab"); - const index = parseInt(e.key) - 1; - if (tabs[index]) { - tabs[index].click(); - } - } -}); +// Simulate agent activity for demo +setInterval(() => { + if (Math.random() > 0.7) { + const messages = [ + { level: "info", msg: "[SCAN] Monitoring task queues..." }, + { level: "info", msg: "[AGENT] Processing next batch..." }, + { level: "success", msg: "[OK] Checkpoint saved" }, + { level: "info", msg: "[SYNC] Synchronizing state..." }, + ]; + const { level, msg } = + messages[Math.floor(Math.random() * messages.length)]; + addAgentLog(level, msg); + } +}, 5000);