// static/admin/js/leads.js const leadsLog = window.LogConfig.createLogger('prospecting-leads'); function leadsList() { return { ...data(), currentPage: 'leads', leads: [], loading: true, error: null, // Filters minScore: 0, filterTier: '', filterChannel: '', filterIssue: '', filterHasEmail: '', // Pagination pagination: { page: 1, per_page: 20, total: 0, pages: 0 }, async init() { await I18n.loadModule('prospecting'); if (window._leadsListInit) return; window._leadsListInit = true; if (window.PlatformSettings) { this.pagination.per_page = await window.PlatformSettings.getRowsPerPage(); } leadsLog.info('Leads list initializing'); await this.loadLeads(); }, async loadLeads() { this.loading = true; this.error = null; try { const params = new URLSearchParams({ page: this.pagination.page, per_page: this.pagination.per_page, }); if (this.minScore > 0) params.set('min_score', this.minScore); if (this.filterTier) params.set('lead_tier', this.filterTier); if (this.filterChannel) params.set('channel', this.filterChannel); if (this.filterIssue) params.set('reason_flag', this.filterIssue); if (this.filterHasEmail) params.set('has_email', this.filterHasEmail); const response = await apiClient.get('/admin/prospecting/leads?' + params); this.leads = response.items || []; this.pagination.total = response.total || 0; this.pagination.pages = response.pages || 0; } catch (err) { this.error = err.message; leadsLog.error('Failed to load leads', err); } finally { this.loading = false; } }, async exportCSV() { try { const params = new URLSearchParams(); if (this.minScore > 0) params.set('min_score', this.minScore); if (this.filterTier) params.set('lead_tier', this.filterTier); if (this.filterChannel) params.set('channel', this.filterChannel); const url = '/admin/prospecting/leads/export/csv?' + params; window.open('/api/v1' + url, '_blank'); Utils.showToast('CSV export started', 'success'); } catch (err) { Utils.showToast('Export failed: ' + err.message, 'error'); } }, sendCampaign(lead) { window.location.href = '/admin/prospecting/prospects/' + lead.id + '#campaigns'; }, get startIndex() { if (this.pagination.total === 0) return 0; return (this.pagination.page - 1) * this.pagination.per_page + 1; }, get endIndex() { const end = this.pagination.page * this.pagination.per_page; return end > this.pagination.total ? this.pagination.total : end; }, get totalPages() { return this.pagination.pages; }, get pageNumbers() { const pages = []; const total = this.totalPages; const current = this.pagination.page; if (total <= 7) { for (let i = 1; i <= total; i++) pages.push(i); return pages; } pages.push(1); if (current > 3) pages.push('...'); for (let i = Math.max(2, current - 1); i <= Math.min(total - 1, current + 1); i++) pages.push(i); if (current < total - 2) pages.push('...'); pages.push(total); return pages; }, goToPage(page) { if (page === '...' || page < 1 || page > this.totalPages) return; this.pagination.page = page; this.loadLeads(); }, nextPage() { if (this.pagination.page < this.totalPages) { this.pagination.page++; this.loadLeads(); } }, previousPage() { if (this.pagination.page > 1) { this.pagination.page--; this.loadLeads(); } }, tierBadgeClass(tier) { const classes = { top_priority: 'text-red-700 bg-red-100 dark:text-red-100 dark:bg-red-700', quick_win: 'text-orange-700 bg-orange-100 dark:text-orange-100 dark:bg-orange-700', strategic: 'text-blue-700 bg-blue-100 dark:text-blue-100 dark:bg-blue-700', low_priority: 'text-gray-700 bg-gray-100 dark:text-gray-100 dark:bg-gray-700', }; return classes[tier] || classes.low_priority; }, scoreColor(score) { if (score == null) return 'text-gray-400'; if (score >= 70) return 'text-red-600'; if (score >= 50) return 'text-orange-600'; if (score >= 30) return 'text-blue-600'; return 'text-gray-600'; }, }; }