Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <meta name="description" content="LeadFlow β Premium lead generation dashboard for scraping Google Maps business leads and managing WhatsApp outreach."> | |
| <title>LeadFlow β Lead Generation Dashboard</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=Poppins:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="style.css"> | |
| <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>π</text></svg>"> | |
| </head> | |
| <body> | |
| <!-- ββββββββββ Sidebar ββββββββββ --> | |
| <nav class="sidebar" id="sidebar"> | |
| <div class="sidebar-brand"> | |
| <div class="sidebar-logo"> | |
| <div class="sidebar-logo-icon">π</div> | |
| <span class="sidebar-logo-text">LeadFlow</span> | |
| </div> | |
| <button class="sidebar-toggle" id="sidebar-toggle" title="Toggle sidebar"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg> | |
| </button> | |
| </div> | |
| <div class="sidebar-menu"> | |
| <a class="sidebar-link active" data-section="dashboard" onclick="scrollToSection('stats-grid')"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg> | |
| <span>Dashboard</span> | |
| </a> | |
| <a class="sidebar-link" data-section="scraper" onclick="scrollToSection('scraper-panel')"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> | |
| <span>Scraper</span> | |
| </a> | |
| <a class="sidebar-link" data-section="leads" onclick="scrollToSection('leads-panel')"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg> | |
| <span>Leads</span> | |
| </a> | |
| <a class="sidebar-link" data-section="templates" onclick="toggleTemplatesPanel()"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg> | |
| <span>Templates</span> | |
| </a> | |
| </div> | |
| <div class="sidebar-footer"> | |
| <a class="sidebar-link" onclick="exportCSV()"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> | |
| <span>Export CSV</span> | |
| </a> | |
| <div class="sidebar-divider"></div> | |
| <div class="sidebar-kbd-hint" onclick="showKeyboardShortcuts()"> | |
| <span>Shortcuts</span> | |
| <kbd>?</kbd> | |
| </div> | |
| </div> | |
| </nav> | |
| <!-- ββββββββββ Main Content ββββββββββ --> | |
| <main class="main-content" id="main-content"> | |
| <!-- ββββββββββ Top Bar ββββββββββ --> | |
| <header class="top-bar" id="top-bar"> | |
| <div class="top-bar-left"> | |
| <button class="mobile-menu-btn" id="mobile-menu-btn" title="Open menu"> | |
| <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="15" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg> | |
| </button> | |
| <div class="breadcrumb"> | |
| <span class="breadcrumb-icon">π</span> | |
| <span>Dashboard</span> | |
| <span class="breadcrumb-sep">βΊ</span> | |
| <span class="breadcrumb-current" id="breadcrumb-current">Overview</span> | |
| </div> | |
| </div> | |
| <div class="top-bar-right"> | |
| <div class="live-clock" id="live-clock"></div> | |
| <div class="connection-status" id="connection-status" title="Backend status"> | |
| <span class="connection-dot"></span> | |
| <span class="connection-text">Connected</span> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- ββββββββββ Stats Cards ββββββββββ --> | |
| <section class="stats-grid" id="stats-grid"> | |
| <div class="stat-card total"> | |
| <div class="stat-card-top"> | |
| <div class="stat-icon"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg> | |
| </div> | |
| <div class="stat-trend" id="stat-trend-total" style="display:none;">β 0</div> | |
| </div> | |
| <div class="stat-value" id="stat-total">0</div> | |
| <div class="stat-label">Total Leads</div> | |
| <div class="stat-bar"><div class="stat-bar-fill" id="bar-total" style="width:100%"></div></div> | |
| </div> | |
| <div class="stat-card new-stat"> | |
| <div class="stat-card-top"> | |
| <div class="stat-icon"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg> | |
| </div> | |
| </div> | |
| <div class="stat-value" id="stat-new">0</div> | |
| <div class="stat-label">New</div> | |
| <div class="stat-bar"><div class="stat-bar-fill" id="bar-new" style="width:0%"></div></div> | |
| </div> | |
| <div class="stat-card contacted"> | |
| <div class="stat-card-top"> | |
| <div class="stat-icon"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg> | |
| </div> | |
| </div> | |
| <div class="stat-value" id="stat-contacted">0</div> | |
| <div class="stat-label">Contacted</div> | |
| <div class="stat-bar"><div class="stat-bar-fill" id="bar-contacted" style="width:0%"></div></div> | |
| </div> | |
| <div class="stat-card replied"> | |
| <div class="stat-card-top"> | |
| <div class="stat-icon"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg> | |
| </div> | |
| </div> | |
| <div class="stat-value" id="stat-replied">0</div> | |
| <div class="stat-label">Replied</div> | |
| <div class="stat-bar"><div class="stat-bar-fill" id="bar-replied" style="width:0%"></div></div> | |
| </div> | |
| <div class="stat-card closed"> | |
| <div class="stat-card-top"> | |
| <div class="stat-icon"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg> | |
| </div> | |
| </div> | |
| <div class="stat-value" id="stat-closed">0</div> | |
| <div class="stat-label">Closed</div> | |
| <div class="stat-bar"><div class="stat-bar-fill" id="bar-closed" style="width:0%"></div></div> | |
| </div> | |
| </section> | |
| <!-- ββββββββββ Conversion Funnel ββββββββββ --> | |
| <section class="panel funnel-panel" id="funnel-panel"> | |
| <div class="panel-header"> | |
| <h2 class="panel-title"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2H2l8 9.46V18l4 2v-6.54L22 2z"/></svg> | |
| Conversion Funnel | |
| </h2> | |
| <div class="funnel-legend"> | |
| <span class="funnel-legend-item"><span class="dot new"></span>New</span> | |
| <span class="funnel-legend-item"><span class="dot contacted"></span>Contacted</span> | |
| <span class="funnel-legend-item"><span class="dot replied"></span>Replied</span> | |
| <span class="funnel-legend-item"><span class="dot closed"></span>Closed</span> | |
| </div> | |
| </div> | |
| <div class="funnel-bars" id="funnel-bars"> | |
| <div class="funnel-step"> | |
| <div class="funnel-bar-track"><div class="funnel-bar-fill new" id="funnel-new" style="width:0%"></div></div> | |
| <div class="funnel-step-info"><span class="funnel-step-label">New</span><span class="funnel-step-value" id="funnel-new-val">0</span></div> | |
| </div> | |
| <div class="funnel-step"> | |
| <div class="funnel-bar-track"><div class="funnel-bar-fill contacted" id="funnel-contacted" style="width:0%"></div></div> | |
| <div class="funnel-step-info"><span class="funnel-step-label">Contacted</span><span class="funnel-step-value" id="funnel-contacted-val">0</span></div> | |
| </div> | |
| <div class="funnel-step"> | |
| <div class="funnel-bar-track"><div class="funnel-bar-fill replied" id="funnel-replied" style="width:0%"></div></div> | |
| <div class="funnel-step-info"><span class="funnel-step-label">Replied</span><span class="funnel-step-value" id="funnel-replied-val">0</span></div> | |
| </div> | |
| <div class="funnel-step"> | |
| <div class="funnel-bar-track"><div class="funnel-bar-fill closed" id="funnel-closed" style="width:0%"></div></div> | |
| <div class="funnel-step-info"><span class="funnel-step-label">Closed</span><span class="funnel-step-value" id="funnel-closed-val">0</span></div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ββββββββββ Scraper Panel ββββββββββ --> | |
| <section class="panel collapsible-panel" id="scraper-panel"> | |
| <div class="panel-header clickable" onclick="togglePanel('scraper-panel')"> | |
| <h2 class="panel-title"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> | |
| Google Maps Scraper | |
| </h2> | |
| <div class="panel-collapse-icon"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><polyline points="6 9 12 15 18 9"/></svg> | |
| </div> | |
| </div> | |
| <div class="panel-collapsible-body" id="scraper-body"> | |
| <form class="scraper-form" id="scrape-form"> | |
| <div class="form-row"> | |
| <div class="form-group" style="flex:3; min-width:250px;"> | |
| <label for="scrape-query">Search Query</label> | |
| <div class="input-with-icon"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> | |
| <input type="text" id="scrape-query" placeholder='e.g. "gym in Mumbai", "restaurant in Delhi"' required> | |
| </div> | |
| </div> | |
| <div class="form-group" style="flex:0 0 110px;"> | |
| <label for="scrape-limit">Limit</label> | |
| <input type="number" id="scrape-limit" value="20" min="1" max="100"> | |
| </div> | |
| </div> | |
| <div class="form-row"> | |
| <label class="toggle-label" id="radius-toggle-wrap"> | |
| <div class="toggle-switch"> | |
| <input type="checkbox" id="scrape-radius-toggle"> | |
| <span class="toggle-slider"></span> | |
| </div> | |
| <span>Radius Search (Coordinates)</span> | |
| </label> | |
| </div> | |
| <div class="form-row advanced-location hidden" id="advanced-location"> | |
| <div class="form-group" style="flex:1; min-width:140px;"> | |
| <label for="scrape-lat">Latitude</label> | |
| <input type="number" step="any" id="scrape-lat" placeholder="e.g. 19.0760"> | |
| </div> | |
| <div class="form-group" style="flex:1; min-width:140px;"> | |
| <label for="scrape-lng">Longitude</label> | |
| <input type="number" step="any" id="scrape-lng" placeholder="e.g. 72.8777"> | |
| </div> | |
| <div class="form-group" style="flex:0 0 100px;"> | |
| <label for="scrape-zoom">Zoom</label> | |
| <input type="number" id="scrape-zoom" value="14" min="1" max="21" title="Higher is closer"> | |
| </div> | |
| </div> | |
| <div class="form-row"> | |
| <button type="submit" class="btn btn-primary btn-scrape-start" id="btn-scrape"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg> | |
| Start Scraping | |
| </button> | |
| </div> | |
| </form> | |
| <div class="scrape-status hidden" id="scrape-status"> | |
| <div class="spinner" id="scrape-spinner"></div> | |
| <div class="scrape-progress-wrap"> | |
| <span id="scrape-status-text">Initializing...</span> | |
| <div class="scrape-progress-bar"> | |
| <div class="scrape-progress-fill" id="scrape-progress-fill" style="width:0%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ββββββββββ Leads Panel ββββββββββ --> | |
| <section class="panel leads-panel" id="leads-panel"> | |
| <div class="panel-header"> | |
| <h2 class="panel-title"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg> | |
| Leads | |
| <span class="lead-count-badge" id="lead-count-badge">0</span> | |
| </h2> | |
| <div class="toolbar"> | |
| <div class="search-box"> | |
| <svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> | |
| <input type="text" id="search-input" placeholder="Search leads..."> | |
| <kbd class="search-kbd">βK</kbd> | |
| </div> | |
| <div class="view-toggles"> | |
| <button class="view-toggle-btn active" id="btn-view-table" title="Table view"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/></svg> | |
| </button> | |
| <button class="view-toggle-btn" id="btn-view-kanban" title="Kanban view"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="5" height="18" rx="1"/><rect x="10" y="3" width="5" height="12" rx="1"/><rect x="17" y="3" width="5" height="15" rx="1"/></svg> | |
| </button> | |
| </div> | |
| <select class="filter-select" id="filter-status"> | |
| <option value="">All Status</option> | |
| <option value="new">β¨ New</option> | |
| <option value="contacted">π Contacted</option> | |
| <option value="replied">π¬ Replied</option> | |
| <option value="closed">π― Closed</option> | |
| </select> | |
| </div> | |
| </div> | |
| <!-- Bulk actions bar --> | |
| <div class="bulk-bar hidden" id="bulk-bar"> | |
| <div class="bulk-count"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg> | |
| <span id="bulk-count-num">0</span> selected | |
| </div> | |
| <div class="bulk-actions"> | |
| <button class="btn btn-sm btn-ghost-accent" onclick="bulkUpdateStatus('contacted')">π Contacted</button> | |
| <button class="btn btn-sm btn-ghost-accent" onclick="bulkUpdateStatus('replied')">π¬ Replied</button> | |
| <button class="btn btn-sm btn-ghost-accent" onclick="bulkUpdateStatus('closed')">π― Closed</button> | |
| <button class="btn btn-sm btn-danger" onclick="bulkDelete()">ποΈ Delete</button> | |
| <button class="btn btn-sm btn-ghost" onclick="clearSelection()">β Clear</button> | |
| </div> | |
| </div> | |
| <!-- Table View --> | |
| <div class="table-wrapper" id="table-view"> | |
| <table class="leads-table" id="leads-table"> | |
| <thead> | |
| <tr> | |
| <th class="checkbox-cell"><input type="checkbox" id="select-all" title="Select all"></th> | |
| <th>Business</th> | |
| <th>Phone</th> | |
| <th class="hide-mobile">Address</th> | |
| <th class="hide-mobile">Website & Socials</th> | |
| <th>Status</th> | |
| <th class="hide-mobile">Notes</th> | |
| <th class="hide-mobile">Follow-up</th> | |
| <th>Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="leads-tbody"> | |
| <!-- Dynamic rows --> | |
| </tbody> | |
| </table> | |
| </div> | |
| <!-- Kanban Board --> | |
| <div class="kanban-board hidden" id="kanban-board"> | |
| <div class="kanban-column" data-status="new"> | |
| <div class="kanban-col-header new"> | |
| <div class="kanban-col-header-left"><span class="kanban-col-dot new"></span> New</div> | |
| <span class="col-count">0</span> | |
| </div> | |
| <div class="kanban-col-body" id="kanban-col-new"></div> | |
| </div> | |
| <div class="kanban-column" data-status="contacted"> | |
| <div class="kanban-col-header contacted"> | |
| <div class="kanban-col-header-left"><span class="kanban-col-dot contacted"></span> Contacted</div> | |
| <span class="col-count">0</span> | |
| </div> | |
| <div class="kanban-col-body" id="kanban-col-contacted"></div> | |
| </div> | |
| <div class="kanban-column" data-status="replied"> | |
| <div class="kanban-col-header replied"> | |
| <div class="kanban-col-header-left"><span class="kanban-col-dot replied"></span> Replied</div> | |
| <span class="col-count">0</span> | |
| </div> | |
| <div class="kanban-col-body" id="kanban-col-replied"></div> | |
| </div> | |
| <div class="kanban-column" data-status="closed"> | |
| <div class="kanban-col-header closed"> | |
| <div class="kanban-col-header-left"><span class="kanban-col-dot closed"></span> Closed</div> | |
| <span class="col-count">0</span> | |
| </div> | |
| <div class="kanban-col-body" id="kanban-col-closed"></div> | |
| </div> | |
| </div> | |
| <!-- Pagination --> | |
| <div class="pagination hidden" id="pagination"> | |
| <div class="pagination-info"> | |
| Showing <span id="page-range">0-0</span> of <span id="page-total">0</span> leads | |
| </div> | |
| <div class="pagination-controls" id="pagination-controls"> | |
| <!-- Dynamic page buttons --> | |
| </div> | |
| </div> | |
| <!-- Empty state --> | |
| <div class="empty-state" id="empty-state"> | |
| <div class="empty-illustration"> | |
| <svg width="120" height="120" viewBox="0 0 120 120" fill="none"> | |
| <circle cx="60" cy="60" r="50" fill="rgba(99,102,241,0.06)" stroke="rgba(99,102,241,0.15)" stroke-width="2"/> | |
| <circle cx="60" cy="60" r="35" fill="rgba(99,102,241,0.04)" stroke="rgba(99,102,241,0.1)" stroke-width="1.5"/> | |
| <path d="M50 55 L60 45 L70 55" stroke="rgba(99,102,241,0.5)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/> | |
| <line x1="60" y1="45" x2="60" y2="72" stroke="rgba(99,102,241,0.5)" stroke-width="2.5" stroke-linecap="round"/> | |
| </svg> | |
| </div> | |
| <h3>No leads yet</h3> | |
| <p class="hint">Use the scraper above to find business leads from Google Maps</p> | |
| <button class="btn btn-primary btn-sm" onclick="scrollToSection('scraper-panel')" style="margin-top:12px;"> | |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> | |
| Start Scraping | |
| </button> | |
| </div> | |
| </section> | |
| <!-- ββββββββββ Templates Panel (hidden by default) ββββββββββ --> | |
| <section class="panel hidden" id="templates-panel"> | |
| <div class="panel-header"> | |
| <h2 class="panel-title"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg> | |
| Message Templates | |
| </h2> | |
| <button class="btn btn-primary btn-sm" id="btn-add-template"> | |
| <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> | |
| New Template | |
| </button> | |
| </div> | |
| <div class="templates-list" id="templates-list"> | |
| <!-- Dynamic template cards --> | |
| </div> | |
| </section> | |
| </main><!-- /main-content --> | |
| <!-- ββββββββββ Lead Detail Drawer ββββββββββ --> | |
| <div class="drawer-overlay" id="drawer-overlay" onclick="closeDrawer()"></div> | |
| <aside class="lead-drawer" id="lead-drawer"> | |
| <div class="drawer-header"> | |
| <h3 id="drawer-lead-name">Lead Details</h3> | |
| <button class="drawer-close" onclick="closeDrawer()"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> | |
| </button> | |
| </div> | |
| <div class="drawer-body" id="drawer-body"> | |
| <!-- Filled dynamically --> | |
| </div> | |
| </aside> | |
| <!-- ββββββββββ Modal: Edit Notes ββββββββββ --> | |
| <div class="modal-overlay" id="modal-notes"> | |
| <div class="modal"> | |
| <div class="modal-header"> | |
| <h2>π Edit Notes</h2> | |
| <button class="modal-close" data-modal="modal-notes">×</button> | |
| </div> | |
| <div class="modal-body"> | |
| <div class="form-group"> | |
| <label>Notes for <strong id="notes-lead-name"></strong></label> | |
| <textarea id="notes-textarea" placeholder="Add notes about this lead..."></textarea> | |
| </div> | |
| <div class="form-group"> | |
| <label>Follow-up Date</label> | |
| <input type="date" id="notes-followup" /> | |
| </div> | |
| </div> | |
| <div class="modal-footer"> | |
| <button class="btn btn-secondary" data-modal="modal-notes">Cancel</button> | |
| <button class="btn btn-primary" id="btn-save-notes">πΎ Save Notes</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ββββββββββ Modal: WhatsApp Message ββββββββββ --> | |
| <div class="modal-overlay" id="modal-whatsapp"> | |
| <div class="modal"> | |
| <div class="modal-header"> | |
| <h2>π¬ Send WhatsApp Message</h2> | |
| <button class="modal-close" data-modal="modal-whatsapp">×</button> | |
| </div> | |
| <div class="modal-body"> | |
| <div class="wa-recipient-card"> | |
| <div class="wa-recipient-avatar" id="wa-recipient-avatar">S</div> | |
| <div> | |
| <div class="wa-recipient-name" id="wa-lead-name"></div> | |
| <div class="wa-recipient-phone" id="wa-lead-phone"></div> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label>Select Template</label> | |
| <select id="wa-template-select" class="filter-select w-full"> | |
| <!-- Populated dynamically --> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label>Message (edit before sending)</label> | |
| <textarea id="wa-message" rows="5"></textarea> | |
| </div> | |
| </div> | |
| <div class="modal-footer"> | |
| <button class="btn btn-secondary" data-modal="modal-whatsapp">Cancel</button> | |
| <button class="btn btn-whatsapp" id="btn-send-wa"> | |
| π¬ Open WhatsApp | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ββββββββββ Modal: Add/Edit Template ββββββββββ --> | |
| <div class="modal-overlay" id="modal-template"> | |
| <div class="modal"> | |
| <div class="modal-header"> | |
| <h2 id="template-modal-title">βοΈ New Template</h2> | |
| <button class="modal-close" data-modal="modal-template">×</button> | |
| </div> | |
| <div class="modal-body"> | |
| <div class="form-group"> | |
| <label>Template Name</label> | |
| <input type="text" id="template-name" placeholder="e.g. Follow-up Message"> | |
| </div> | |
| <div class="form-group"> | |
| <label>Message Content <span class="hint-text">Use {{name}} for personalization</span></label> | |
| <textarea id="template-content" rows="5" placeholder="Type your message template here..."></textarea> | |
| </div> | |
| </div> | |
| <div class="modal-footer"> | |
| <button class="btn btn-secondary" data-modal="modal-template">Cancel</button> | |
| <button class="btn btn-primary" id="btn-save-template">πΎ Save Template</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ββββββββββ Keyboard Shortcuts Modal ββββββββββ --> | |
| <div class="modal-overlay" id="modal-shortcuts"> | |
| <div class="modal modal-wide"> | |
| <div class="modal-header"> | |
| <h2>β¨οΈ Keyboard Shortcuts</h2> | |
| <button class="modal-close" data-modal="modal-shortcuts">×</button> | |
| </div> | |
| <div class="modal-body"> | |
| <div class="shortcuts-grid"> | |
| <div class="shortcut-item"><kbd>β</kbd> + <kbd>K</kbd> <span>Search leads</span></div> | |
| <div class="shortcut-item"><kbd>?</kbd> <span>Show shortcuts</span></div> | |
| <div class="shortcut-item"><kbd>1</kbd> <span>Table view</span></div> | |
| <div class="shortcut-item"><kbd>2</kbd> <span>Kanban view</span></div> | |
| <div class="shortcut-item"><kbd>E</kbd> <span>Export CSV</span></div> | |
| <div class="shortcut-item"><kbd>Esc</kbd> <span>Close modal/drawer</span></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ββββββββββ Toast Container ββββββββββ --> | |
| <div class="toast-container" id="toast-container"></div> | |
| <script src="app.js"></script> | |
| </body> | |
| </html> | |