botui/ui/suite/crm/crm.html

312 lines
14 KiB
HTML
Raw Normal View History

<!-- CRM - Customer Relationship Management -->
<!-- Dynamics nomenclature: Lead → Opportunity → Account/Contact -->
<link rel="stylesheet" href="crm/crm.css">
<div class="crm-container">
<!-- Header -->
<header class="crm-header">
<div class="crm-header-left">
<h1 data-i18n="crm-title">CRM</h1>
<nav class="crm-tabs">
<button class="crm-tab active" data-view="pipeline" data-i18n="crm-pipeline">Pipeline</button>
<button class="crm-tab" data-view="leads" data-i18n="crm-leads">Leads</button>
<button class="crm-tab" data-view="opportunities" data-i18n="crm-opportunities">Opportunities</button>
<button class="crm-tab" data-view="accounts" data-i18n="crm-accounts">Accounts</button>
<button class="crm-tab" data-view="contacts" data-i18n="crm-contacts">Contacts</button>
</nav>
</div>
<div class="crm-header-right">
<div class="crm-search">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/>
</svg>
<input type="text"
placeholder="Search leads, opportunities, accounts..."
data-i18n-placeholder="crm-search-placeholder"
hx-get="/api/crm/search"
hx-trigger="keyup changed delay:300ms"
hx-target="#crm-search-results">
</div>
<button class="btn-primary" id="crm-new-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
</svg>
<span data-i18n="crm-new">New</span>
</button>
</div>
</header>
<!-- Search Results Dropdown -->
<div id="crm-search-results" class="crm-search-results"></div>
<!-- Pipeline View (Default) -->
<div id="crm-pipeline-view" class="crm-view active">
<div class="pipeline-container">
<!-- Lead Stage -->
<div class="pipeline-column" data-stage="lead">
<div class="pipeline-header">
<span class="pipeline-title" data-i18n="crm-stage-lead">Lead</span>
<span class="pipeline-count" hx-get="/api/crm/count?stage=lead" hx-trigger="load">0</span>
</div>
<div class="pipeline-cards"
hx-get="/api/crm/pipeline?stage=lead"
hx-trigger="load"
hx-swap="innerHTML">
<!-- Lead cards loaded via HTMX -->
</div>
<button class="pipeline-add" hx-get="/suite/crm/partials/lead-form.html" hx-target="#crm-modal-content" hx-on::after-request="openCrmModal()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
</svg>
<span data-i18n="crm-add-lead">Add Lead</span>
</button>
</div>
<!-- Qualified Stage -->
<div class="pipeline-column" data-stage="qualified">
<div class="pipeline-header">
<span class="pipeline-title" data-i18n="crm-stage-qualified">Qualified</span>
<span class="pipeline-count" hx-get="/api/crm/count?stage=qualified" hx-trigger="load">0</span>
</div>
<div class="pipeline-cards"
hx-get="/api/crm/pipeline?stage=qualified"
hx-trigger="load"
hx-swap="innerHTML">
</div>
</div>
<!-- Proposal Stage -->
<div class="pipeline-column" data-stage="proposal">
<div class="pipeline-header">
<span class="pipeline-title" data-i18n="crm-stage-proposal">Proposal</span>
<span class="pipeline-count" hx-get="/api/crm/count?stage=proposal" hx-trigger="load">0</span>
</div>
<div class="pipeline-cards"
hx-get="/api/crm/pipeline?stage=proposal"
hx-trigger="load"
hx-swap="innerHTML">
</div>
</div>
<!-- Negotiation Stage -->
<div class="pipeline-column" data-stage="negotiation">
<div class="pipeline-header">
<span class="pipeline-title" data-i18n="crm-stage-negotiation">Negotiation</span>
<span class="pipeline-count" hx-get="/api/crm/count?stage=negotiation" hx-trigger="load">0</span>
</div>
<div class="pipeline-cards"
hx-get="/api/crm/pipeline?stage=negotiation"
hx-trigger="load"
hx-swap="innerHTML">
</div>
</div>
<!-- Won Stage -->
<div class="pipeline-column won" data-stage="won">
<div class="pipeline-header">
<span class="pipeline-title" data-i18n="crm-stage-won">Won</span>
<span class="pipeline-count" hx-get="/api/crm/count?stage=won" hx-trigger="load">0</span>
</div>
<div class="pipeline-cards"
hx-get="/api/crm/pipeline?stage=won"
hx-trigger="load"
hx-swap="innerHTML">
</div>
</div>
<!-- Lost Stage -->
<div class="pipeline-column lost" data-stage="lost">
<div class="pipeline-header">
<span class="pipeline-title" data-i18n="crm-stage-lost">Lost</span>
<span class="pipeline-count" hx-get="/api/crm/count?stage=lost" hx-trigger="load">0</span>
</div>
<div class="pipeline-cards"
hx-get="/api/crm/pipeline?stage=lost"
hx-trigger="load"
hx-swap="innerHTML">
</div>
</div>
</div>
<!-- Pipeline Summary -->
<div class="pipeline-summary">
<div class="summary-card">
<span class="summary-label" data-i18n="crm-total-value">Total Pipeline Value</span>
<span class="summary-value" hx-get="/api/crm/stats/pipeline-value" hx-trigger="load">$0</span>
</div>
<div class="summary-card">
<span class="summary-label" data-i18n="crm-conversion-rate">Conversion Rate</span>
<span class="summary-value" hx-get="/api/crm/stats/conversion-rate" hx-trigger="load">0%</span>
</div>
<div class="summary-card">
<span class="summary-label" data-i18n="crm-avg-deal">Avg Deal Size</span>
<span class="summary-value" hx-get="/api/crm/stats/avg-deal" hx-trigger="load">$0</span>
</div>
<div class="summary-card">
<span class="summary-label" data-i18n="crm-this-month">Won This Month</span>
<span class="summary-value success" hx-get="/api/crm/stats/won-month" hx-trigger="load">$0</span>
</div>
</div>
</div>
<!-- Leads List View -->
<div id="crm-leads-view" class="crm-view">
<div class="crm-list-header">
<div class="list-filters">
<select hx-get="/api/crm/leads" hx-trigger="change" hx-target="#leads-table-body" hx-include="this">
<option value="all" data-i18n="crm-filter-all">All Leads</option>
<option value="new" data-i18n="crm-filter-new">New</option>
<option value="contacted" data-i18n="crm-filter-contacted">Contacted</option>
<option value="qualified" data-i18n="crm-filter-qualified">Qualified</option>
</select>
</div>
</div>
<table class="crm-table">
<thead>
<tr>
<th data-i18n="crm-col-name">Name</th>
<th data-i18n="crm-col-company">Company</th>
<th data-i18n="crm-col-email">Email</th>
<th data-i18n="crm-col-phone">Phone</th>
<th data-i18n="crm-col-source">Source</th>
<th data-i18n="crm-col-status">Status</th>
<th data-i18n="crm-col-created">Created</th>
<th data-i18n="crm-col-actions">Actions</th>
</tr>
</thead>
<tbody id="leads-table-body" hx-get="/api/crm/leads" hx-trigger="load">
<!-- Leads loaded via HTMX -->
</tbody>
</table>
</div>
<!-- Opportunities List View -->
<div id="crm-opportunities-view" class="crm-view">
<table class="crm-table">
<thead>
<tr>
<th data-i18n="crm-col-opportunity">Opportunity</th>
<th data-i18n="crm-col-account">Account</th>
<th data-i18n="crm-col-value">Value</th>
<th data-i18n="crm-col-stage">Stage</th>
<th data-i18n="crm-col-probability">Probability</th>
<th data-i18n="crm-col-close-date">Expected Close</th>
<th data-i18n="crm-col-owner">Owner</th>
<th data-i18n="crm-col-actions">Actions</th>
</tr>
</thead>
<tbody id="opportunities-table-body" hx-get="/api/crm/opportunities" hx-trigger="load">
</tbody>
</table>
</div>
<!-- Accounts List View -->
<div id="crm-accounts-view" class="crm-view">
<table class="crm-table">
<thead>
<tr>
<th data-i18n="crm-col-account">Account</th>
<th data-i18n="crm-col-industry">Industry</th>
<th data-i18n="crm-col-phone">Phone</th>
<th data-i18n="crm-col-city">City</th>
<th data-i18n="crm-col-revenue">Annual Revenue</th>
<th data-i18n="crm-col-contacts">Contacts</th>
<th data-i18n="crm-col-actions">Actions</th>
</tr>
</thead>
<tbody id="accounts-table-body" hx-get="/api/crm/accounts" hx-trigger="load">
</tbody>
</table>
</div>
<!-- Contacts List View -->
<div id="crm-contacts-view" class="crm-view">
<table class="crm-table">
<thead>
<tr>
<th data-i18n="crm-col-name">Name</th>
<th data-i18n="crm-col-account">Account</th>
<th data-i18n="crm-col-title">Title</th>
<th data-i18n="crm-col-email">Email</th>
<th data-i18n="crm-col-phone">Phone</th>
<th data-i18n="crm-col-actions">Actions</th>
</tr>
</thead>
<tbody id="contacts-table-body" hx-get="/api/crm/contacts" hx-trigger="load">
</tbody>
</table>
</div>
</div>
<!-- Modal for forms -->
<div id="crm-modal" class="crm-modal">
<div class="crm-modal-backdrop" onclick="closeCrmModal()"></div>
<div class="crm-modal-content" id="crm-modal-content">
<!-- Form content loaded via HTMX -->
</div>
</div>
<script>
(function() {
// Tab switching
document.querySelectorAll('.crm-tab').forEach(tab => {
tab.addEventListener('click', function() {
document.querySelectorAll('.crm-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.crm-view').forEach(v => v.classList.remove('active'));
this.classList.add('active');
const view = this.dataset.view;
document.getElementById(`crm-${view}-view`).classList.add('active');
});
});
// New button dropdown
const newBtn = document.getElementById('crm-new-btn');
newBtn.addEventListener('click', function() {
// Default: open lead form
htmx.ajax('GET', '/suite/crm/partials/lead-form.html', '#crm-modal-content').then(() => {
openCrmModal();
});
});
// Modal functions
window.openCrmModal = function() {
document.getElementById('crm-modal').classList.add('open');
};
window.closeCrmModal = function() {
document.getElementById('crm-modal').classList.remove('open');
};
// Drag and drop for pipeline
const pipelineCards = document.querySelectorAll('.pipeline-cards');
pipelineCards.forEach(column => {
column.addEventListener('dragover', e => {
e.preventDefault();
column.classList.add('drag-over');
});
column.addEventListener('dragleave', () => {
column.classList.remove('drag-over');
});
column.addEventListener('drop', e => {
e.preventDefault();
column.classList.remove('drag-over');
const cardId = e.dataTransfer.getData('text/plain');
const newStage = column.closest('.pipeline-column').dataset.stage;
// Update via HTMX
htmx.ajax('POST', `/api/crm/opportunity/${cardId}/stage`, {
values: { stage: newStage }
});
});
});
// Initialize i18n if available
if (window.i18n && window.i18n.translatePage) {
window.i18n.translatePage();
}
})();
</script>