Files
orion/app/modules/prospecting/static/admin/js/prospect-detail.js
Samir Boulahtit 28b08580c8 feat(prospecting): improve prospect detail with score details and tech badge
- Score Breakdown: show point-by-point contributions from score_breakdown
  dict, sorted by value, color-coded green (positive) vs red (negative)
- Tech Profile: prominent CMS badge (WordPress, Shopify, etc.) with
  e-commerce platform tag, "Custom / Unknown CMS" fallback
- Add SSL issuer and expiry date to tech profile card

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:24:19 +02:00

147 lines
5.2 KiB
JavaScript

// static/admin/js/prospect-detail.js
const detailLog = window.LogConfig.createLogger('prospecting-detail');
function prospectDetail(prospectId) {
return {
...data(),
currentPage: 'prospects',
prospectId: prospectId,
prospect: null,
interactions: [],
campaignSends: [],
loading: true,
error: null,
activeTab: 'overview',
tabs: [
{ id: 'overview', label: 'Overview' },
{ id: 'interactions', label: 'Interactions' },
{ id: 'campaigns', label: 'Campaigns' },
],
// Interaction modal
showInteractionModal: false,
newInteraction: {
interaction_type: 'note',
subject: '',
notes: '',
outcome: '',
next_action: '',
},
// Campaign modal
showSendCampaignModal: false,
async init() {
await I18n.loadModule('prospecting');
if (window._prospectDetailInit) return;
window._prospectDetailInit = true;
detailLog.info('Prospect detail initializing for ID:', this.prospectId);
await this.loadProspect();
},
async loadProspect() {
this.loading = true;
this.error = null;
try {
this.prospect = await apiClient.get('/admin/prospecting/prospects/' + this.prospectId);
await Promise.all([
this.loadInteractions(),
this.loadCampaignSends(),
]);
} catch (err) {
this.error = err.message;
detailLog.error('Failed to load prospect', err);
} finally {
this.loading = false;
}
},
async loadInteractions() {
try {
const resp = await apiClient.get('/admin/prospecting/prospects/' + this.prospectId + '/interactions');
this.interactions = resp.items || resp || [];
} catch (err) {
detailLog.warn('Failed to load interactions', err);
}
},
async loadCampaignSends() {
try {
const resp = await apiClient.get('/admin/prospecting/campaigns/sends?prospect_id=' + this.prospectId);
this.campaignSends = resp.items || resp || [];
} catch (err) {
detailLog.warn('Failed to load campaign sends', err);
}
},
async updateStatus() {
try {
await apiClient.put('/admin/prospecting/prospects/' + this.prospectId, {
status: this.prospect.status,
});
Utils.showToast('Status updated', 'success');
} catch (err) {
Utils.showToast('Failed: ' + err.message, 'error');
}
},
async runEnrichment() {
try {
await apiClient.post('/admin/prospecting/enrichment/full/' + this.prospectId);
Utils.showToast('Enrichment scan started', 'success');
setTimeout(() => this.loadProspect(), 5000);
} catch (err) {
Utils.showToast('Failed: ' + err.message, 'error');
}
},
async createInteraction() {
try {
await apiClient.post(
'/admin/prospecting/prospects/' + this.prospectId + '/interactions',
this.newInteraction,
);
Utils.showToast('Interaction logged', 'success');
this.showInteractionModal = false;
this.newInteraction = { interaction_type: 'note', subject: '', notes: '', outcome: '', next_action: '' };
await this.loadInteractions();
} catch (err) {
Utils.showToast('Failed: ' + err.message, 'error');
}
},
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';
},
isPositiveFlag(flag) {
var positive = ['has_website', 'has_contacts', 'has_email', 'has_phone', 'has_ssl', 'modern_cms', 'fast_site', 'mobile_friendly'];
return positive.indexOf(flag) !== -1;
},
techProfileEntries() {
const tp = this.prospect?.tech_profile;
if (!tp) return [];
const entries = [];
if (tp.cms) entries.push(['CMS', tp.cms + (tp.cms_version ? ' ' + tp.cms_version : '')]);
if (tp.server) entries.push(['Server', tp.server]);
if (tp.js_framework) entries.push(['JS Framework', tp.js_framework]);
if (tp.analytics) entries.push(['Analytics', tp.analytics]);
if (tp.ecommerce_platform) entries.push(['E-commerce', tp.ecommerce_platform]);
if (tp.hosting_provider) entries.push(['Hosting', tp.hosting_provider]);
if (tp.cdn) entries.push(['CDN', tp.cdn]);
entries.push(['SSL Valid', tp.has_valid_cert ? 'Yes' : 'No']);
return entries;
},
};
}