gb/CRM.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

504 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CRM App Improvement Specification
## Current State Analysis
The CRM app (`botui/ui/suite/crm/crm.html`) provides basic customer relationship management with:
- Pipeline view (Lead → Qualified → Proposal → Negotiation → Won/Lost)
- Deals, Accounts, Contacts, Campaigns tabs
- Lead form with basic fields
- Drag-and-drop pipeline cards
- HTMX-based data loading
## Critical Issues to Fix
### 1. Broken API Endpoints
**Problem:** Mixed endpoint patterns causing 404s
- Pipeline uses `/api/ui/crm/pipeline` (correct)
- Counts use `/api/ui/crm/count` AND `/api/crm/count` (inconsistent)
- Deals use `/api/ui/crm/deals` (correct)
- Stage updates use `/api/crm/opportunity/{id}/stage` (missing `/ui/`)
**Fix:**
```html
<!-- BEFORE (inconsistent) -->
<span hx-get="/api/crm/count?stage=qualified">0</span>
<span hx-get="/api/ui/crm/count?stage=new">0</span>
<!-- AFTER (consistent) -->
<span hx-get="/api/ui/crm/count?stage=qualified">0</span>
<span hx-get="/api/ui/crm/count?stage=new">0</span>
```
**Action:** Standardize ALL endpoints to `/api/ui/crm/*` pattern.
### 2. Missing Deal Card Template
**Problem:** Pipeline loads empty - no HTML template for deal cards returned by API
**Fix:** Backend must return HTML fragments like:
```html
<div class="pipeline-card" draggable="true" data-id="{{id}}">
<div class="card-header">
<h4>{{title}}</h4>
<span class="card-value">${{value}}</span>
</div>
<div class="card-body">
<p>{{company}}</p>
<span class="card-contact">{{contact_name}}</span>
</div>
<div class="card-footer">
<span class="card-probability">{{probability}}%</span>
<span class="card-date">{{close_date}}</span>
</div>
</div>
```
**Action:** Create `botserver/src/ui/crm/templates/deal_card.html` and render in API response.
### 3. Drag-and-Drop Not Working
**Problem:** Cards need `draggable="true"` and proper event handlers
**Fix:**
```javascript
// Add to pipeline card template
<div class="pipeline-card" draggable="true"
ondragstart="event.dataTransfer.setData('text/plain', this.dataset.id)">
// Update drop handler
column.addEventListener('drop', async (e) => {
e.preventDefault();
const cardId = e.dataTransfer.getData('text/plain');
const newStage = column.closest('.pipeline-column').dataset.stage;
const token = localStorage.getItem('gb_token');
await fetch(`/api/ui/crm/opportunity/${cardId}/stage`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify({ stage: newStage })
});
// Refresh pipeline
htmx.trigger('#crm-pipeline-view', 'refresh');
});
```
### 4. Form Validation Missing
**Problem:** Lead form submits incomplete data
**Fix:**
```html
<!-- Add required fields -->
<input type="text" name="first_name" required>
<input type="text" name="last_name" required>
<input type="email" name="email" required>
<!-- Add client-side validation -->
<script>
window.submitLeadForm = async function(event) {
const form = document.getElementById('leadForm');
if (!form.checkValidity()) {
form.reportValidity();
return;
}
// ... rest of submission logic
};
</script>
```
## Major Improvements Needed
### 5. Activity Timeline
**What:** Show interaction history for each deal/contact
**Implementation:**
```html
<!-- Add to detail panel -->
<div class="activity-timeline">
<div class="timeline-item">
<div class="timeline-icon">📧</div>
<div class="timeline-content">
<strong>Email sent</strong>
<p>Proposal sent to john@company.com</p>
<span class="timeline-date">2 hours ago</span>
</div>
</div>
<div class="timeline-item">
<div class="timeline-icon">📞</div>
<div class="timeline-content">
<strong>Call completed</strong>
<p>Discussed pricing and timeline</p>
<span class="timeline-date">Yesterday</span>
</div>
</div>
</div>
```
**Backend:** Add `GET /api/ui/crm/opportunity/{id}/activities` endpoint.
### 6. Quick Actions Menu
**What:** Right-click context menu on pipeline cards
**Implementation:**
```javascript
document.addEventListener('contextmenu', (e) => {
const card = e.target.closest('.pipeline-card');
if (!card) return;
e.preventDefault();
showContextMenu(e.clientX, e.clientY, [
{ label: 'Edit', action: () => editDeal(card.dataset.id) },
{ label: 'Send Email', action: () => sendEmail(card.dataset.id) },
{ label: 'Schedule Call', action: () => scheduleCall(card.dataset.id) },
{ label: 'Mark as Won', action: () => updateStage(card.dataset.id, 'won') },
{ label: 'Mark as Lost', action: () => updateStage(card.dataset.id, 'lost') },
{ label: 'Delete', action: () => deleteDeal(card.dataset.id), danger: true }
]);
});
```
### 7. Bulk Operations
**What:** Select multiple deals and perform batch actions
**Implementation:**
```html
<!-- Add checkbox to cards -->
<div class="pipeline-card">
<input type="checkbox" class="card-select" data-id="{{id}}">
<!-- ... rest of card -->
</div>
<!-- Add bulk action bar -->
<div id="bulk-actions" class="bulk-actions" style="display: none;">
<span class="bulk-count">0 selected</span>
<button onclick="bulkUpdateStage()">Change Stage</button>
<button onclick="bulkAssignOwner()">Assign Owner</button>
<button onclick="bulkDelete()">Delete</button>
</div>
<script>
document.addEventListener('change', (e) => {
if (e.target.classList.contains('card-select')) {
const selected = document.querySelectorAll('.card-select:checked');
const bulkBar = document.getElementById('bulk-actions');
if (selected.length > 0) {
bulkBar.style.display = 'flex';
bulkBar.querySelector('.bulk-count').textContent = `${selected.length} selected`;
} else {
bulkBar.style.display = 'none';
}
}
});
</script>
```
### 8. Advanced Filters
**What:** Filter deals by owner, date range, value, probability
**Implementation:**
```html
<div class="crm-filters">
<select id="filter-owner" onchange="applyFilters()">
<option value="">All Owners</option>
<!-- Populated from API -->
</select>
<input type="date" id="filter-date-from" onchange="applyFilters()">
<input type="date" id="filter-date-to" onchange="applyFilters()">
<input type="number" id="filter-value-min" placeholder="Min Value" onchange="applyFilters()">
<input type="number" id="filter-value-max" placeholder="Max Value" onchange="applyFilters()">
<select id="filter-probability" onchange="applyFilters()">
<option value="">All Probabilities</option>
<option value="high">High (>70%)</option>
<option value="medium">Medium (30-70%)</option>
<option value="low">Low (<30%)</option>
</select>
</div>
<script>
function applyFilters() {
const params = new URLSearchParams({
owner: document.getElementById('filter-owner').value,
date_from: document.getElementById('filter-date-from').value,
date_to: document.getElementById('filter-date-to').value,
value_min: document.getElementById('filter-value-min').value,
value_max: document.getElementById('filter-value-max').value,
probability: document.getElementById('filter-probability').value
});
htmx.ajax('GET', `/api/ui/crm/pipeline?${params}`, '#crm-pipeline-view');
}
</script>
```
### 9. Deal Detail Panel
**What:** Slide-out panel with full deal information
**Implementation:**
```html
<div id="deal-detail-panel" class="detail-panel">
<div class="detail-header">
<h2 id="detail-title"></h2>
<button onclick="closeDetailPanel()">×</button>
</div>
<div class="detail-tabs">
<button class="detail-tab active" data-tab="overview">Overview</button>
<button class="detail-tab" data-tab="activities">Activities</button>
<button class="detail-tab" data-tab="files">Files</button>
<button class="detail-tab" data-tab="notes">Notes</button>
</div>
<div id="detail-content" class="detail-content">
<!-- Content loaded via HTMX -->
</div>
</div>
<script>
function showDealDetail(dealId) {
const panel = document.getElementById('deal-detail-panel');
panel.classList.add('open');
htmx.ajax('GET', `/api/ui/crm/opportunity/${dealId}`, '#detail-content');
}
</script>
<style>
.detail-panel {
position: fixed;
right: -400px;
top: 0;
width: 400px;
height: 100vh;
background: var(--surface);
border-left: 1px solid var(--border);
transition: right 0.3s ease;
z-index: 1000;
}
.detail-panel.open {
right: 0;
}
</style>
```
### 10. Email Integration
**What:** Send emails directly from CRM
**Implementation:**
```html
<div id="email-composer" class="modal">
<form id="email-form">
<input type="hidden" name="deal_id" id="email-deal-id">
<div class="form-group">
<label>To</label>
<input type="email" name="to" required>
</div>
<div class="form-group">
<label>Subject</label>
<input type="text" name="subject" required>
</div>
<div class="form-group">
<label>Template</label>
<select id="email-template" onchange="loadTemplate(this.value)">
<option value="">Blank</option>
<option value="proposal">Proposal</option>
<option value="followup">Follow-up</option>
<option value="meeting">Meeting Request</option>
</select>
</div>
<div class="form-group">
<label>Message</label>
<textarea name="body" rows="10" required></textarea>
</div>
<div class="form-actions">
<button type="button" onclick="closeEmailComposer()">Cancel</button>
<button type="submit">Send Email</button>
</div>
</form>
</div>
```
**Backend:** Add `POST /api/ui/crm/opportunity/{id}/email` endpoint.
## Performance Optimizations
### 11. Lazy Loading
**Problem:** Loading all pipeline cards at once is slow
**Fix:**
```html
<!-- Load only visible stages initially -->
<div class="pipeline-cards"
hx-get="/api/ui/crm/pipeline?stage=new"
hx-trigger="intersect once"
hx-swap="innerHTML">
<div class="loading-skeleton"></div>
</div>
```
### 12. Optimistic Updates
**Problem:** Drag-and-drop feels slow waiting for server response
**Fix:**
```javascript
column.addEventListener('drop', async (e) => {
e.preventDefault();
const cardId = e.dataTransfer.getData('text/plain');
const card = document.querySelector(`[data-id="${cardId}"]`);
const newStage = column.closest('.pipeline-column').dataset.stage;
// Optimistic update - move card immediately
column.querySelector('.pipeline-cards').appendChild(card);
// Update server in background
try {
await fetch(`/api/ui/crm/opportunity/${cardId}/stage`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ stage: newStage })
});
} catch (err) {
// Revert on error
console.error('Failed to update stage:', err);
// Move card back to original column
}
});
```
## UI/UX Enhancements
### 13. Keyboard Shortcuts
```javascript
document.addEventListener('keydown', (e) => {
// Ctrl+N: New lead
if (e.ctrlKey && e.key === 'n') {
e.preventDefault();
document.getElementById('crm-new-btn').click();
}
// Ctrl+F: Focus search
if (e.ctrlKey && e.key === 'f') {
e.preventDefault();
document.querySelector('.crm-search input').focus();
}
// Escape: Close modal
if (e.key === 'Escape') {
closeCrmModal();
closeDetailPanel();
}
});
```
### 14. Empty States
```html
<!-- When no deals in stage -->
<div class="empty-state">
<svg class="empty-icon"><!-- icon --></svg>
<h3>No deals in this stage</h3>
<p>Drag deals here or create a new one</p>
<button onclick="openLeadForm()">Create Lead</button>
</div>
```
### 15. Loading States
```html
<!-- Replace "Loading..." text with skeleton -->
<div class="pipeline-card skeleton">
<div class="skeleton-line" style="width: 70%;"></div>
<div class="skeleton-line" style="width: 40%;"></div>
<div class="skeleton-line" style="width: 90%;"></div>
</div>
```
## Backend Requirements
### API Endpoints Needed
```
GET /api/ui/crm/pipeline?stage={stage}&owner={owner}&date_from={date}&date_to={date}
GET /api/ui/crm/count?stage={stage}
GET /api/ui/crm/opportunity/{id}
GET /api/ui/crm/opportunity/{id}/activities
POST /api/ui/crm/opportunity/{id}/stage
POST /api/ui/crm/opportunity/{id}/email
POST /api/ui/crm/leads
GET /api/ui/crm/deals?filter={filter}
GET /api/ui/crm/accounts
GET /api/ui/crm/contacts
GET /api/ui/crm/search?q={query}
GET /api/ui/crm/stats/pipeline-value
GET /api/ui/crm/stats/conversion-rate
GET /api/ui/crm/stats/avg-deal
GET /api/ui/crm/stats/won-month
```
### Database Schema Additions
```sql
-- Activity tracking
CREATE TABLE crm_activities (
id UUID PRIMARY KEY,
opportunity_id UUID REFERENCES crm_opportunities(id),
activity_type VARCHAR(50), -- email, call, meeting, note
subject TEXT,
description TEXT,
created_at TIMESTAMP,
created_by UUID
);
-- Email templates
CREATE TABLE crm_email_templates (
id UUID PRIMARY KEY,
name VARCHAR(255),
subject TEXT,
body TEXT,
created_at TIMESTAMP
);
```
## Implementation Priority
1. **Critical (Do First):**
- Fix API endpoint inconsistencies
- Add deal card HTML template
- Fix drag-and-drop functionality
- Add form validation
2. **High Priority:**
- Activity timeline
- Deal detail panel
- Advanced filters
- Email integration
3. **Medium Priority:**
- Bulk operations
- Quick actions menu
- Keyboard shortcuts
- Empty/loading states
4. **Nice to Have:**
- Lazy loading
- Optimistic updates
- Advanced analytics dashboard
## Testing Checklist
- [ ] Create new lead via form
- [ ] Drag deal between pipeline stages
- [ ] Search for deals/accounts
- [ ] Filter deals by owner/date/value
- [ ] View deal details in side panel
- [ ] Send email from deal
- [ ] Bulk update multiple deals
- [ ] Keyboard shortcuts work
- [ ] Mobile responsive layout
- [ ] All API endpoints return correct data