Files
orion/app/modules/prospecting/static/admin/js/prospects.js
Samir Boulahtit 6d6eba75bf
Some checks failed
CI / pytest (push) Failing after 48m31s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped
CI / ruff (push) Successful in 11s
CI / validate (push) Successful in 23s
CI / dependency-scanning (push) Successful in 28s
feat(prospecting): add complete prospecting module for lead discovery and scoring
Migrates scanning pipeline from marketing-.lu-domains app into Orion module.
Supports digital (domain scan) and offline (manual capture) lead channels
with enrichment, scoring, campaign management, and interaction tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 00:59:47 +01:00

150 lines
5.1 KiB
JavaScript

// static/admin/js/prospects.js
const prospectsLog = window.LogConfig.createLogger('prospecting-prospects');
function prospectsList() {
return {
...data(),
currentPage: 'prospects',
prospects: [],
loading: true,
error: null,
// Filters
search: '',
filterChannel: '',
filterStatus: '',
filterTier: '',
// Pagination
pagination: { page: 1, per_page: 20, total: 0, pages: 0 },
// Create modal
showCreateModal: false,
creating: false,
newProspect: {
channel: 'digital',
domain_name: '',
business_name: '',
phone: '',
email: '',
city: '',
source: 'street',
notes: '',
},
async init() {
await I18n.loadModule('prospecting');
if (window._prospectsListInit) return;
window._prospectsListInit = true;
if (window.PlatformSettings) {
this.pagination.per_page = await window.PlatformSettings.getRowsPerPage();
}
prospectsLog.info('Prospects list initializing');
await this.loadProspects();
},
async loadProspects() {
this.loading = true;
this.error = null;
try {
const params = new URLSearchParams({
page: this.pagination.page,
per_page: this.pagination.per_page,
});
if (this.search) params.set('search', this.search);
if (this.filterChannel) params.set('channel', this.filterChannel);
if (this.filterStatus) params.set('status', this.filterStatus);
if (this.filterTier) params.set('tier', this.filterTier);
const response = await apiClient.get('/admin/prospecting/prospects?' + params);
this.prospects = response.items || [];
this.pagination.total = response.total || 0;
this.pagination.pages = response.pages || 0;
} catch (err) {
this.error = err.message;
prospectsLog.error('Failed to load prospects', err);
} finally {
this.loading = false;
}
},
async createProspect() {
this.creating = true;
try {
const payload = { ...this.newProspect };
if (payload.channel === 'digital') {
delete payload.business_name;
delete payload.phone;
delete payload.email;
delete payload.city;
} else {
delete payload.domain_name;
}
await apiClient.post('/admin/prospecting/prospects', payload);
Utils.showToast('Prospect created', 'success');
this.showCreateModal = false;
this.resetNewProspect();
await this.loadProspects();
} catch (err) {
Utils.showToast('Failed: ' + err.message, 'error');
} finally {
this.creating = false;
}
},
resetNewProspect() {
this.newProspect = {
channel: 'digital',
domain_name: '',
business_name: '',
phone: '',
email: '',
city: '',
source: 'street',
notes: '',
};
},
goToPage(page) {
this.pagination.page = page;
this.loadProspects();
},
statusBadgeClass(status) {
const classes = {
pending: 'text-yellow-700 bg-yellow-100 dark:text-yellow-100 dark:bg-yellow-700',
active: 'text-green-700 bg-green-100 dark:text-green-100 dark:bg-green-700',
contacted: 'text-blue-700 bg-blue-100 dark:text-blue-100 dark:bg-blue-700',
converted: 'text-purple-700 bg-purple-100 dark:text-purple-100 dark:bg-purple-700',
inactive: 'text-gray-700 bg-gray-100 dark:text-gray-100 dark:bg-gray-700',
error: 'text-red-700 bg-red-100 dark:text-red-100 dark:bg-red-700',
};
return classes[status] || classes.pending;
},
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';
},
};
}