Rename tasks-sentient to tasks (make sentient theme default)
This commit is contained in:
parent
664211d6db
commit
4451cffdb5
13 changed files with 5665 additions and 1418 deletions
983
ui/suite/base-layout-preview.html
Normal file
983
ui/suite/base-layout-preview.html
Normal file
|
|
@ -0,0 +1,983 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BotUI Suite - Base Layout Preview</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
/* =============================================================================
|
||||
SENTIENT THEME VARIABLES
|
||||
============================================================================= */
|
||||
|
||||
:root {
|
||||
--sentient-bg-primary: #0a0a0a;
|
||||
--sentient-bg-secondary: #111111;
|
||||
--sentient-bg-tertiary: #1a1a1a;
|
||||
--sentient-bg-card: #141414;
|
||||
--sentient-bg-hover: #1f1f1f;
|
||||
--sentient-accent: #c5f82a;
|
||||
--sentient-accent-dim: rgba(197, 248, 42, 0.15);
|
||||
--sentient-accent-glow: rgba(197, 248, 42, 0.3);
|
||||
--sentient-success: #22c55e;
|
||||
--sentient-warning: #f59e0b;
|
||||
--sentient-error: #ef4444;
|
||||
--sentient-info: #3b82f6;
|
||||
--sentient-text-primary: #ffffff;
|
||||
--sentient-text-secondary: #a1a1a1;
|
||||
--sentient-text-muted: #6b6b6b;
|
||||
--sentient-border: #2a2a2a;
|
||||
--sentient-border-hover: #3a3a3a;
|
||||
--sentient-radius-sm: 6px;
|
||||
--sentient-radius-md: 10px;
|
||||
--sentient-radius-lg: 16px;
|
||||
--sentient-font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--sentient-font-family);
|
||||
background: var(--sentient-bg-primary);
|
||||
color: var(--sentient-text-primary);
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
LAYOUT
|
||||
============================================================================= */
|
||||
|
||||
.suite-app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.suite-main {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.suite-content-panel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
padding: 20px 24px;
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
AI PANEL
|
||||
============================================================================= */
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.ai-panel-close:hover {
|
||||
background: var(--sentient-bg-tertiary);
|
||||
color: var(--sentient-text-primary);
|
||||
}
|
||||
|
||||
.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;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.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; }
|
||||
}
|
||||
|
||||
.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-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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.stat-card.highlight {
|
||||
border-color: var(--sentient-accent);
|
||||
}
|
||||
|
||||
.stat-card.highlight .stat-card-value {
|
||||
color: var(--sentient-accent);
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
APP HEADER
|
||||
============================================================================= */
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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 {
|
||||
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);
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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
|
||||
============================================================================= */
|
||||
|
||||
::-webkit-scrollbar { width: 6px; height: 6px; }
|
||||
::-webkit-scrollbar-track { background: var(--sentient-bg-secondary); }
|
||||
::-webkit-scrollbar-thumb { background: var(--sentient-bg-tertiary); border-radius: 3px; }
|
||||
::-webkit-scrollbar-thumb:hover { background: var(--sentient-border-hover); }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="suite-app">
|
||||
<!-- Top Header Bar -->
|
||||
<header class="suite-topbar">
|
||||
<div class="topbar-left">
|
||||
<nav class="topbar-tabs">
|
||||
<button class="topbar-tab active">Dashboard</button>
|
||||
<button class="topbar-tab">Analytics</button>
|
||||
</nav>
|
||||
|
||||
<div class="topbar-app-launcher">
|
||||
<button class="app-icon" data-app="chat" title="Chat">💬</button>
|
||||
<button class="app-icon active" data-app="files" title="Files">📁</button>
|
||||
<button class="app-icon" data-app="terminal" title="Terminal">⌨️</button>
|
||||
<button class="app-icon" data-app="tasks" title="Tasks">✓</button>
|
||||
<button class="app-icon" data-app="calendar" title="Calendar">📅</button>
|
||||
<button class="app-icon" data-app="docs" title="Docs">📄</button>
|
||||
<button class="app-icon" data-app="settings" title="Settings">⚙️</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="topbar-right">
|
||||
<button class="topbar-btn-primary">✨ New Intent</button>
|
||||
<button class="topbar-btn-icon" title="Settings">⚙️</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<main class="suite-main">
|
||||
<!-- Left: Content Panel -->
|
||||
<section class="suite-content-panel">
|
||||
<!-- Stat Cards -->
|
||||
<div class="stat-cards">
|
||||
<div class="stat-card highlight">
|
||||
<div class="stat-card-icon">📊</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Total Records</div>
|
||||
<div class="stat-card-value">12,847</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card-icon">✅</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Active</div>
|
||||
<div class="stat-card-value">8,234</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card-icon">⏳</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Pending</div>
|
||||
<div class="stat-card-value">2,156</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card-icon">📈</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Growth</div>
|
||||
<div class="stat-card-value">+24%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- App Header -->
|
||||
<div class="app-header">
|
||||
<div class="app-title-section">
|
||||
<h1>Files Manager</h1>
|
||||
<p>Manage your documents and media files</p>
|
||||
</div>
|
||||
<div class="app-actions">
|
||||
<button class="app-btn-primary">+ Upload File</button>
|
||||
<button class="app-btn-secondary">🔍</button>
|
||||
<button class="app-btn-secondary">⋯</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data Table -->
|
||||
<div class="data-table-container">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Size</th>
|
||||
<th>Modified</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>project-report.pdf</td>
|
||||
<td>PDF Document</td>
|
||||
<td>2.4 MB</td>
|
||||
<td>Dec 13, 2025</td>
|
||||
<td><span class="status-badge active">Active</span></td>
|
||||
<td>
|
||||
<div class="table-actions">
|
||||
<button class="table-action-btn">View</button>
|
||||
<button class="table-action-btn">Edit</button>
|
||||
<button class="table-action-btn delete">Delete</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>dashboard-mockup.fig</td>
|
||||
<td>Figma File</td>
|
||||
<td>8.1 MB</td>
|
||||
<td>Dec 12, 2025</td>
|
||||
<td><span class="status-badge pending">Pending</span></td>
|
||||
<td>
|
||||
<div class="table-actions">
|
||||
<button class="table-action-btn">View</button>
|
||||
<button class="table-action-btn">Edit</button>
|
||||
<button class="table-action-btn delete">Delete</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>api-documentation.md</td>
|
||||
<td>Markdown</td>
|
||||
<td>156 KB</td>
|
||||
<td>Dec 11, 2025</td>
|
||||
<td><span class="status-badge active">Active</span></td>
|
||||
<td>
|
||||
<div class="table-actions">
|
||||
<button class="table-action-btn">View</button>
|
||||
<button class="table-action-btn">Edit</button>
|
||||
<button class="table-action-btn delete">Delete</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>backup-2025-12.zip</td>
|
||||
<td>Archive</td>
|
||||
<td>45.2 MB</td>
|
||||
<td>Dec 10, 2025</td>
|
||||
<td><span class="status-badge inactive">Archived</span></td>
|
||||
<td>
|
||||
<div class="table-actions">
|
||||
<button class="table-action-btn">View</button>
|
||||
<button class="table-action-btn">Edit</button>
|
||||
<button class="table-action-btn delete">Delete</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>user-analytics.csv</td>
|
||||
<td>Spreadsheet</td>
|
||||
<td>890 KB</td>
|
||||
<td>Dec 9, 2025</td>
|
||||
<td><span class="status-badge active">Active</span></td>
|
||||
<td>
|
||||
<div class="table-actions">
|
||||
<button class="table-action-btn">View</button>
|
||||
<button class="table-action-btn">Edit</button>
|
||||
<button class="table-action-btn delete">Delete</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="data-table-footer">
|
||||
<span class="pagination-info">Showing 1-5 of 847 files</span>
|
||||
<div class="pagination-controls">
|
||||
<button class="pagination-btn">←</button>
|
||||
<button class="pagination-btn active">1</button>
|
||||
<button class="pagination-btn">2</button>
|
||||
<button class="pagination-btn">3</button>
|
||||
<button class="pagination-btn">...</button>
|
||||
<button class="pagination-btn">170</button>
|
||||
<button class="pagination-btn">→</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Right: AI Assistant Panel -->
|
||||
<aside class="suite-ai-panel">
|
||||
<div class="ai-panel-header">
|
||||
<div class="ai-panel-title">
|
||||
<span class="ai-avatar">🤖</span>
|
||||
<div>
|
||||
<h3>AI Developer</h3>
|
||||
<p class="ai-status">Desenvolvendo: CRM Deloitte</p>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ai-panel-close">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="ai-panel-messages" id="ai-messages">
|
||||
<div class="ai-message assistant">
|
||||
<div class="ai-message-bubble">Olá! Sou o AI Developer. Como posso ajudar você hoje?</div>
|
||||
</div>
|
||||
<div class="ai-message assistant">
|
||||
<div class="ai-message-bubble">Você pode me pedir para modificar campos, alterar cores, adicionar validações ou qualquer outra mudança no sistema.</div>
|
||||
</div>
|
||||
<div class="ai-message user">
|
||||
<div class="ai-message-bubble">Adicione um campo de telefone no formulário de cadastro</div>
|
||||
</div>
|
||||
<div class="ai-message assistant">
|
||||
<div class="ai-message-bubble">Perfeito! Adicionei o campo de telefone com máscara automática e validação de formato brasileiro.</div>
|
||||
<span class="ai-message-action">Ver alterações</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-quick-actions">
|
||||
<span class="quick-actions-label">AÇÕES RÁPIDAS</span>
|
||||
<div class="quick-actions-grid">
|
||||
<button class="quick-action-btn">Adicionar campo</button>
|
||||
<button class="quick-action-btn">Mudar cor</button>
|
||||
<button class="quick-action-btn">Adicionar validação</button>
|
||||
<button class="quick-action-btn">Exportar dados</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-panel-input">
|
||||
<input type="text" class="ai-input" placeholder="Digite suas modificações..." id="ai-input">
|
||||
<button class="ai-send-btn">➤</button>
|
||||
</div>
|
||||
</aside>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Tab switching
|
||||
document.querySelectorAll('.topbar-tab').forEach(tab => {
|
||||
tab.addEventListener('click', function() {
|
||||
document.querySelectorAll('.topbar-tab').forEach(t => t.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
// App icon switching
|
||||
document.querySelectorAll('.app-icon').forEach(icon => {
|
||||
icon.addEventListener('click', function() {
|
||||
document.querySelectorAll('.app-icon').forEach(i => i.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
// Quick actions
|
||||
document.querySelectorAll('.quick-action-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const action = this.textContent;
|
||||
addMessage('user', action);
|
||||
setTimeout(() => {
|
||||
addMessage('assistant', `Ação "${action}" executada com sucesso!`);
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
// Send message
|
||||
document.querySelector('.ai-send-btn').addEventListener('click', sendMessage);
|
||||
document.getElementById('ai-input').addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') sendMessage();
|
||||
});
|
||||
|
||||
function sendMessage() {
|
||||
const input = document.getElementById('ai-input');
|
||||
const message = input.value.trim();
|
||||
if (!message) return;
|
||||
|
||||
addMessage('user', message);
|
||||
input.value = '';
|
||||
|
||||
setTimeout(() => {
|
||||
addMessage('assistant', `Entendido! Processando: "${message}"`);
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function addMessage(type, content) {
|
||||
const container = document.getElementById('ai-messages');
|
||||
const div = document.createElement('div');
|
||||
div.className = `ai-message ${type}`;
|
||||
div.innerHTML = `<div class="ai-message-bubble">${content}</div>`;
|
||||
container.appendChild(div);
|
||||
container.scrollTop = container.scrollHeight;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
798
ui/suite/base-layout.css
Normal file
798
ui/suite/base-layout.css
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
98
ui/suite/base-layout.html
Normal file
98
ui/suite/base-layout.html
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<!-- =============================================================================
|
||||
BOTUI SUITE - BASE LAYOUT
|
||||
Sentient Theme with AI Assistant Panel
|
||||
============================================================================= -->
|
||||
|
||||
<link rel="stylesheet" href="/themes/sentient/sentient.css">
|
||||
<link rel="stylesheet" href="/suite/base-layout.css">
|
||||
|
||||
<div class="suite-app sentient-theme">
|
||||
<!-- Top Header Bar -->
|
||||
<header class="suite-topbar">
|
||||
<!-- Left: Navigation Tabs -->
|
||||
<div class="topbar-left">
|
||||
<nav class="topbar-tabs">
|
||||
<button class="topbar-tab active">Dashboard</button>
|
||||
<button class="topbar-tab">Analytics</button>
|
||||
</nav>
|
||||
|
||||
<!-- App Launcher -->
|
||||
<div class="topbar-app-launcher">
|
||||
<button class="app-icon" data-app="chat" title="Chat">
|
||||
<span>💬</span>
|
||||
</button>
|
||||
<button class="app-icon" data-app="files" title="Files">
|
||||
<span>📁</span>
|
||||
</button>
|
||||
<button class="app-icon" data-app="terminal" title="Terminal">
|
||||
<span>⌨️</span>
|
||||
</button>
|
||||
<button class="app-icon" data-app="tasks" title="Tasks">
|
||||
<span>✓</span>
|
||||
</button>
|
||||
<button class="app-icon" data-app="calendar" title="Calendar">
|
||||
<span>📅</span>
|
||||
</button>
|
||||
<button class="app-icon" data-app="docs" title="Docs">
|
||||
<span>📄</span>
|
||||
</button>
|
||||
<button class="app-icon" data-app="settings" title="Settings">
|
||||
<span>⚙️</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: Actions -->
|
||||
<div class="topbar-right">
|
||||
<button class="topbar-btn-primary">
|
||||
<span>✨</span> New Intent
|
||||
</button>
|
||||
<button class="topbar-btn-icon" title="Settings">⚙️</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<main class="suite-main">
|
||||
<!-- Left: Content Panel -->
|
||||
<section class="suite-content-panel" id="suite-content">
|
||||
<!-- App content goes here -->
|
||||
</section>
|
||||
|
||||
<!-- Right: AI Assistant Panel -->
|
||||
<aside class="suite-ai-panel" id="ai-panel">
|
||||
<div class="ai-panel-header">
|
||||
<div class="ai-panel-title">
|
||||
<span class="ai-avatar">🤖</span>
|
||||
<div>
|
||||
<h3>AI Developer</h3>
|
||||
<p class="ai-status">Desenvolvendo: CRM Deloitte</p>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ai-panel-close" onclick="toggleAIPanel()">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="ai-panel-messages" id="ai-messages">
|
||||
<!-- Messages will be inserted here -->
|
||||
</div>
|
||||
|
||||
<div class="ai-quick-actions">
|
||||
<span class="quick-actions-label">AÇÕES RÁPIDAS</span>
|
||||
<div class="quick-actions-grid">
|
||||
<button class="quick-action-btn">Adicionar campo</button>
|
||||
<button class="quick-action-btn">Mudar cor</button>
|
||||
<button class="quick-action-btn">Adicionar validação</button>
|
||||
<button class="quick-action-btn">Exportar dados</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-panel-input">
|
||||
<input type="text" class="ai-input" placeholder="Digite suas modificações..." id="ai-input">
|
||||
<button class="ai-send-btn" onclick="sendAIMessage()">
|
||||
<span>➤</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="/suite/base-layout.js"></script>
|
||||
192
ui/suite/base-layout.js
Normal file
192
ui/suite/base-layout.js
Normal file
|
|
@ -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 = `<div class="ai-message-bubble">${content}</div>`;
|
||||
if (action) {
|
||||
html += `<span class="ai-message-action">${action}</span>`;
|
||||
}
|
||||
|
||||
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 = `
|
||||
<div class="ai-typing-indicator">
|
||||
<span></span><span></span><span></span>
|
||||
</div>
|
||||
`;
|
||||
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();
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
@ -12,6 +12,8 @@
|
|||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="/css/base.css" />
|
||||
<link rel="stylesheet" href="/css/app.css" />
|
||||
<link rel="stylesheet" href="/themes/sentient/sentient.css" />
|
||||
<link rel="stylesheet" href="/css/ai-panel.css" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- Skip navigation link for accessibility -->
|
||||
|
|
@ -728,6 +730,44 @@
|
|||
<div id="main-content" aria-busy="false" tabindex="-1">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<!-- AI Assistant Panel (visible on all screens except Chat) -->
|
||||
<aside class="ai-assistant-panel" id="ai-panel">
|
||||
<div class="ai-panel-header">
|
||||
<div class="ai-panel-title">
|
||||
<span class="ai-avatar">🤖</span>
|
||||
<div>
|
||||
<h3>AI Assistant</h3>
|
||||
<p class="ai-status">Ready to help</p>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ai-panel-toggle" onclick="toggleAIPanel()" aria-label="Close AI Panel">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="ai-panel-messages" id="ai-messages">
|
||||
<div class="ai-message assistant">
|
||||
<div class="ai-message-bubble">Olá! Sou seu assistente AI. Posso ajudar com qualquer tarefa nesta tela.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-quick-actions">
|
||||
<span class="quick-actions-label">AÇÕES RÁPIDAS</span>
|
||||
<div class="quick-actions-grid" id="ai-quick-actions">
|
||||
<!-- Quick actions loaded dynamically based on current app -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-panel-input">
|
||||
<input type="text" class="ai-input" placeholder="Como posso ajudar?" id="ai-input"
|
||||
onkeypress="if(event.key==='Enter')sendAIMessage()">
|
||||
<button class="ai-send-btn" onclick="sendAIMessage()" aria-label="Send message">➤</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- AI Panel Toggle Button (when panel is collapsed) -->
|
||||
<button class="ai-panel-fab" id="ai-fab" onclick="toggleAIPanel()" aria-label="Open AI Assistant">
|
||||
🤖
|
||||
</button>
|
||||
</main>
|
||||
|
||||
<div class="notifications-container" id="notifications"></div>
|
||||
|
|
@ -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 =>
|
||||
`<button class="quick-action-btn" onclick="handleQuickAction('${a.action}')">${a.label}</button>`
|
||||
).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 = `<div class="ai-message-bubble">${escapeHtml(message)}</div>`;
|
||||
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 = `<div class="ai-typing-indicator"><span></span><span></span><span></span></div>`;
|
||||
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 = `<div class="ai-message-bubble">Entendi! Estou processando sua solicitação: "${escapeHtml(message)}". Como posso ajudar mais?</div>`;
|
||||
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);
|
||||
</script>
|
||||
|
||||
{% block scripts %}{% endblock %}
|
||||
|
|
|
|||
392
ui/suite/css/ai-panel.css
Normal file
392
ui/suite/css/ai-panel.css
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
200
ui/suite/drive/drive-sentient.html
Normal file
200
ui/suite/drive/drive-sentient.html
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Drive - BotUI Suite</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/themes/sentient/sentient.css">
|
||||
<link rel="stylesheet" href="/suite/base-layout.css">
|
||||
<link rel="stylesheet" href="/suite/drive/drive-sentient.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="suite-app sentient-theme">
|
||||
<!-- Top Header Bar -->
|
||||
<header class="suite-topbar">
|
||||
<div class="topbar-left">
|
||||
<nav class="topbar-tabs">
|
||||
<button class="topbar-tab active">My Files</button>
|
||||
<button class="topbar-tab">Shared</button>
|
||||
</nav>
|
||||
|
||||
<div class="topbar-app-launcher">
|
||||
<button class="app-icon" data-app="chat" title="Chat">💬</button>
|
||||
<button class="app-icon" data-app="mail" title="Mail">📧</button>
|
||||
<button class="app-icon active" data-app="drive" title="Drive">📁</button>
|
||||
<button class="app-icon" data-app="tasks" title="Tasks">✓</button>
|
||||
<button class="app-icon" data-app="calendar" title="Calendar">📅</button>
|
||||
<button class="app-icon" data-app="meet" title="Meet">📹</button>
|
||||
<button class="app-icon" data-app="paper" title="Paper">📝</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="topbar-right">
|
||||
<button class="topbar-btn-primary" onclick="uploadFile()">
|
||||
<span>⬆️</span> Upload
|
||||
</button>
|
||||
<button class="topbar-btn-icon ai-toggle active" title="AI Assistant" onclick="toggleAIPanel()">🤖</button>
|
||||
<button class="topbar-btn-icon" title="Settings">⚙️</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<main class="suite-main">
|
||||
<!-- Left: Content Panel -->
|
||||
<section class="suite-content-panel">
|
||||
<!-- Stat Cards -->
|
||||
<div class="stat-cards">
|
||||
<div class="stat-card highlight">
|
||||
<div class="stat-card-icon">💾</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Storage Used</div>
|
||||
<div class="stat-card-value">12.4 GB</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card-icon">📄</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Files</div>
|
||||
<div class="stat-card-value">1,847</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card-icon">📂</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Folders</div>
|
||||
<div class="stat-card-value">156</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card-icon">🔗</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Shared</div>
|
||||
<div class="stat-card-value">42</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- App Header -->
|
||||
<div class="app-header">
|
||||
<div class="app-title-section">
|
||||
<h1>Drive</h1>
|
||||
<p>Manage your files and folders</p>
|
||||
</div>
|
||||
<div class="app-actions">
|
||||
<div class="search-box">
|
||||
<span class="search-icon">🔍</span>
|
||||
<input type="text" placeholder="Search files..." class="search-input">
|
||||
</div>
|
||||
<button class="app-btn-secondary" onclick="toggleView('grid')">⊞</button>
|
||||
<button class="app-btn-secondary" onclick="toggleView('list')">☰</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Breadcrumb -->
|
||||
<div class="breadcrumb">
|
||||
<span class="breadcrumb-item">Home</span>
|
||||
<span class="breadcrumb-separator">›</span>
|
||||
<span class="breadcrumb-item">Projects</span>
|
||||
<span class="breadcrumb-separator">›</span>
|
||||
<span class="breadcrumb-item current">General Bots</span>
|
||||
</div>
|
||||
|
||||
<!-- File Grid -->
|
||||
<div class="file-grid" id="file-view">
|
||||
<!-- Folders -->
|
||||
<div class="file-item folder" onclick="openFolder(this)">
|
||||
<div class="file-icon">📁</div>
|
||||
<div class="file-name">Documents</div>
|
||||
<div class="file-meta">12 items</div>
|
||||
</div>
|
||||
<div class="file-item folder" onclick="openFolder(this)">
|
||||
<div class="file-icon">📁</div>
|
||||
<div class="file-name">Images</div>
|
||||
<div class="file-meta">45 items</div>
|
||||
</div>
|
||||
<div class="file-item folder" onclick="openFolder(this)">
|
||||
<div class="file-icon">📁</div>
|
||||
<div class="file-name">Source Code</div>
|
||||
<div class="file-meta">89 items</div>
|
||||
</div>
|
||||
<div class="file-item folder" onclick="openFolder(this)">
|
||||
<div class="file-icon">📁</div>
|
||||
<div class="file-name">Backups</div>
|
||||
<div class="file-meta">8 items</div>
|
||||
</div>
|
||||
|
||||
<!-- Files -->
|
||||
<div class="file-item" onclick="selectFile(this)">
|
||||
<div class="file-icon">📄</div>
|
||||
<div class="file-name">project-report.pdf</div>
|
||||
<div class="file-meta">2.4 MB</div>
|
||||
</div>
|
||||
<div class="file-item" onclick="selectFile(this)">
|
||||
<div class="file-icon">🖼️</div>
|
||||
<div class="file-name">dashboard-mockup.png</div>
|
||||
<div class="file-meta">1.8 MB</div>
|
||||
</div>
|
||||
<div class="file-item" onclick="selectFile(this)">
|
||||
<div class="file-icon">📊</div>
|
||||
<div class="file-name">analytics-2025.xlsx</div>
|
||||
<div class="file-meta">890 KB</div>
|
||||
</div>
|
||||
<div class="file-item" onclick="selectFile(this)">
|
||||
<div class="file-icon">📝</div>
|
||||
<div class="file-name">meeting-notes.md</div>
|
||||
<div class="file-meta">45 KB</div>
|
||||
</div>
|
||||
<div class="file-item" onclick="selectFile(this)">
|
||||
<div class="file-icon">🎬</div>
|
||||
<div class="file-name">demo-video.mp4</div>
|
||||
<div class="file-meta">125 MB</div>
|
||||
</div>
|
||||
<div class="file-item" onclick="selectFile(this)">
|
||||
<div class="file-icon">📦</div>
|
||||
<div class="file-name">release-v2.0.zip</div>
|
||||
<div class="file-meta">45 MB</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Right: AI Assistant Panel -->
|
||||
<aside class="suite-ai-panel" id="ai-panel">
|
||||
<div class="ai-panel-header">
|
||||
<div class="ai-panel-title">
|
||||
<span class="ai-avatar">🤖</span>
|
||||
<div>
|
||||
<h3>AI Assistant</h3>
|
||||
<p class="ai-status">File Helper</p>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ai-panel-close" onclick="toggleAIPanel()">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="ai-panel-messages" id="ai-messages">
|
||||
<div class="ai-message assistant">
|
||||
<div class="ai-message-bubble">Olá! Posso ajudar a organizar, buscar ou processar seus arquivos.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-quick-actions">
|
||||
<span class="quick-actions-label">AÇÕES RÁPIDAS</span>
|
||||
<div class="quick-actions-grid">
|
||||
<button class="quick-action-btn" onclick="aiAction('organize')">Organizar pasta</button>
|
||||
<button class="quick-action-btn" onclick="aiAction('find')">Buscar arquivo</button>
|
||||
<button class="quick-action-btn" onclick="aiAction('analyze')">Analisar conteúdo</button>
|
||||
<button class="quick-action-btn" onclick="aiAction('share')">Compartilhar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-panel-input">
|
||||
<input type="text" class="ai-input" placeholder="Peça ajuda com arquivos..." id="ai-input">
|
||||
<button class="ai-send-btn" onclick="sendAIMessage()">➤</button>
|
||||
</div>
|
||||
</aside>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="/suite/drive/drive-sentient.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
217
ui/suite/mail/mail-sentient.css
Normal file
217
ui/suite/mail/mail-sentient.css
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
232
ui/suite/mail/mail-sentient.html
Normal file
232
ui/suite/mail/mail-sentient.html
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Email - BotUI Suite</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/themes/sentient/sentient.css">
|
||||
<link rel="stylesheet" href="/suite/base-layout.css">
|
||||
<link rel="stylesheet" href="/suite/mail/mail-sentient.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="suite-app sentient-theme">
|
||||
<!-- Top Header Bar -->
|
||||
<header class="suite-topbar">
|
||||
<div class="topbar-left">
|
||||
<nav class="topbar-tabs">
|
||||
<button class="topbar-tab active">Inbox</button>
|
||||
<button class="topbar-tab">Sent</button>
|
||||
</nav>
|
||||
|
||||
<div class="topbar-app-launcher">
|
||||
<button class="app-icon" data-app="chat" title="Chat">💬</button>
|
||||
<button class="app-icon active" data-app="mail" title="Mail">📧</button>
|
||||
<button class="app-icon" data-app="drive" title="Drive">📁</button>
|
||||
<button class="app-icon" data-app="tasks" title="Tasks">✓</button>
|
||||
<button class="app-icon" data-app="calendar" title="Calendar">📅</button>
|
||||
<button class="app-icon" data-app="meet" title="Meet">📹</button>
|
||||
<button class="app-icon" data-app="paper" title="Paper">📝</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="topbar-right">
|
||||
<button class="topbar-btn-primary" onclick="composeEmail()">
|
||||
<span>✉️</span> Compose
|
||||
</button>
|
||||
<button class="topbar-btn-icon ai-toggle" title="AI Assistant" onclick="toggleAIPanel()">🤖</button>
|
||||
<button class="topbar-btn-icon" title="Settings">⚙️</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<main class="suite-main">
|
||||
<!-- Left: Content Panel -->
|
||||
<section class="suite-content-panel">
|
||||
<!-- Stat Cards -->
|
||||
<div class="stat-cards">
|
||||
<div class="stat-card highlight">
|
||||
<div class="stat-card-icon">📥</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Inbox</div>
|
||||
<div class="stat-card-value">234</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card-icon">⭐</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Starred</div>
|
||||
<div class="stat-card-value">18</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card-icon">📤</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Sent</div>
|
||||
<div class="stat-card-value">156</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card-icon">📋</div>
|
||||
<div class="stat-card-content">
|
||||
<div class="stat-card-label">Drafts</div>
|
||||
<div class="stat-card-value">5</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- App Header -->
|
||||
<div class="app-header">
|
||||
<div class="app-title-section">
|
||||
<h1>Email</h1>
|
||||
<p>Manage your inbox and communications</p>
|
||||
</div>
|
||||
<div class="app-actions">
|
||||
<div class="search-box">
|
||||
<span class="search-icon">🔍</span>
|
||||
<input type="text" placeholder="Search emails..." class="search-input">
|
||||
</div>
|
||||
<button class="app-btn-secondary">🔄</button>
|
||||
<button class="app-btn-secondary">⋯</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Email List -->
|
||||
<div class="email-list-container">
|
||||
<div class="email-list">
|
||||
<div class="email-item unread">
|
||||
<input type="checkbox" class="email-checkbox">
|
||||
<button class="email-star starred">⭐</button>
|
||||
<div class="email-sender">Maria Santos</div>
|
||||
<div class="email-content">
|
||||
<span class="email-subject">Relatório Q4 finalizado</span>
|
||||
<span class="email-preview"> - Olá, segue em anexo o relatório completo do quarto trimestre...</span>
|
||||
</div>
|
||||
<div class="email-labels">
|
||||
<span class="email-label urgent">Urgente</span>
|
||||
</div>
|
||||
<div class="email-date">10:45</div>
|
||||
</div>
|
||||
|
||||
<div class="email-item unread">
|
||||
<input type="checkbox" class="email-checkbox">
|
||||
<button class="email-star">☆</button>
|
||||
<div class="email-sender">João Silva</div>
|
||||
<div class="email-content">
|
||||
<span class="email-subject">Re: Reunião amanhã</span>
|
||||
<span class="email-preview"> - Confirmado! Estarei presente às 14h na sala de reuniões...</span>
|
||||
</div>
|
||||
<div class="email-labels">
|
||||
<span class="email-label meeting">Reunião</span>
|
||||
</div>
|
||||
<div class="email-date">09:32</div>
|
||||
</div>
|
||||
|
||||
<div class="email-item">
|
||||
<input type="checkbox" class="email-checkbox">
|
||||
<button class="email-star">☆</button>
|
||||
<div class="email-sender">General Bots</div>
|
||||
<div class="email-content">
|
||||
<span class="email-subject">Novo recurso disponível</span>
|
||||
<span class="email-preview"> - Temos o prazer de anunciar um novo recurso de automação...</span>
|
||||
</div>
|
||||
<div class="email-labels">
|
||||
<span class="email-label notification">Notificação</span>
|
||||
</div>
|
||||
<div class="email-date">Ontem</div>
|
||||
</div>
|
||||
|
||||
<div class="email-item">
|
||||
<input type="checkbox" class="email-checkbox">
|
||||
<button class="email-star starred">⭐</button>
|
||||
<div class="email-sender">Ana Costa</div>
|
||||
<div class="email-content">
|
||||
<span class="email-subject">Proposta comercial - Cliente ABC</span>
|
||||
<span class="email-preview"> - Conforme conversamos, segue a proposta atualizada com os novos termos...</span>
|
||||
</div>
|
||||
<div class="email-labels">
|
||||
<span class="email-label work">Trabalho</span>
|
||||
</div>
|
||||
<div class="email-date">Ontem</div>
|
||||
</div>
|
||||
|
||||
<div class="email-item">
|
||||
<input type="checkbox" class="email-checkbox">
|
||||
<button class="email-star">☆</button>
|
||||
<div class="email-sender">Pedro Oliveira</div>
|
||||
<div class="email-content">
|
||||
<span class="email-subject">Atualização do projeto</span>
|
||||
<span class="email-preview"> - O desenvolvimento está 80% concluído. Próxima etapa é a revisão...</span>
|
||||
</div>
|
||||
<div class="email-labels"></div>
|
||||
<div class="email-date">12 Dez</div>
|
||||
</div>
|
||||
|
||||
<div class="email-item">
|
||||
<input type="checkbox" class="email-checkbox">
|
||||
<button class="email-star">☆</button>
|
||||
<div class="email-sender">Lucas Mendes</div>
|
||||
<div class="email-content">
|
||||
<span class="email-subject">Feedback do cliente</span>
|
||||
<span class="email-preview"> - Recebi feedback positivo sobre a última entrega. Eles querem...</span>
|
||||
</div>
|
||||
<div class="email-labels">
|
||||
<span class="email-label success">Sucesso</span>
|
||||
</div>
|
||||
<div class="email-date">11 Dez</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="email-list-footer">
|
||||
<span class="pagination-info">1-6 of 234 emails</span>
|
||||
<div class="pagination-controls">
|
||||
<button class="pagination-btn">←</button>
|
||||
<button class="pagination-btn active">1</button>
|
||||
<button class="pagination-btn">2</button>
|
||||
<button class="pagination-btn">3</button>
|
||||
<button class="pagination-btn">→</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Right: AI Assistant Panel -->
|
||||
<aside class="suite-ai-panel" id="ai-panel">
|
||||
<div class="ai-panel-header">
|
||||
<div class="ai-panel-title">
|
||||
<span class="ai-avatar">🤖</span>
|
||||
<div>
|
||||
<h3>AI Assistant</h3>
|
||||
<p class="ai-status">Email Helper</p>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ai-panel-close" onclick="toggleAIPanel()">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="ai-panel-messages" id="ai-messages">
|
||||
<div class="ai-message assistant">
|
||||
<div class="ai-message-bubble">Olá! Posso ajudar com seus emails. Peça para resumir, responder ou organizar.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-quick-actions">
|
||||
<span class="quick-actions-label">AÇÕES RÁPIDAS</span>
|
||||
<div class="quick-actions-grid">
|
||||
<button class="quick-action-btn" onclick="aiAction('summarize')">Resumir email</button>
|
||||
<button class="quick-action-btn" onclick="aiAction('reply')">Gerar resposta</button>
|
||||
<button class="quick-action-btn" onclick="aiAction('translate')">Traduzir</button>
|
||||
<button class="quick-action-btn" onclick="aiAction('organize')">Organizar inbox</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-panel-input">
|
||||
<input type="text" class="ai-input" placeholder="Peça ajuda com emails..." id="ai-input">
|
||||
<button class="ai-send-btn" onclick="sendAIMessage()">➤</button>
|
||||
</div>
|
||||
</aside>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="/suite/mail/mail-sentient.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
203
ui/suite/mail/mail-sentient.js
Normal file
203
ui/suite/mail/mail-sentient.js
Normal file
|
|
@ -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 = `<div class="ai-message-bubble">${content}</div>`;
|
||||
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 = `
|
||||
<div class="ai-typing-indicator">
|
||||
<span></span><span></span><span></span>
|
||||
</div>
|
||||
`;
|
||||
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();
|
||||
}
|
||||
|
||||
})();
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
function setupKeyboardShortcuts() {
|
||||
document.addEventListener("keydown", function (e) {
|
||||
// Escape: Deselect task
|
||||
if (e.key === "Escape") {
|
||||
deselectTask();
|
||||
}
|
||||
|
||||
// Cmd/Ctrl + K: Focus search
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
|
||||
e.preventDefault();
|
||||
document.querySelector(".topbar-search-input")?.focus();
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// TASK SELECTION & FILTERING
|
||||
// =============================================================================
|
||||
|
||||
function selectTask(taskId) {
|
||||
TasksState.selectedTaskId = taskId;
|
||||
|
||||
// Update selected state in list
|
||||
document.querySelectorAll(".task-card").forEach((card) => {
|
||||
card.classList.toggle("selected", card.dataset.taskId == taskId);
|
||||
});
|
||||
|
||||
// Load task details (in real app, this would fetch from API)
|
||||
loadTaskDetails(taskId);
|
||||
}
|
||||
|
||||
function deselectTask() {
|
||||
TasksState.selectedTaskId = null;
|
||||
document.querySelectorAll(".task-card").forEach((card) => {
|
||||
card.classList.remove("selected");
|
||||
});
|
||||
}
|
||||
|
||||
function navigateTasks(direction) {
|
||||
const cards = Array.from(document.querySelectorAll(".task-card"));
|
||||
if (cards.length === 0) return;
|
||||
|
||||
const currentIndex = cards.findIndex((c) => c.classList.contains("selected"));
|
||||
let newIndex;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const taskId = cards[newIndex].dataset.taskId;
|
||||
selectTask(taskId);
|
||||
cards[newIndex].scrollIntoView({ behavior: "smooth", block: "nearest" });
|
||||
}
|
||||
|
||||
function setActiveFilter(filter, button) {
|
||||
TasksState.currentFilter = filter;
|
||||
|
||||
// Update active pill
|
||||
document.querySelectorAll(".status-pill").forEach((pill) => {
|
||||
pill.classList.remove("active");
|
||||
});
|
||||
button.classList.add("active");
|
||||
|
||||
// Filter will be handled by HTMX, but we track state
|
||||
addAgentLog("info", `[FILTER] Showing ${filter} tasks`);
|
||||
}
|
||||
|
||||
// Export tasks as JSON
|
||||
function exportTasks() {
|
||||
fetch("/api/tasks?format=json")
|
||||
function searchTasks(query) {
|
||||
if (query.length > 0) {
|
||||
addAgentLog("info", `[SEARCH] Searching: "${query}"`);
|
||||
}
|
||||
|
||||
// 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((tasks) => {
|
||||
const dataStr = JSON.stringify(tasks, null, 2);
|
||||
const dataUri =
|
||||
"data:application/json;charset=utf-8," +
|
||||
encodeURIComponent(dataStr);
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
showToast("Decision applied successfully", "success");
|
||||
addAgentLog("success", `[OK] Decision applied, task resuming`);
|
||||
|
||||
const exportFileDefaultName = `tasks-${new Date().toISOString().split("T")[0]}.json`;
|
||||
|
||||
const linkElement = document.createElement("a");
|
||||
linkElement.setAttribute("href", dataUri);
|
||||
linkElement.setAttribute("download", exportFileDefaultName);
|
||||
linkElement.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;
|
||||
|
||||
// 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;
|
||||
|
||||
// Update footer text
|
||||
const footerText = document.getElementById("footer-text");
|
||||
if (stats.active === 0) {
|
||||
footerText.innerHTML = "All tasks completed! 🎉";
|
||||
// Hide decision section (in real app, would update via HTMX)
|
||||
const decisionSection = document.querySelector(
|
||||
".decision-required-section",
|
||||
);
|
||||
if (decisionSection) {
|
||||
decisionSection.style.display = "none";
|
||||
}
|
||||
} else {
|
||||
footerText.innerHTML = `<strong>${stats.active}</strong> ${stats.active === 1 ? "task" : "tasks"} remaining`;
|
||||
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`);
|
||||
|
||||
// Show/hide footer
|
||||
const footer = document.getElementById("task-footer");
|
||||
footer.style.display = stats.total > 0 ? "flex" : "none";
|
||||
});
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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");
|
||||
|
||||
fetch(`/api/tasks/${taskId}/priority`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ priority }),
|
||||
}).then(() => {
|
||||
btn.classList.toggle("active");
|
||||
updateStats();
|
||||
});
|
||||
}
|
||||
|
||||
// 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 input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.className = "task-edit-input";
|
||||
input.value = currentText;
|
||||
|
||||
taskText.replaceWith(input);
|
||||
input.focus();
|
||||
input.select();
|
||||
|
||||
input.addEventListener("blur", function () {
|
||||
saveEdit();
|
||||
});
|
||||
|
||||
input.addEventListener("keydown", function (e) {
|
||||
if (e.key === "Enter") {
|
||||
saveEdit();
|
||||
} else if (e.key === "Escape") {
|
||||
cancelEdit();
|
||||
}
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
function cancelEdit() {
|
||||
const span = document.createElement("span");
|
||||
span.className = "task-text";
|
||||
span.textContent = currentText;
|
||||
input.replaceWith(span);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete task
|
||||
if (e.target.closest('[data-action="delete"]')) {
|
||||
const btn = e.target.closest('[data-action="delete"]');
|
||||
const taskId = btn.dataset.taskId;
|
||||
|
||||
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";
|
||||
const decisionSection = document.querySelector(
|
||||
".decision-required-section",
|
||||
);
|
||||
if (decisionSection) {
|
||||
decisionSection.style.opacity = "0.5";
|
||||
setTimeout(() => {
|
||||
taskItem.remove();
|
||||
updateStats();
|
||||
}, 300);
|
||||
});
|
||||
decisionSection.style.display = "none";
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Animation for removing tasks
|
||||
// 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 = "○";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// AGENT ACTIVITY LOG
|
||||
// =============================================================================
|
||||
|
||||
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 = `
|
||||
<span class="activity-timestamp">${timestamp}</span>
|
||||
<span class="activity-message">${message}</span>
|
||||
`;
|
||||
|
||||
// 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 = `
|
||||
<span style="font-size: 16px;">${icons[type] || icons.info}</span>
|
||||
<span>${message}</span>
|
||||
`;
|
||||
|
||||
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
|
||||
// =============================================================================
|
||||
|
||||
const style = document.createElement("style");
|
||||
style.textContent = `
|
||||
@keyframes slideOut {
|
||||
@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(-100%);
|
||||
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();
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 5000);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue