gb/EMAIL.md
Rodrigo Rodriguez (Pragmatismo) f3bad05e76
Some checks failed
BotServer CI / build (push) Failing after 13s
Fix LXD socket handling in container mode
2026-03-15 18:19:22 -03:00

461 lines
12 KiB
Markdown

# Email App Improvement Specification
## Outlook + Gmail + Lotus Notes Best Features
## Current State Analysis
The Email app (`botui/ui/suite/mail/mail.html`) has:
-**3-panel layout**: Sidebar, List, Content
-**Folders**: Inbox, Starred, Sent, Scheduled, Drafts, Tracking, Spam, Trash
-**Compose modal**: Rich text editor with formatting toolbar
-**Features**: Templates, Signatures, Rules, Auto-responder, Multi-account
-**Scheduling**: Send later functionality
-**Tracking**: Email open tracking
-**Labels**: Color-coded labels
## 🎯 Best-of-Breed Improvements
### 1. Snooze Emails (Gmail)
**What:** Temporarily hide emails until a specific time
**Implementation:**
```html
<div class="snooze-menu">
<button onclick="snoozeUntil('later-today')">
Later today (6:00 PM)
</button>
<button onclick="snoozeUntil('tomorrow')">
Tomorrow (8:00 AM)
</button>
<button onclick="snoozeUntil('this-weekend')">
This weekend (Sat 9:00 AM)
</button>
<button onclick="snoozeUntil('next-week')">
Next week (Mon 8:00 AM)
</button>
<button onclick="snoozeCustom()">
Pick date & time...
</button>
</div>
<script>
async function snoozeUntil(preset) {
const emailIds = getSelectedEmails();
await fetch('/api/email/snooze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email_ids: emailIds, preset })
});
refreshMailList();
}
</script>
```
### 2. Smart Compose (Gmail AI)
**What:** AI-powered sentence completion while typing
**Implementation:**
```javascript
let composeTimeout;
document.getElementById('compose-body').addEventListener('input', async (e) => {
clearTimeout(composeTimeout);
composeTimeout = setTimeout(async () => {
const text = e.target.textContent;
if (text.length < 10) return;
const response = await fetch('/api/email/smart-compose', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ context: text })
});
const { suggestion } = await response.json();
if (suggestion) showSuggestion(suggestion);
}, 500);
});
// Accept with Tab key
document.addEventListener('keydown', (e) => {
if (e.key === 'Tab' && hasSuggestion()) {
e.preventDefault();
acceptSuggestion();
}
});
</script>
```
### 3. Nudges (Gmail Reminder)
**What:** Remind to follow up on emails without replies
**Implementation:**
```html
<div class="nudge-banner">
<span>You haven't replied to <strong>John Doe</strong> in 3 days</span>
<button onclick="replyToNudge('email-123')">Reply</button>
<button onclick="dismissNudge('email-123')">Dismiss</button>
</div>
<script>
async function checkNudges() {
const response = await fetch('/api/email/nudges');
const { nudges } = await response.json();
nudges.forEach(showNudgeBanner);
}
setInterval(checkNudges, 3600000); // Check hourly
</script>
```
### 4. Conversation Threading (Outlook)
**What:** Group related emails together
**Implementation:**
```html
<div class="mail-thread" data-thread-id="abc123">
<div class="thread-header" onclick="toggleThread(this)">
<span class="thread-icon"></span>
<span class="thread-sender">John Doe</span>
<span class="thread-subject">Project Discussion</span>
<span class="thread-count">3 messages</span>
<span class="thread-time">Today</span>
</div>
<div class="thread-messages" style="display: none;">
<!-- Individual messages -->
</div>
</div>
```
### 5. Follow-Up Flags (Lotus Notes)
**What:** Flag emails with specific follow-up dates
**Implementation:**
```html
<div class="follow-up-menu">
<button onclick="flagForFollowUp('today')">
🚩 Today
</button>
<button onclick="flagForFollowUp('tomorrow')">
🚩 Tomorrow
</button>
<button onclick="flagForFollowUp('this-week')">
🚩 This Week
</button>
<button onclick="flagForFollowUp('next-week')">
🚩 Next Week
</button>
<button onclick="flagForFollowUp('custom')">
🚩 Custom Date...
</button>
<button onclick="clearFlag()">
Clear Flag
</button>
</div>
<script>
async function flagForFollowUp(when) {
const emailIds = getSelectedEmails();
await fetch('/api/email/flag', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email_ids: emailIds, follow_up: when })
});
refreshMailList();
}
</script>
```
### 6. To-Do Integration (Lotus Notes)
**What:** Convert emails to tasks
**Implementation:**
```html
<button class="action-btn" onclick="convertToTask()">
<svg><!-- checkbox icon --></svg>
<span>Add to Tasks</span>
</button>
<script>
async function convertToTask() {
const email = getCurrentEmail();
const response = await fetch('/api/tasks/from-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: email.subject,
description: email.body,
due_date: null,
email_id: email.id
})
});
const { task_id } = await response.json();
showNotification(`Task created: ${email.subject}`);
}
</script>
```
### 7. Reading Pane Options (Outlook)
**What:** Toggle between right pane, bottom pane, or no pane
**Implementation:**
```html
<div class="view-options">
<button onclick="setReadingPane('right')" title="Right pane">
<svg><!-- layout icon --></svg>
</button>
<button onclick="setReadingPane('bottom')" title="Bottom pane">
<svg><!-- layout icon --></svg>
</button>
<button onclick="setReadingPane('off')" title="Hide pane">
<svg><!-- layout icon --></svg>
</button>
</div>
<script>
function setReadingPane(position) {
const layout = document.querySelector('.mail-layout');
layout.className = `mail-layout reading-pane-${position}`;
localStorage.setItem('reading_pane', position);
}
</script>
<style>
.mail-layout.reading-pane-right {
grid-template-columns: 250px 400px 1fr;
}
.mail-layout.reading-pane-bottom {
grid-template-columns: 250px 1fr;
grid-template-rows: 1fr 300px;
}
.mail-layout.reading-pane-off .mail-content {
display: none;
}
</style>
```
### 8. Sweep and Clean Up (Outlook)
**What:** Bulk delete/archive emails from specific senders
**Implementation:**
```html
<div class="sweep-menu">
<h4>Clean up emails from: <strong id="sweep-sender"></strong></h4>
<button onclick="sweepDelete('all')">
Delete all emails from this sender
</button>
<button onclick="sweepDelete('older-than-10')">
Delete emails older than 10 days
</button>
<button onclick="sweepArchive('all')">
Archive all emails from this sender
</button>
<button onclick="sweepMove('folder')">
Move all to folder...
</button>
</div>
<script>
async function sweepDelete(criteria) {
const sender = document.getElementById('sweep-sender').textContent;
await fetch('/api/email/sweep', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sender, action: 'delete', criteria })
});
refreshMailList();
}
</script>
```
### 9. Replication Conflicts (Lotus Notes)
**What:** Show when email was modified in multiple places
**Implementation:**
```html
<div class="conflict-banner" style="background: #fff3cd;">
<svg><!-- warning icon --></svg>
<span>This email has conflicting versions</span>
<button onclick="resolveConflict()">Resolve</button>
</div>
<div class="conflict-resolver">
<div class="conflict-version">
<h4>Version 1 (Local)</h4>
<div class="version-content">...</div>
<button onclick="keepVersion(1)">Keep This</button>
</div>
<div class="conflict-version">
<h4>Version 2 (Server)</h4>
<div class="version-content">...</div>
<button onclick="keepVersion(2)">Keep This</button>
</div>
</div>
```
### 10. Offline Mode (Gmail + Lotus Notes)
**What:** Read and compose emails offline, sync when online
**Implementation:**
```javascript
// Service Worker for offline support
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw-email.js');
}
// Cache emails for offline access
async function cacheEmailsForOffline() {
const cache = await caches.open('email-cache-v1');
const emails = await fetch('/api/email/list?folder=inbox');
const data = await emails.json();
// Cache email list
await cache.put('/api/email/list?folder=inbox', new Response(JSON.stringify(data)));
// Cache individual emails
for (const email of data.emails) {
const response = await fetch(`/api/email/${email.id}`);
await cache.put(`/api/email/${email.id}`, response.clone());
}
}
// Queue actions when offline
const offlineQueue = [];
async function sendEmailOffline(emailData) {
if (navigator.onLine) {
return await fetch('/api/email/send', {
method: 'POST',
body: JSON.stringify(emailData)
});
} else {
offlineQueue.push({ action: 'send', data: emailData });
localStorage.setItem('email_offline_queue', JSON.stringify(offlineQueue));
showNotification('Email queued. Will send when online.');
}
}
// Sync when back online
window.addEventListener('online', async () => {
const queue = JSON.parse(localStorage.getItem('email_offline_queue') || '[]');
for (const item of queue) {
if (item.action === 'send') {
await fetch('/api/email/send', {
method: 'POST',
body: JSON.stringify(item.data)
});
}
}
localStorage.removeItem('email_offline_queue');
showNotification('Offline emails sent!');
});
</script>
```
## 🔧 Backend API Requirements
```
# Gmail Features
POST /api/email/snooze - Snooze emails
POST /api/email/smart-compose - Get AI suggestions
GET /api/email/nudges - Get follow-up reminders
# Outlook Features
GET /api/email/thread/:id - Get conversation thread
POST /api/email/sweep - Bulk delete/archive
# Lotus Notes Features
POST /api/email/flag - Flag for follow-up
POST /api/tasks/from-email - Convert email to task
GET /api/email/conflicts - Get replication conflicts
POST /api/email/resolve-conflict - Resolve conflict
# Offline Support
GET /api/email/offline-sync - Sync offline changes
```
## 📊 Database Schema
```sql
-- Snoozed emails
CREATE TABLE email_snooze (
id UUID PRIMARY KEY,
email_id UUID REFERENCES emails(id),
snooze_until TIMESTAMP,
created_at TIMESTAMP
);
-- Follow-up flags
CREATE TABLE email_flags (
id UUID PRIMARY KEY,
email_id UUID REFERENCES emails(id),
follow_up_date DATE,
flag_type VARCHAR(50),
completed BOOLEAN DEFAULT FALSE
);
-- Nudges
CREATE TABLE email_nudges (
id UUID PRIMARY KEY,
email_id UUID REFERENCES emails(id),
last_sent TIMESTAMP,
dismissed BOOLEAN DEFAULT FALSE
);
-- Offline queue
CREATE TABLE email_offline_queue (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
action VARCHAR(50),
data JSONB,
created_at TIMESTAMP
);
-- Threading
ALTER TABLE emails ADD COLUMN thread_id UUID;
ALTER TABLE emails ADD COLUMN in_reply_to UUID;
```
## ✅ Implementation Priority
### Phase 1: Gmail Features (Week 1)
1. Snooze emails
2. Smart compose
3. Nudges
### Phase 2: Outlook Features (Week 2)
4. Conversation threading
5. Reading pane options
6. Sweep and clean up
### Phase 3: Lotus Notes Features (Week 3)
7. Follow-up flags
8. To-do integration
9. Offline mode
## 🧪 Testing Checklist
- [ ] Snooze email until tomorrow
- [ ] Smart compose suggests text
- [ ] Nudge appears for old emails
- [ ] Thread groups related emails
- [ ] Reading pane switches layouts
- [ ] Sweep deletes all from sender
- [ ] Flag email for follow-up
- [ ] Convert email to task
- [ ] Compose email offline
- [ ] Sync offline emails when online