Files
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

154 lines
5.0 KiB
JavaScript

// noqa: js-006 - async init pattern is safe, no data loading
// static/admin/js/capture.js
const captureLog = window.LogConfig.createLogger('prospecting-capture');
function quickCapture() {
return {
...data(),
currentPage: 'capture',
form: {
business_name: '',
phone: '',
email: '',
address: '',
city: '',
postal_code: '',
source: 'street',
notes: '',
tags: [],
location_lat: null,
location_lng: null,
},
sources: [
{ value: 'street', label: 'Street' },
{ value: 'networking_event', label: 'Networking' },
{ value: 'referral', label: 'Referral' },
{ value: 'other', label: 'Other' },
],
availableTags: [
'no-website', 'gmail', 'restaurant', 'retail', 'services',
'professional', 'construction', 'beauty', 'food',
],
submitting: false,
saved: false,
lastSaved: '',
gettingLocation: false,
recentCaptures: [],
async init() {
await I18n.loadModule('prospecting');
if (window._captureInit) return;
window._captureInit = true;
captureLog.info('Quick capture initializing');
},
toggleTag(tag) {
const idx = this.form.tags.indexOf(tag);
if (idx >= 0) {
this.form.tags.splice(idx, 1);
} else {
this.form.tags.push(tag);
}
},
getLocation() {
if (!navigator.geolocation) {
Utils.showToast('Geolocation not supported', 'error');
return;
}
this.gettingLocation = true;
navigator.geolocation.getCurrentPosition(
(pos) => {
this.form.location_lat = pos.coords.latitude;
this.form.location_lng = pos.coords.longitude;
this.gettingLocation = false;
captureLog.info('Location acquired', pos.coords);
},
(err) => {
this.gettingLocation = false;
Utils.showToast('Location error: ' + err.message, 'error');
captureLog.error('Geolocation error', err);
},
{ enableHighAccuracy: true, timeout: 10000 },
);
},
async submitCapture() {
if (!this.form.business_name) {
Utils.showToast('Business name is required', 'error');
return;
}
this.submitting = true;
try {
const payload = {
channel: 'offline',
business_name: this.form.business_name,
source: this.form.source,
notes: this.form.notes,
tags: this.form.tags.length > 0 ? this.form.tags : null,
address: this.form.address || null,
city: this.form.city || null,
postal_code: this.form.postal_code || null,
location_lat: this.form.location_lat,
location_lng: this.form.location_lng,
contacts: [],
};
if (this.form.phone) {
payload.contacts.push({ contact_type: 'phone', value: this.form.phone });
}
if (this.form.email) {
payload.contacts.push({ contact_type: 'email', value: this.form.email });
}
const result = await apiClient.post('/admin/prospecting/prospects', payload);
this.lastSaved = this.form.business_name;
this.recentCaptures.unshift({
id: result.id,
business_name: this.form.business_name,
city: this.form.city,
source: this.form.source,
});
this.saved = true;
setTimeout(() => { this.saved = false; }, 3000);
this.resetForm();
Utils.showToast('Prospect saved!', 'success');
captureLog.info('Capture saved', result.id);
} catch (err) {
Utils.showToast('Failed: ' + err.message, 'error');
captureLog.error('Capture failed', err);
} finally {
this.submitting = false;
}
},
resetForm() {
this.form = {
business_name: '',
phone: '',
email: '',
address: '',
city: this.form.city, // Keep city for rapid captures in same area
postal_code: this.form.postal_code,
source: this.form.source,
notes: '',
tags: [],
location_lat: this.form.location_lat, // Keep GPS
location_lng: this.form.location_lng,
};
},
};
}