592 lines
23 KiB
JavaScript
592 lines
23 KiB
JavaScript
|
|
/**
|
|||
|
|
* Export Intel — profiel popup (imperatief, geen Alpine in modal).
|
|||
|
|
*/
|
|||
|
|
(function (global) {
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
function normalizeWebUrl(u) {
|
|||
|
|
if (!u) return '';
|
|||
|
|
u = String(u).trim();
|
|||
|
|
if (!u) return '';
|
|||
|
|
if (!/^https?:\/\//i.test(u)) u = 'https://' + u.replace(/^\/+/, '');
|
|||
|
|
return u;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
function contactGapHint(e) {
|
|||
|
|
var r = e.reach || {};
|
|||
|
|
var hasDirect = !!(e.phone || e.email || e.website || (r.phones && r.phones.length) || (r.emails && r.emails.length) || (r.websites && r.websites.length));
|
|||
|
|
if (hasDirect) return '';
|
|||
|
|
var src = e.source || 'osm';
|
|||
|
|
return '<p class="ei-drawer-empty ei-drawer-hint">Geen telefoon of e-mail in bron (' + esc(src) + '). Gratis opties: <strong>Bewerken</strong> (handmatig), of <strong>Sync contacten</strong> (scant websites) / <strong>Sync distributeurs</strong> (haalt OSM-tags opnieuw).</p>';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function reachSectionHtml(e) {
|
|||
|
|
var r = e.reach || {};
|
|||
|
|
var html = '';
|
|||
|
|
function links(title, items, render) {
|
|||
|
|
if (!items || !items.length) return;
|
|||
|
|
html += '<div class="ei-reach-block"><h5>' + esc(title) + '</h5>';
|
|||
|
|
for (var i = 0; i < items.length; i++) html += render(items[i]);
|
|||
|
|
html += '</div>';
|
|||
|
|
}
|
|||
|
|
links('E-mail', r.emails, function (it) {
|
|||
|
|
return '<div class="ei-drawer-kv"><span>' + esc(it.label || 'E-mail') + '</span><a href="' + esc(it.href || ('mailto:' + it.value)) + '">' + esc(it.value) + '</a></div>';
|
|||
|
|
});
|
|||
|
|
links('Telefoon', r.phones, function (it) {
|
|||
|
|
return '<div class="ei-drawer-kv"><span>' + esc(it.label || 'Tel') + '</span><a href="' + esc(it.href || ('tel:' + it.value)) + '">' + esc(it.value) + '</a></div>';
|
|||
|
|
});
|
|||
|
|
if (!r.emails || !r.emails.length) {
|
|||
|
|
if (e.email) html += '<div class="ei-drawer-kv"><span>E-mail</span><a href="mailto:' + esc(e.email) + '">' + esc(e.email) + '</a></div>';
|
|||
|
|
}
|
|||
|
|
if (!r.phones || !r.phones.length) {
|
|||
|
|
if (e.phone) html += '<div class="ei-drawer-kv"><span>Telefoon</span><a href="tel:' + esc(e.phone) + '">' + esc(e.phone) + '</a></div>';
|
|||
|
|
}
|
|||
|
|
html += websitesHtml(e);
|
|||
|
|
links('Adres', r.addresses, function (it) {
|
|||
|
|
return '<div class="ei-drawer-kv"><span>' + esc(it.label || 'Adres') + '</span><span>' + esc(it.value) + '</span></div>';
|
|||
|
|
});
|
|||
|
|
links('Social', r.social, function (it) {
|
|||
|
|
return '<div class="ei-drawer-kv"><span>' + esc(it.label || 'Link') + '</span><a href="' + esc(it.href) + '" target="_blank" rel="noopener">' + esc(it.value) + '</a></div>';
|
|||
|
|
});
|
|||
|
|
if (r.maps_url) {
|
|||
|
|
html += '<div class="ei-drawer-kv"><span>Maps</span><a href="' + esc(r.maps_url) + '" target="_blank" rel="noopener">Google Maps →</a></div>';
|
|||
|
|
}
|
|||
|
|
return html;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function websitesHtml(e) {
|
|||
|
|
var sites = e.websites;
|
|||
|
|
if ((!sites || !sites.length) && e.website) {
|
|||
|
|
sites = [{ label: 'Organisatie', url: e.website, display: e.website }];
|
|||
|
|
}
|
|||
|
|
if (!sites || !sites.length) return '';
|
|||
|
|
var rows = '';
|
|||
|
|
for (var i = 0; i < sites.length; i++) {
|
|||
|
|
var s = sites[i];
|
|||
|
|
var href = normalizeWebUrl(s.url || s);
|
|||
|
|
if (!href) continue;
|
|||
|
|
rows += '<div class="ei-drawer-kv"><span>' + esc(s.label || 'Website') + '</span>' +
|
|||
|
|
'<a href="' + esc(href) + '" target="_blank" rel="noopener">' + esc(s.display || s.url || href) + '</a></div>';
|
|||
|
|
}
|
|||
|
|
return rows;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function esc(s) {
|
|||
|
|
if (s == null) return '';
|
|||
|
|
return String(s)
|
|||
|
|
.replace(/&/g, '&')
|
|||
|
|
.replace(/</g, '<')
|
|||
|
|
.replace(/>/g, '>')
|
|||
|
|
.replace(/"/g, '"');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function typeLabel(t) {
|
|||
|
|
return (t || '').replace(/_/g, ' ');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function getApp() {
|
|||
|
|
return global.__exportIntelApp || null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function territoryName(iso2) {
|
|||
|
|
var app = getApp();
|
|||
|
|
if (!iso2) return '';
|
|||
|
|
if (app && app.territories) {
|
|||
|
|
for (var i = 0; i < app.territories.length; i++) {
|
|||
|
|
if (app.territories[i].country_iso2 === iso2) return app.territories[i].name_nl;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return iso2;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function formatCoords(lat, lon) {
|
|||
|
|
if (lat == null || lon == null) return '';
|
|||
|
|
var a = Number(lat);
|
|||
|
|
var b = Number(lon);
|
|||
|
|
if (!Number.isFinite(a) || !Number.isFinite(b)) return '';
|
|||
|
|
return a.toFixed(5) + ', ' + b.toFixed(5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function mountEl() {
|
|||
|
|
return document.getElementById('ei-profile-mount');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function activeMapId(app) {
|
|||
|
|
if (!app) return 'ei-map';
|
|||
|
|
if (app.tab === 'map') return 'ei-map';
|
|||
|
|
if (app.tabMapId && typeof app.tabMapId === 'function') {
|
|||
|
|
return app.tabMapId() || 'ei-map';
|
|||
|
|
}
|
|||
|
|
return 'ei-map-' + (app.tab || 'map');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var Profile = {
|
|||
|
|
_busy: false,
|
|||
|
|
|
|||
|
|
showShell: function (on) {
|
|||
|
|
var el = document.getElementById('ei-profile-modal');
|
|||
|
|
if (!el) return;
|
|||
|
|
if (on) {
|
|||
|
|
el.classList.add('is-open');
|
|||
|
|
el.setAttribute('aria-hidden', 'false');
|
|||
|
|
document.body.classList.add('ei-profile-open');
|
|||
|
|
} else {
|
|||
|
|
el.classList.remove('is-open');
|
|||
|
|
el.setAttribute('aria-hidden', 'true');
|
|||
|
|
document.body.classList.remove('ei-profile-open');
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
clearMount: function () {
|
|||
|
|
var m = mountEl();
|
|||
|
|
if (m) m.innerHTML = '';
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
renderLoading: function () {
|
|||
|
|
var m = mountEl();
|
|||
|
|
if (!m) return;
|
|||
|
|
m.innerHTML =
|
|||
|
|
'<div class="ei-drawer-loading" style="padding:3rem 1.5rem;text-align:center">' +
|
|||
|
|
'<span class="ei-map-spinner"></span>' +
|
|||
|
|
'<p style="margin-top:1rem;color:#94a3b8">Profiel laden…</p></div>';
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
renderError: function (msg) {
|
|||
|
|
var m = mountEl();
|
|||
|
|
if (!m) return;
|
|||
|
|
m.innerHTML =
|
|||
|
|
'<div class="ei-drawer-loading" style="padding:2rem 1.5rem">' +
|
|||
|
|
'<p style="color:#fecdd3;margin-bottom:1rem">' + esc(msg || 'Laden mislukt') + '</p>' +
|
|||
|
|
'<button type="button" class="btn btn-sm" data-ei-action="close">Sluiten</button></div>';
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
patchEntityInApp: function (app, entity) {
|
|||
|
|
if (!app || !entity || !entity.id) return;
|
|||
|
|
var id = entity.id;
|
|||
|
|
if (Array.isArray(app.entities)) {
|
|||
|
|
for (var i = 0; i < app.entities.length; i++) {
|
|||
|
|
if (app.entities[i].id === id) {
|
|||
|
|
Object.assign(app.entities[i], entity);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (Array.isArray(app.crmPipeline)) {
|
|||
|
|
for (var j = 0; j < app.crmPipeline.length; j++) {
|
|||
|
|
if (app.crmPipeline[j].id === id) {
|
|||
|
|
Object.assign(app.crmPipeline[j], entity);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (Array.isArray(app.halalTopEntities)) {
|
|||
|
|
for (var k = 0; k < app.halalTopEntities.length; k++) {
|
|||
|
|
if (app.halalTopEntities[k].id === id) {
|
|||
|
|
Object.assign(app.halalTopEntities[k], entity);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
renderFull: function (e, app) {
|
|||
|
|
var m = mountEl();
|
|||
|
|
if (!m || !e) return;
|
|||
|
|
app = app || getApp();
|
|||
|
|
|
|||
|
|
var contacts = e.contacts || [];
|
|||
|
|
var contactRows = '';
|
|||
|
|
for (var i = 0; i < contacts.length; i++) {
|
|||
|
|
var c = contacts[i];
|
|||
|
|
contactRows +=
|
|||
|
|
'<div class="ei-contact-row">' +
|
|||
|
|
'<strong>' + esc(c.name || c.role || 'Contact') + '</strong>' +
|
|||
|
|
(c.role ? '<span class="ei-contact-role">' + esc(c.role) + '</span>' : '') +
|
|||
|
|
(c.email ? '<div>📧 <a href="mailto:' + esc(c.email) + '">' + esc(c.email) + '</a></div>' : '') +
|
|||
|
|
(c.phone ? '<div>📞 <a href="tel:' + esc(c.phone) + '">' + esc(c.phone) + '</a></div>' : '') +
|
|||
|
|
(c.website_contact_url ? '<div>🌐 <a href="' + esc(normalizeWebUrl(c.website_contact_url)) + '" target="_blank" rel="noopener">' + esc(c.website_contact_url) + '</a></div>' : '') +
|
|||
|
|
(c.linkedin_url ? '<div>in <a href="' + esc(normalizeWebUrl(c.linkedin_url)) + '" target="_blank" rel="noopener">LinkedIn</a></div>' : '') +
|
|||
|
|
'</div>';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var halalTags = '';
|
|||
|
|
var labels = e.halal_reason_labels || [];
|
|||
|
|
for (var j = 0; j < labels.length; j++) {
|
|||
|
|
halalTags += '<span class="ei-halal-tag">' + esc(labels[j]) + '</span>';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var crmBtn = e.client_id
|
|||
|
|
? '<a class="ei-action-tile ei-action-crm-done" href="/clients"><span class="ei-action-icon">✓</span><span class="ei-action-label">Open CRM</span><span class="ei-action-sub">' +
|
|||
|
|
esc(e.crm_client_name || ('Klant #' + e.client_id)) + '</span></a>'
|
|||
|
|
: '<button type="button" class="ei-action-tile ei-action-crm" data-ei-action="crm" data-ei-id="' + e.id + '"><span class="ei-action-icon">➕</span><span class="ei-action-label">Naar CRM</span><span class="ei-action-sub">Klant + deal</span></button>';
|
|||
|
|
|
|||
|
|
var mapsBtn = e.maps_url
|
|||
|
|
? '<a class="ei-action-tile" href="' + esc(e.maps_url) + '" target="_blank" rel="noopener"><span class="ei-action-icon">📍</span><span class="ei-action-label">Maps</span><span class="ei-action-sub">Locatie</span></a>'
|
|||
|
|
: '';
|
|||
|
|
|
|||
|
|
var dealBtn = e.deal_id
|
|||
|
|
? '<a class="ei-action-tile" href="/deals"><span class="ei-action-icon">💼</span><span class="ei-action-label">Deal</span><span class="ei-action-sub">Pipeline</span></a>'
|
|||
|
|
: '';
|
|||
|
|
|
|||
|
|
var favOn = !!e.is_favorite;
|
|||
|
|
|
|||
|
|
m.innerHTML =
|
|||
|
|
'<header class="ei-drawer-head-rich">' +
|
|||
|
|
'<div class="ei-drawer-head-top">' +
|
|||
|
|
'<div class="ei-drawer-hero">' +
|
|||
|
|
'<span class="ei-drawer-badge ei-type-' + esc(e.entity_type) + '">' + esc(typeLabel(e.entity_type)) + '</span>' +
|
|||
|
|
'<span class="ei-drawer-flag">' + esc(e.country_iso2) + '</span>' +
|
|||
|
|
(e.client_id ? '<span class="ei-crm-badge">In CRM</span>' : '') +
|
|||
|
|
(favOn ? '<span class="ei-crm-badge" style="color:#facc15;border-color:rgba(251,191,36,.4)">★ Favoriet</span>' : '') +
|
|||
|
|
(e.halal_score != null ? '<span class="ei-halal-pill ei-halal-hot">' + Math.round(Number(e.halal_score)) + '/100</span>' : '') +
|
|||
|
|
'</div>' +
|
|||
|
|
'<button type="button" class="ei-drawer-close" data-ei-action="close" title="Sluiten">✕</button>' +
|
|||
|
|
'</div>' +
|
|||
|
|
'<h2 class="ei-drawer-title" id="ei-profile-title">' + esc(e.name) + '</h2>' +
|
|||
|
|
'<p class="ei-drawer-meta">' + esc(territoryName(e.country_iso2)) +
|
|||
|
|
' · Confidence <strong>' + (e.confidence || 0) + '%</strong>' +
|
|||
|
|
' · Pipeline <strong>' + esc(e.pipeline_stage || '—') + '</strong>' +
|
|||
|
|
' · Bron <strong>' + esc(e.source || '—') + '</strong></p>' +
|
|||
|
|
'</header>' +
|
|||
|
|
'<div class="ei-drawer-toolbar">' +
|
|||
|
|
crmBtn +
|
|||
|
|
'<button type="button" class="ei-action-tile ei-action-edit" data-ei-action="edit"><span class="ei-action-icon">✎</span><span class="ei-action-label">Bewerken</span><span class="ei-action-sub">Velden</span></button>' +
|
|||
|
|
'<button type="button" class="ei-action-tile' + (favOn ? ' ei-action-fav-on' : '') + '" data-ei-action="favorite" data-ei-id="' + e.id + '" data-ei-fav="' + (favOn ? '1' : '0') + '"><span class="ei-action-icon">' + (favOn ? '★' : '☆') + '</span><span class="ei-action-label">' + (favOn ? 'Favoriet' : 'Favoriet maken') + '</span></button>' +
|
|||
|
|
mapsBtn +
|
|||
|
|
'<button type="button" class="ei-action-tile" data-ei-action="sync-contacts"><span class="ei-action-icon">↻</span><span class="ei-action-label">Contacten</span></button>' +
|
|||
|
|
dealBtn +
|
|||
|
|
'<a class="ei-action-tile" href="/herman?context=export_catalog&entity=' + e.id + '"><span class="ei-action-icon">📋</span><span class="ei-action-label">Herman</span></a>' +
|
|||
|
|
'</div>' +
|
|||
|
|
'<div class="ei-drawer-body ei-drawer-body-rich">' +
|
|||
|
|
(halalTags ? '<div class="ei-halal-tags">' + halalTags + '</div>' : '') +
|
|||
|
|
((e.city || e.address_line || (e.lat && e.lon))
|
|||
|
|
? '<section class="ei-drawer-section"><h4>Locatie</h4>' +
|
|||
|
|
(e.address_line ? '<p>' + esc(e.address_line) + '</p>' : '') +
|
|||
|
|
(e.city ? '<p>' + esc(e.city) + (e.postal_code ? ', ' + esc(e.postal_code) : '') + '</p>' : '') +
|
|||
|
|
((e.lat && e.lon)
|
|||
|
|
? '<p class="ei-drawer-coords">' + esc(formatCoords(e.lat, e.lon)) +
|
|||
|
|
(e.maps_url ? ' <a href="' + esc(e.maps_url) + '" target="_blank" rel="noopener">Maps →</a>' : '') + '</p>'
|
|||
|
|
: '') +
|
|||
|
|
'</section>'
|
|||
|
|
: '') +
|
|||
|
|
'<section class="ei-drawer-section"><h4>Contact & web</h4>' +
|
|||
|
|
reachSectionHtml(e) + contactGapHint(e) +
|
|||
|
|
((!e.reach || !e.reach.has_any) && !e.email && !e.phone && !(e.websites && e.websites.length) && !e.website && !contacts.length ? '<p class="ei-drawer-empty">Geen contact — sync contacten om te verrijken.</p>' : '') +
|
|||
|
|
'</section>' +
|
|||
|
|
(e.client_id
|
|||
|
|
? '<section class="ei-drawer-section"><h4>CRM</h4>' +
|
|||
|
|
'<div class="ei-drawer-kv"><span>Klant</span><a href="/clients">' + esc(e.crm_client_name || ('#' + e.client_id)) + '</a></div>' +
|
|||
|
|
(e.crm_deal_title ? '<div class="ei-drawer-kv"><span>Deal</span><span>' + esc(e.crm_deal_title) + '</span></div>' : '') +
|
|||
|
|
'</section>'
|
|||
|
|
: '') +
|
|||
|
|
'<section class="ei-drawer-section"><h4>Contactpersonen (' + contacts.length + ')</h4>' +
|
|||
|
|
(contactRows || '<p class="ei-drawer-empty">Geen contactpersonen.</p>') +
|
|||
|
|
'</section></div>';
|
|||
|
|
|
|||
|
|
if (app) {
|
|||
|
|
app.selected = e;
|
|||
|
|
app.drawerOpen = true;
|
|||
|
|
app.drawerLoading = false;
|
|||
|
|
app.listFocusDetail = e;
|
|||
|
|
app.listFocusId = e.id;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
fetchEntity: function (id) {
|
|||
|
|
return fetch('/api/export-intel/entities/' + id).then(function (r) {
|
|||
|
|
return r.json().then(function (d) {
|
|||
|
|
if (!r.ok) throw new Error(d.error || d.detail || ('HTTP ' + r.status));
|
|||
|
|
if (d && d.error) throw new Error(d.error);
|
|||
|
|
return d;
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
highlightOnMap: function (entity, app) {
|
|||
|
|
if (!entity || !entity.lat || !entity.lon || !global.ExportIntelMap) return;
|
|||
|
|
try {
|
|||
|
|
var mapId = activeMapId(app);
|
|||
|
|
global.ExportIntelMap.flyTo(entity.lat, entity.lon, 14, mapId);
|
|||
|
|
global.ExportIntelMap.highlightEntity(entity.id, mapId);
|
|||
|
|
} catch (e) { /* ignore */ }
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
open: function (id) {
|
|||
|
|
id = Number(id);
|
|||
|
|
if (!Number.isFinite(id) || id <= 0) return;
|
|||
|
|
if (this._busy) return;
|
|||
|
|
this._busy = true;
|
|||
|
|
|
|||
|
|
this.showShell(true);
|
|||
|
|
this.renderLoading();
|
|||
|
|
|
|||
|
|
var self = this;
|
|||
|
|
var app = getApp();
|
|||
|
|
|
|||
|
|
this.fetchEntity(id)
|
|||
|
|
.then(function (entity) {
|
|||
|
|
self.renderFull(entity, app);
|
|||
|
|
self.highlightOnMap(entity, app);
|
|||
|
|
})
|
|||
|
|
.catch(function (err) {
|
|||
|
|
self.renderError(err.message || 'Profiel laden mislukt');
|
|||
|
|
})
|
|||
|
|
.finally(function () {
|
|||
|
|
self._busy = false;
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
close: function () {
|
|||
|
|
this._busy = false;
|
|||
|
|
this.showShell(false);
|
|||
|
|
this.clearMount();
|
|||
|
|
var app = getApp();
|
|||
|
|
if (app) {
|
|||
|
|
app.drawerOpen = false;
|
|||
|
|
app.drawerLoading = false;
|
|||
|
|
app.selected = null;
|
|||
|
|
app.editMode = false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
setFavorite: function (id, currentlyFavorite) {
|
|||
|
|
var self = this;
|
|||
|
|
var app = getApp();
|
|||
|
|
id = Number(id);
|
|||
|
|
if (!Number.isFinite(id)) return Promise.reject(new Error('Ongeldig id'));
|
|||
|
|
|
|||
|
|
return fetch('/api/export-intel/entities/favorites', {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: { 'Content-Type': 'application/json' },
|
|||
|
|
body: JSON.stringify({ entity_ids: [id], favorite: !currentlyFavorite }),
|
|||
|
|
})
|
|||
|
|
.then(function (r) {
|
|||
|
|
return r.json().then(function (d) {
|
|||
|
|
if (!r.ok) throw new Error((d && (d.error || d.detail)) || 'Favoriet mislukt');
|
|||
|
|
return d;
|
|||
|
|
});
|
|||
|
|
})
|
|||
|
|
.then(function () {
|
|||
|
|
return self.fetchEntity(id);
|
|||
|
|
})
|
|||
|
|
.then(function (entity) {
|
|||
|
|
if (app) {
|
|||
|
|
app.selected = entity;
|
|||
|
|
app.listFocusDetail = entity;
|
|||
|
|
app.listFocusId = entity.id;
|
|||
|
|
app.syncMsg = entity.is_favorite ? '★ Favoriet opgeslagen' : 'Favoriet verwijderd';
|
|||
|
|
if (app.stats && typeof app.stats.favorites === 'number') {
|
|||
|
|
app.stats.favorites += entity.is_favorite ? 1 : -1;
|
|||
|
|
if (app.stats.favorites < 0) app.stats.favorites = 0;
|
|||
|
|
}
|
|||
|
|
self.patchEntityInApp(app, entity);
|
|||
|
|
}
|
|||
|
|
self.renderFull(entity, app);
|
|||
|
|
return entity;
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
doCrm: function (id) {
|
|||
|
|
var self = this;
|
|||
|
|
var app = getApp();
|
|||
|
|
id = Number(id);
|
|||
|
|
if (!app || !app.pushEntityToCrm) return;
|
|||
|
|
app.pushEntityToCrm(id).then(function () {
|
|||
|
|
return self.fetchEntity(id);
|
|||
|
|
}).then(function (entity) {
|
|||
|
|
if (app) {
|
|||
|
|
app.selected = entity;
|
|||
|
|
app.listFocusDetail = entity;
|
|||
|
|
self.patchEntityInApp(app, entity);
|
|||
|
|
}
|
|||
|
|
self.renderFull(entity, app);
|
|||
|
|
}).catch(function (err) {
|
|||
|
|
if (app) app.syncMsg = 'CRM mislukt: ' + (err.message || err);
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
doEdit: function () {
|
|||
|
|
var app = getApp();
|
|||
|
|
if (!app || !app.selected) return;
|
|||
|
|
app.startEdit();
|
|||
|
|
this.renderEdit(app);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
renderEdit: function (app) {
|
|||
|
|
var e = app.selected;
|
|||
|
|
var f = app.editForm || {};
|
|||
|
|
var m = mountEl();
|
|||
|
|
if (!m || !e) return;
|
|||
|
|
m.innerHTML =
|
|||
|
|
'<header class="ei-drawer-head-rich"><div class="ei-drawer-head-top">' +
|
|||
|
|
'<h2 class="ei-drawer-title">Bewerken · ' + esc(e.name) + '</h2>' +
|
|||
|
|
'<button type="button" class="ei-drawer-close" data-ei-action="close">✕</button></div></header>' +
|
|||
|
|
'<div class="ei-drawer-body ei-drawer-body-rich"><div class="ei-drawer-edit">' +
|
|||
|
|
'<div class="ei-edit-grid">' +
|
|||
|
|
'<label>Naam<input type="text" id="ei-edit-name" value="' + esc(f.name || '') + '"></label>' +
|
|||
|
|
'<label>Stad<input type="text" id="ei-edit-city" value="' + esc(f.city || '') + '"></label>' +
|
|||
|
|
'<label>E-mail<input type="email" id="ei-edit-email" value="' + esc(f.email || '') + '"></label>' +
|
|||
|
|
'<label>Telefoon<input type="text" id="ei-edit-phone" value="' + esc(f.phone || '') + '"></label>' +
|
|||
|
|
'<label>Website<input type="url" id="ei-edit-website" value="' + esc(f.website || '') + '"></label>' +
|
|||
|
|
'</div><div class="ei-edit-actions">' +
|
|||
|
|
'<button type="button" class="btn btn-sm btn-pulse-green" data-ei-action="save-edit">Opslaan</button>' +
|
|||
|
|
'<button type="button" class="btn btn-sm" data-ei-action="cancel-edit">Annuleren</button>' +
|
|||
|
|
'</div></div></div>';
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
saveEdit: function () {
|
|||
|
|
var app = getApp();
|
|||
|
|
if (!app || !app.selected) return;
|
|||
|
|
var self = this;
|
|||
|
|
app.editForm.name = (document.getElementById('ei-edit-name') || {}).value || '';
|
|||
|
|
app.editForm.city = (document.getElementById('ei-edit-city') || {}).value || '';
|
|||
|
|
app.editForm.email = (document.getElementById('ei-edit-email') || {}).value || '';
|
|||
|
|
app.editForm.phone = (document.getElementById('ei-edit-phone') || {}).value || '';
|
|||
|
|
app.editForm.website = (document.getElementById('ei-edit-website') || {}).value || '';
|
|||
|
|
var id = app.selected.id;
|
|||
|
|
Promise.resolve(app.saveEdit()).then(function () {
|
|||
|
|
return self.fetchEntity(id);
|
|||
|
|
}).then(function (entity) {
|
|||
|
|
if (app) {
|
|||
|
|
app.selected = entity;
|
|||
|
|
app.listFocusDetail = entity;
|
|||
|
|
self.patchEntityInApp(app, entity);
|
|||
|
|
}
|
|||
|
|
self.renderFull(entity, app);
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
cancelEdit: function () {
|
|||
|
|
var app = getApp();
|
|||
|
|
if (!app) return;
|
|||
|
|
app.cancelEdit();
|
|||
|
|
if (app.selected) this.renderFull(app.selected, app);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
doFavorite: function (id, isFav) {
|
|||
|
|
var self = this;
|
|||
|
|
this.setFavorite(id, isFav).catch(function (err) {
|
|||
|
|
self.renderError(err.message || 'Favoriet mislukt');
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
doSyncContacts: function () {
|
|||
|
|
var app = getApp();
|
|||
|
|
if (app && app.syncKind) app.syncKind('contacts');
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onModalClick: function (ev) {
|
|||
|
|
var t = ev.target;
|
|||
|
|
if (!t || !t.closest) return;
|
|||
|
|
if (!t.closest('#ei-profile-modal')) return;
|
|||
|
|
|
|||
|
|
if (t.closest('.ei-profile-backdrop')) {
|
|||
|
|
ev.preventDefault();
|
|||
|
|
this.close();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var actionEl = t.closest('[data-ei-action]');
|
|||
|
|
if (!actionEl) return;
|
|||
|
|
|
|||
|
|
var action = actionEl.getAttribute('data-ei-action');
|
|||
|
|
if (!action) return;
|
|||
|
|
|
|||
|
|
ev.preventDefault();
|
|||
|
|
ev.stopPropagation();
|
|||
|
|
|
|||
|
|
var id = Number(actionEl.getAttribute('data-ei-id') || (getApp() && getApp().selected && getApp().selected.id));
|
|||
|
|
|
|||
|
|
if (action === 'close') this.close();
|
|||
|
|
else if (action === 'crm') this.doCrm(id);
|
|||
|
|
else if (action === 'edit') this.doEdit();
|
|||
|
|
else if (action === 'favorite') {
|
|||
|
|
var isFav = actionEl.getAttribute('data-ei-fav') === '1';
|
|||
|
|
this.doFavorite(id, isFav);
|
|||
|
|
}
|
|||
|
|
else if (action === 'sync-contacts') this.doSyncContacts();
|
|||
|
|
else if (action === 'save-edit') this.saveEdit();
|
|||
|
|
else if (action === 'cancel-edit') this.cancelEdit();
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onRowClick: function (ev) {
|
|||
|
|
var t = ev.target;
|
|||
|
|
if (!t || !t.closest) return;
|
|||
|
|
if (t.closest('#ei-profile-modal')) return;
|
|||
|
|
if (t.closest('.ei-td-check, input[type=checkbox], .ei-fav-btn, .ei-td-actions')) return;
|
|||
|
|
if (t.closest('a[href]') && !t.closest('.ei-row-open-btn, .ei-link-btn')) return;
|
|||
|
|
var row = t.closest('[data-entity-id]');
|
|||
|
|
if (!row) return;
|
|||
|
|
var id = row.getAttribute('data-entity-id');
|
|||
|
|
if (!id) return;
|
|||
|
|
ev.preventDefault();
|
|||
|
|
ev.stopPropagation();
|
|||
|
|
this.open(id);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
bindApp: function (app) {
|
|||
|
|
if (!app) return;
|
|||
|
|
global.__exportIntelApp = app;
|
|||
|
|
var self = this;
|
|||
|
|
app.openEntity = function (id) { self.open(id); };
|
|||
|
|
app.closeDrawer = function () { self.close(); };
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
wireModal: function () {
|
|||
|
|
var modal = document.getElementById('ei-profile-modal');
|
|||
|
|
if (!modal || modal._eiWired) return;
|
|||
|
|
modal._eiWired = true;
|
|||
|
|
var self = this;
|
|||
|
|
modal.addEventListener('click', function (ev) { self.onModalClick(ev); }, true);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
boot: function () {
|
|||
|
|
var self = this;
|
|||
|
|
this.wireModal();
|
|||
|
|
document.addEventListener('click', function (ev) { self.onRowClick(ev); }, true);
|
|||
|
|
document.addEventListener('keydown', function (ev) {
|
|||
|
|
if (ev.key === 'Escape') {
|
|||
|
|
var modal = document.getElementById('ei-profile-modal');
|
|||
|
|
if (modal && modal.classList.contains('is-open')) self.close();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
var params = new URLSearchParams(global.location.search);
|
|||
|
|
var openId = params.get('open');
|
|||
|
|
|
|||
|
|
function tryBind() {
|
|||
|
|
var page = document.querySelector('.ei-page');
|
|||
|
|
if (page && page._x_dataStack && page._x_dataStack[0]) {
|
|||
|
|
self.bindApp(page._x_dataStack[0]);
|
|||
|
|
if (openId) self.open(openId);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
if (global.__exportIntelApp) {
|
|||
|
|
self.bindApp(global.__exportIntelApp);
|
|||
|
|
if (openId) self.open(openId);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!tryBind()) {
|
|||
|
|
var n = 0;
|
|||
|
|
var timer = setInterval(function () {
|
|||
|
|
n++;
|
|||
|
|
if (tryBind() || n > 120) clearInterval(timer);
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
document.addEventListener('alpine:initialized', function () {
|
|||
|
|
tryBind();
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
global.ExportIntelProfile = Profile;
|
|||
|
|
|
|||
|
|
if (document.readyState === 'loading') {
|
|||
|
|
document.addEventListener('DOMContentLoaded', function () { Profile.boot(); });
|
|||
|
|
} else {
|
|||
|
|
Profile.boot();
|
|||
|
|
}
|
|||
|
|
})(window);
|