Files
foodlinkk-command-center/cockpit/templates/export_intel.html
T
Aissa 7289b770e5 Fix Wereldexport world map loading and tiles.
Self-host Leaflet locally, proxy map tiles through cockpit, set explicit map height, and invalidate size after init.
2026-07-19 18:36:02 +00:00

802 lines
42 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Wereldexport · Foodlinkk B2B{% endblock %}
{% block extra_head %}
<link rel="stylesheet" href="/static/vendor/leaflet/leaflet.css?v=1" />
<link rel="stylesheet" href="/static/vendor/leaflet.markercluster/MarkerCluster.css?v=1" />
<link rel="stylesheet" href="/static/vendor/leaflet.markercluster/MarkerCluster.Default.css?v=1" />
<link rel="stylesheet" href="/static/css/export-intel.css?v=13" />
<link rel="stylesheet" href="/static/css/export-intel-tabs.css?v=10" />
<script src="/static/vendor/leaflet/leaflet.js?v=1"></script>
<script src="/static/vendor/leaflet.markercluster/leaflet.markercluster.js?v=1"></script>
<script src="/static/js/export-intel-map.js?v=9"></script>
{% endblock %}
{% block content %}
<div class="ei-page" x-data="exportIntelApp()" x-init="init()">
<header class="ei-header page-header">
<div>
<h1>Wereldexport <span class="live-badge">Live</span></h1>
<p class="ei-subtitle">Foodlinkk B2B · wereldwijde markten · halal vlees · cateraars · distributeurs</p>
</div>
<div class="retail-actions">
<button class="btn btn-sm btn-pulse-green" @click="syncKind('world')" :disabled="busy" title="Europa · MENA · Afrika · Amerika">🌍 Sync wereld</button>
<button class="btn btn-sm btn-pulse" @click="syncKind('region')" :disabled="busy || !region" title="Sync huidige regio-filter">Sync regio</button>
<button class="btn btn-sm btn-pulse-green" @click="syncKind('all')" :disabled="busy">⚡ Sync alles</button>
<button class="btn btn-sm btn-pulse" @click="syncKind('distributors')" :disabled="busy">Distributeurs</button>
<button class="btn btn-sm btn-pulse" @click="syncKind('caterers')" :disabled="busy">Cateraars</button>
<button class="btn btn-sm btn-pulse-green" @click="syncKind('contacts')" :disabled="busy">Contacten</button>
<button class="btn btn-sm btn-pulse-purple" @click="syncKind('tenders')" :disabled="busy">TED</button>
<a class="btn btn-sm" :href="contactsExportUrl()" target="_blank">CSV export</a>
</div>
</header>
<p class="ei-sync-msg" x-show="syncMsg" x-text="syncMsg"></p>
<div class="ei-selection-bar" x-show="showSelectionBar()" x-cloak>
<div class="ei-selection-left">
<span class="ei-selection-count" x-show="selectedIds.length" x-text="selectedIds.length + ' geselecteerd'"></span>
<span class="ei-selection-hint" x-show="!selectedIds.length" x-text="tab === 'contacts' ? 'Selecteer contacten met ☑ — organisatie gaat naar CRM' : 'Selecteer rijen met ☑ voor bulk-acties'"></span>
</div>
<div class="ei-selection-actions">
<button type="button" class="btn btn-sm" @click="toggleSelectAllVisible()" title="Alles op lijst">☑ Alles</button>
<button type="button" class="btn btn-sm" x-show="selectedIds.length" @click="clearSelection()">Wis selectie</button>
<button type="button" class="btn btn-sm btn-pulse" :disabled="!selectedIds.length || busy" @click="favoriteSelection()">★ Favoriet</button>
<button type="button" class="btn btn-sm" :disabled="!selectedIds.length || busy" @click="unfavoriteSelection()">☆ Verwijder favoriet</button>
<button type="button" class="btn btn-sm btn-pulse-green" :disabled="!selectedIds.length || crmPushBusy" @click="openCrmModal()"> Naar CRM</button>
</div>
</div>
<div class="ei-neo-kpi-row">
<div class="ei-neo-kpi">
<div class="ei-neo-kpi-top">
<div class="ei-neo-ring" style="--ring-color:#0ea5e9;--ring-pct:85"><div class="ei-neo-ring-inner">🌍</div></div>
<div><div class="ei-neo-kpi-label">Landen</div></div>
</div>
<div class="ei-neo-kpi-value" x-text="stats.territories||0"></div>
</div>
<div class="ei-neo-kpi">
<div class="ei-neo-kpi-top">
<div class="ei-neo-ring" style="--ring-color:#38bdf8;--ring-pct:70"><div class="ei-neo-ring-inner">📊</div></div>
<div><div class="ei-neo-kpi-label">Entities</div></div>
</div>
<div class="ei-neo-kpi-value" x-text="stats.entities||0"></div>
</div>
<div class="ei-neo-kpi">
<div class="ei-neo-kpi-top">
<div class="ei-neo-ring" style="--ring-color:#fbbf24;--ring-pct:75"><div class="ei-neo-ring-inner">📦</div></div>
<div><div class="ei-neo-kpi-label">Distributeurs</div></div>
</div>
<div class="ei-neo-kpi-value" x-text="stats.distributors||0"></div>
</div>
<div class="ei-neo-kpi">
<div class="ei-neo-kpi-top">
<div class="ei-neo-ring" style="--ring-color:#a78bfa;--ring-pct:65"><div class="ei-neo-ring-inner">🏢</div></div>
<div><div class="ei-neo-kpi-label">Cateraars</div></div>
</div>
<div class="ei-neo-kpi-value" x-text="stats.caterers||0"></div>
</div>
<div class="ei-neo-kpi">
<div class="ei-neo-kpi-top">
<div class="ei-neo-ring" style="--ring-color:#34d399;--ring-pct:60"><div class="ei-neo-ring-inner">📇</div></div>
<div><div class="ei-neo-kpi-label">Contacten</div></div>
</div>
<div class="ei-neo-kpi-value" x-text="stats.contacts||0"></div>
</div>
<div class="ei-neo-kpi">
<div class="ei-neo-kpi-top">
<div class="ei-neo-ring" style="--ring-color:#c084fc;--ring-pct:50"><div class="ei-neo-ring-inner">📋</div></div>
<div><div class="ei-neo-kpi-label">Tenders open</div></div>
</div>
<div class="ei-neo-kpi-value" x-text="stats.tenders_open||0"></div>
</div>
</div>
<div class="ei-filter-bar">
<div class="ei-filter-group ei-filter-group-geo">
<label class="ei-filter-label">Regio
<select x-model="region" @change="country=''; refresh()">
<option value="">Alle regio's</option>
<template x-for="r in regions" :key="r.code">
<option :value="r.code" x-text="r.name_nl"></option>
</template>
</select>
</label>
<label class="ei-filter-label">Land
<select x-model="country" @change="onCountryChange()">
<option value="">Alle landen</option>
<template x-for="t in territories.filter(t => !region || t.region_code===region)" :key="t.code">
<option :value="t.country_iso2" x-text="t.name_nl"></option>
</template>
</select>
</label>
</div>
<div class="ei-filter-group ei-filter-group-search">
<label class="ei-filter-label ei-filter-search-wrap">
<span class="ei-filter-sr">Zoeken</span>
<input type="search" placeholder="Organisatie, e-mail, stad…" x-model="q" @input="onSearchInput()" />
</label>
</div>
<div class="ei-filter-group ei-filter-group-maptypes" x-show="showMapTypeFilter()" x-cloak>
<span class="ei-filter-label">Kaart toont</span>
<div class="ei-type-chips" role="group" aria-label="Types op kaart">
<button type="button" class="ei-type-chip" :class="{ active: !mapTypesSelected.length }" @click="clearMapTypes()">Alle</button>
<template x-for="chip in mapTypeChips" :key="chip.v">
<button type="button" class="ei-type-chip" :class="{ active: isMapTypeActive(chip.v) }" @click="toggleMapType(chip.v)">
<span x-text="chip.icon"></span> <span x-text="chip.l"></span>
</button>
</template>
</div>
</div>
<div class="ei-filter-group ei-filter-group-halal" x-show="showMapTypeFilter()" x-cloak>
<label class="ei-filter-check">
<input type="checkbox" x-model="mapHalalMode" @change="applyMapFilters()" />
<span>🥩 Halal-kansen modus</span>
</label>
<label class="ei-filter-label ei-halal-min" x-show="mapHalalMode">
Min. score
<input type="range" min="30" max="90" step="5" x-model.number="mapHalalMin" @change="applyMapFilters()" />
<strong x-text="mapHalalMin"></strong>
</label>
</div>
<div class="ei-filter-group ei-filter-group-collection" x-show="showCrmFilter()" x-cloak>
<label class="ei-filter-check" x-show="tab === 'map'">
<input type="checkbox" x-model="filterFavoritesOnly" @change="onFilterCollectionChange()" />
<span>★ Alleen favorieten</span>
</label>
<label class="ei-filter-label">CRM
<select x-model="filterCrmStatus" @change="onFilterCollectionChange()">
<option value="">Alle</option>
<option value="not_linked">Nog niet in CRM</option>
<option value="linked">In CRM</option>
</select>
</label>
</div>
<div class="ei-filter-group ei-filter-group-context" x-show="showEntityTypeFilter() || showEmailFilter()" x-cloak>
<label class="ei-filter-label" x-show="showEntityTypeFilter()">Type
<select x-model="entityTypeFilter" @change="loadTabData()">
<template x-for="o in entityTypeOptions()" :key="o.v">
<option :value="o.v" x-text="o.l"></option>
</template>
</select>
</label>
<label class="ei-filter-label" x-show="showEmailFilter()">E-mail
<select x-model="filterHasEmail" @change="loadTabData()">
<option value="">Alle</option>
<option value="yes">Met e-mail</option>
<option value="no">Zonder e-mail</option>
</select>
</label>
</div>
<div class="ei-filter-meta">
<span class="ei-filter-count" x-show="resultCount > 0" x-text="resultCount + ' resultaten'"></span>
<span class="ei-filter-count ei-filter-fav" x-show="stats.favorites > 0" x-text="(stats.favorites||0) + ' ★'"></span>
<button type="button" class="btn btn-sm ei-filter-clear" x-show="q || entityTypeFilter || mapTypesSelected.length || mapHalalMode || filterHasEmail || filterFavoritesOnly || filterCrmStatus" @click="clearFilters()">Wis filters</button>
</div>
</div>
<div class="vtabs-layout wereldexport-workspace" style="--vt-color:#0ea5e9" x-ref="vtabsWrap">
<nav class="vtabs-nav" x-ref="vtabsNav" :style="'--vt-indicator-top:'+tabIndicatorTop+'px;--vt-indicator-h:'+tabIndicatorH+'px'">
<div class="vtabs-nav-header"><strong>Modules</strong></div>
<template x-for="t in tabs" :key="t.id">
<button type="button" class="vtab-btn vtab-btn-rich" :class="{active: tab===t.id}" :data-tab="t.id"
:style="'--tab-accent:'+t.color"
@click="setTab(t.id, $el)">
<span class="ei-vtab-icon-wrap" x-text="t.icon"></span>
<span class="ei-vtab-text">
<span class="ei-vtab-label" x-text="t.label"></span>
<span class="ei-vtab-sub" x-show="tabSub(t.id)" x-text="tabSub(t.id)"></span>
</span>
<span class="ei-vtab-meta">
<span class="ei-vtab-key" x-text="t.key"></span>
<span class="ei-vtab-badge" x-show="tabCount(t.id) > 0" x-text="tabCount(t.id)"></span>
</span>
</button>
</template>
</nav>
<div class="vtabs-content">
<!-- Kaart -->
<section x-show="tab==='map'" x-cloak>
<div class="ei-tab-panel">
<div class="ei-tab-panel-head">
<div>
<h2>Wereldkaart</h2>
<p>Klik pin voor preview · dubbelklik of “Volledig profiel” voor alle gegevens</p>
</div>
</div>
<div class="ei-tab-panel-body">
<div class="ei-map-legend">
<template x-if="!mapHalalMode">
<div class="ei-legend-row">
<span class="ei-legend-item"><i class="ei-leg-pin" style="--c:#f59e0b">📦</i> Distributeur</span>
<span class="ei-legend-item"><i class="ei-leg-pin" style="--c:#eab308">🏪</i> Groothandel</span>
<span class="ei-legend-item"><i class="ei-leg-pin" style="--c:#a78bfa">🏢</i> Cateraar</span>
<span class="ei-legend-item"><i class="ei-leg-pin" style="--c:#34d399">🍽️</i> Horeca</span>
<span class="ei-legend-item"><i class="ei-leg-pin" style="--c:#0ea5e9">📍</i> Overig</span>
</div>
</template>
<template x-if="mapHalalMode">
<div class="ei-legend-row ei-legend-halal">
<span class="ei-legend-item"><i class="ei-leg-pin" style="--c:#22c55e"></i> Hoge kans (75+)</span>
<span class="ei-legend-item"><i class="ei-leg-pin" style="--c:#eab308"></i> Gemiddeld (55+)</span>
<span class="ei-legend-item"><i class="ei-leg-pin" style="--c:#f97316"></i> Matig (40+)</span>
<span class="ei-legend-item"><i class="ei-leg-pin" style="--c:#64748b"></i> Laag</span>
</div>
</template>
</div>
<div class="ei-map-empty" x-show="tab==='map' && !entities.length && !busy" x-cloak>
<div class="ei-map-empty-inner">
<strong x-show="region || country">Nog geen data op de kaart voor deze selectie</strong>
<strong x-show="!region && !country">Zoom in of filter op regio</strong>
<p>Landen staan in het systeem, maar entities + contacten komen pas na een <em>OSM sync</em> (OpenStreetMap).</p>
<p x-show="region==='africa'">Afrika: 51 landen toegevoegd — sync nog niet gedraaid voor dit continent.</p>
<div class="ei-map-empty-actions">
<button type="button" class="btn btn-sm btn-pulse-green" x-show="region" @click="syncKind('region')" :disabled="busy">↻ Sync regio</button>
<button type="button" class="btn btn-sm btn-pulse" @click="syncKind('world')" :disabled="busy">🌍 Sync wereld</button>
</div>
</div>
</div>
<div class="ei-split">
<div class="ei-map-error" x-show="mapError" x-cloak x-text="mapError"></div>
<div class="ei-map-wrap"><div id="ei-map"></div></div>
<div class="ei-panel">
<h3 style="margin:0 0 0.5rem;font-size:0.9rem" x-text="mapHalalMode ? 'Top halal-kansen' : 'Entities op kaart'"></h3>
<p style="font-size:0.75rem;color:#64748b;margin:0 0 0.75rem" x-show="!mapHalalMode">Klik rij of kaart-pin voor volledig profiel.</p>
<p style="font-size:0.75rem;color:#64748b;margin:0 0 0.75rem" x-show="mapHalalMode">Klik een rij voor contact & context · kaart zoomt mee. Groen = beste halal-vlees kans.</p>
<div class="ei-list-focus" x-show="listFocusLoading || listFocusDetail" x-cloak>
<div class="ei-list-focus-head">
<strong x-show="listFocusLoading">Profiel laden…</strong>
<template x-if="listFocusDetail">
<div>
<span class="ei-drawer-badge" :class="'ei-type-'+listFocusDetail.entity_type" x-text="typeLabel(listFocusDetail.entity_type)"></span>
<span class="ei-halal-pill ei-halal-hot" x-show="listFocusDetail.halal_score" x-text="Math.round(listFocusDetail.halal_score)+'/100'"></span>
</div>
</template>
<button type="button" class="btn btn-sm" @click="clearListFocus()"></button>
</div>
<template x-if="listFocusDetail">
<div class="ei-list-focus-body">
<h4 class="ei-list-focus-title" x-text="listFocusDetail.name"></h4>
<p class="ei-list-focus-meta">
<span x-text="territoryName(listFocusDetail.country_iso2)"></span>
<span x-show="listFocusDetail.city"> · <span x-text="listFocusDetail.city"></span></span>
<span> · Confidence <span x-text="listFocusDetail.confidence||0"></span>%</span>
</p>
<div class="ei-list-focus-kv" x-show="listFocusDetail.address_line">
<span>Adres</span><span x-text="listFocusDetail.address_line"></span>
</div>
<div class="ei-list-focus-kv" x-show="listFocusDetail.email">
<span>E-mail</span><a :href="'mailto:'+listFocusDetail.email" x-text="listFocusDetail.email"></a>
</div>
<div class="ei-list-focus-kv" x-show="listFocusDetail.phone">
<span>Telefoon</span><a :href="'tel:'+listFocusDetail.phone" x-text="listFocusDetail.phone"></a>
</div>
<div class="ei-list-focus-kv" x-show="listFocusDetail.website">
<span>Website</span><a :href="listFocusDetail.website" target="_blank" rel="noopener" x-text="listFocusDetail.website"></a>
</div>
<p class="ei-drawer-empty" x-show="!listFocusDetail.email && !listFocusDetail.phone && !listFocusDetail.website && !(listFocusDetail.contacts||[]).length">
Geen contactgegevens — OSM heeft vaak alleen naam+locatie. Probeer ↻ Sync contacten.
</p>
<div class="ei-halal-tags" x-show="(listFocusDetail.halal_reason_labels||[]).length">
<template x-for="tag in (listFocusDetail.halal_reason_labels||[])" :key="tag">
<span class="ei-halal-tag" x-text="tag"></span>
</template>
</div>
<template x-for="c in (listFocusDetail.contacts||[]).slice(0,3)" :key="c.id">
<div class="ei-contact-row ei-contact-row-compact">
<strong x-text="c.name || c.role || 'Contact'"></strong>
<div x-show="c.email">📧 <a :href="'mailto:'+c.email" x-text="c.email"></a></div>
<div x-show="c.phone">📞 <a :href="'tel:'+c.phone" x-text="c.phone"></a></div>
</div>
</template>
<div class="ei-list-focus-actions">
<button type="button" class="btn btn-sm btn-pulse" @click="openEntity(listFocusDetail.id)">Volledig profiel →</button>
<a class="btn btn-sm" x-show="listFocusDetail.maps_url" :href="listFocusDetail.maps_url" target="_blank" rel="noopener">Google Maps</a>
</div>
</div>
</template>
</div>
<div class="ei-halal-markets" x-show="mapHalalMode && halalTopMarkets.length">
<h4>Landen met meeste kans</h4>
<template x-for="m in halalTopMarkets.slice(0,8)" :key="m.country_iso2">
<div class="ei-halal-market-row" @click="country=m.country_iso2; refresh()" style="cursor:pointer">
<span class="ei-halal-flag" x-text="m.country_iso2"></span>
<span class="ei-halal-market-name" x-text="m.country_iso2"></span>
<strong class="ei-halal-score" x-text="Math.round(m.combined_score||0)"></strong>
</div>
</template>
</div>
<div class="ei-table-wrap">
<table class="ei-table">
<thead>
<tr>
<th class="ei-th-check"><input type="checkbox" @change="toggleSelectAllVisible()" title="Alles selecteren" /></th>
<th class="ei-th-fav"></th>
<th>Naam</th>
<th>Type</th>
<th>Stad</th>
<th>Contact</th>
<th x-show="mapHalalMode">Score</th>
<th>Land</th>
</tr>
</thead>
<tbody>
<template x-for="e in (mapHalalMode && halalTopEntities.length ? halalTopEntities : entities)" :key="e.id">
<tr
@click="selectListEntity(e.id)"
:class="{ 'ei-row-active': listFocusId === e.id, 'ei-row-selected': isSelected(e.id), 'ei-row-crm': e.client_id }"
style="cursor:pointer"
>
<td class="ei-td-check" @click.stop>
<input type="checkbox" :checked="isSelected(e.id)" @change="toggleSelect(e.id, $event)" />
</td>
<td class="ei-td-fav" @click.stop>
<button type="button" class="ei-fav-btn" :class="{ active: e.is_favorite }" @click="toggleFavorite(e.id, e.is_favorite, $event)" x-text="e.is_favorite ? '★' : '☆'"></button>
</td>
<td>
<span class="ei-row-name" x-text="e.name"></span>
<span class="ei-crm-badge" x-show="e.client_id" title="In CRM">CRM</span>
</td>
<td><span class="ei-type-pill ei-type-pill-sm" :class="'ei-type-'+e.entity_type" x-text="typeLabel(e.entity_type)"></span></td>
<td x-text="e.city || '—'"></td>
<td class="ei-contact-cell" x-text="contactPreview(e)"></td>
<td x-show="mapHalalMode"><span class="ei-halal-pill" :class="'ei-halal-'+(e.halal_score>=75?'hot':e.halal_score>=55?'warm':'mild')" x-text="Math.round(e.halal_score||0)"></span></td>
<td x-text="e.country_iso2"></td>
</tr>
</template>
</tbody>
</table>
</div>
<div class="ei-empty" x-show="!entities.length">
<p>Geen entities met geo in selectie.</p>
<button class="btn btn-sm btn-pulse ei-empty-cta" @click="syncKind('distributors')">Start sync — fase B vult data</button>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Distributeurs -->
<section x-show="tab==='distributors'" x-cloak x-effect="tab==='distributors' && loadTabData()">
<div class="ei-tab-panel">
<div class="ei-tab-panel-head">
<div><h2>Distributeurs & groothandels</h2><p>OSM · Google Places · handmatige seeds</p></div>
<button class="btn btn-sm btn-pulse" @click="syncKind('distributors')" :disabled="busy">↻ Sync</button>
</div>
<div class="ei-tab-panel-body">
<div class="ei-panel" style="border:none;background:transparent;padding:0">
<div class="ei-table-wrap">
<table class="ei-table">
<thead><tr><th>Naam</th><th>Land</th><th>Stad</th><th>E-mail</th><th>Telefoon</th><th>Confidence</th></tr></thead>
<tbody>
<template x-for="e in entities" :key="e.id">
<tr @click="openEntity(e.id)" style="cursor:pointer">
<td x-text="e.name"></td>
<td x-text="e.country_iso2"></td>
<td x-text="e.city||'—'"></td>
<td x-text="e.primary_email||e.email||'—'"></td>
<td x-text="e.primary_phone||e.phone||'—'"></td>
<td class="ei-confidence" x-text="e.confidence+'%'"></td>
</tr>
</template>
</tbody>
</table>
</div>
<div class="ei-empty" x-show="!entities.length">
<p>Nog geen distributeurs in deze selectie.</p>
<button class="btn btn-sm btn-pulse" @click="syncKind('distributors')">Sync distributeurs (fase B)</button>
</div>
</div>
</div>
</div>
</section>
<!-- Eindklanten -->
<section x-show="tab==='customers'" x-cloak>
<div class="ei-tab-panel">
<div class="ei-tab-panel-head">
<div><h2>Eindklanten</h2><p>Restaurants · döner · slagers · horeca</p></div>
<button class="btn btn-sm btn-pulse" @click="syncKind('customers')" :disabled="busy || !country">↻ Sync horeca</button>
</div>
<div class="ei-tab-panel-body">
<div class="ei-panel" style="border:none;background:transparent;padding:0">
<div class="ei-table-wrap">
<table class="ei-table">
<thead><tr><th>Naam</th><th>Type</th><th>Land</th><th>Stad</th></tr></thead>
<tbody>
<template x-for="e in entities" :key="e.id">
<tr @click="openEntity(e.id)" style="cursor:pointer">
<td x-text="e.name"></td>
<td x-text="typeLabel(e.entity_type)"></td>
<td x-text="e.country_iso2"></td>
<td x-text="e.city||'—'"></td>
</tr>
</template>
</tbody>
</table>
</div>
<div class="ei-empty" x-show="!entities.length">
<p>Selecteer een land en sync horeca via OSM.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Cateraars -->
<section x-show="tab==='caterers'" x-cloak>
<div class="ei-tab-panel">
<div class="ei-tab-panel-head">
<div><h2>Facilitaire cateraars</h2><p>Albron · Sodexo · Compass · presence per land</p></div>
<button class="btn btn-sm btn-pulse" @click="syncKind('caterers')" :disabled="busy">↻ Sync</button>
</div>
<div class="ei-tab-panel-body">
<div class="ei-panel" style="margin-bottom:1rem">
<h3 style="margin:0 0 0.5rem">Brand presence</h3>
<div class="ei-table-wrap">
<table class="ei-table">
<thead><tr><th>Brand</th><th>Land</th><th>Lokale naam</th><th>Takken</th></tr></thead>
<tbody>
<template x-for="p in catererPresence" :key="p.id">
<tr>
<td x-text="p.brand_name"></td>
<td x-text="p.country_iso2"></td>
<td x-text="p.local_legal_name||'—'"></td>
<td x-text="(p.service_lines||[]).join(', ')"></td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
<div class="ei-panel">
<h3 style="margin:0 0 0.5rem">Cateraar entities</h3>
<div class="ei-table-wrap">
<table class="ei-table">
<thead><tr><th>Naam</th><th>Land</th><th>E-mail</th><th>Website</th></tr></thead>
<tbody>
<template x-for="e in entities" :key="e.id">
<tr @click="openEntity(e.id)" style="cursor:pointer">
<td x-text="e.name"></td>
<td x-text="e.country_iso2"></td>
<td x-text="e.primary_email||'—'"></td>
<td><a :href="e.website" target="_blank" rel="noopener" x-text="e.website||'—'"></a></td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</div>
</div>
</section>
<!-- Contacten -->
<section x-show="tab==='contacts'" x-cloak>
<div class="ei-tab-panel">
<div class="ei-tab-panel-head">
<div><h2>Contactregistry</h2><p>E-mail · telefoon · outreach-ready · direct naar CRM</p></div>
<div class="ei-tab-panel-actions">
<button type="button" class="btn btn-sm btn-pulse-green" :disabled="!selectedIds.length || crmPushBusy" @click="openCrmModal()"> Geselecteerde naar CRM</button>
<a class="btn btn-sm" :href="contactsExportUrl()" target="_blank">CSV export</a>
</div>
</div>
<div class="ei-tab-panel-body">
<div class="ei-panel" style="border:none;background:transparent;padding:0">
<div class="ei-table-wrap">
<table class="ei-table">
<thead>
<tr>
<th class="ei-th-check"><input type="checkbox" @change="toggleSelectAllVisible()" title="Alles selecteren" /></th>
<th>Organisatie</th>
<th>Stad</th>
<th>Type</th>
<th>Land</th>
<th>Rol</th>
<th>Naam</th>
<th>E-mail</th>
<th>Telefoon</th>
<th>CRM</th>
<th></th>
</tr>
</thead>
<tbody>
<template x-for="c in contacts" :key="c.id">
<tr :class="{ 'ei-row-selected': isSelected(c.entity_id), 'ei-row-crm': c.client_id }">
<td class="ei-td-check" @click.stop>
<input type="checkbox" :checked="isSelected(c.entity_id)" @change="toggleSelect(c.entity_id, $event)" />
</td>
<td>
<button type="button" class="ei-link-btn" @click="openEntity(c.entity_id)" x-text="c.entity_name"></button>
</td>
<td x-text="c.entity_city || '—'"></td>
<td><span class="ei-type-pill ei-type-pill-sm" :class="'ei-type-'+c.entity_type" x-text="typeLabel(c.entity_type)"></span></td>
<td x-text="c.country_iso2"></td>
<td x-text="c.role || '—'"></td>
<td x-text="c.name||'—'"></td>
<td x-text="c.email||'—'"></td>
<td x-text="c.phone||c.mobile||'—'"></td>
<td>
<span class="ei-crm-badge" x-show="c.client_id" :title="c.crm_client_name || 'In CRM'">CRM</span>
<span class="muted" x-show="!c.client_id"></span>
</td>
<td class="ei-td-actions" @click.stop>
<button type="button" class="btn btn-sm btn-pulse-green" x-show="!c.client_id" @click="pushEntityToCrm(c.entity_id)" :disabled="crmPushBusy"> CRM</button>
<a class="btn btn-sm" x-show="c.client_id" :href="'/clients'" title="Bekijk in CRM">Open</a>
</td>
</tr>
</template>
</tbody>
</table>
</div>
<div class="ei-load-more" x-show="contactsHasMore">
<button type="button" class="btn btn-sm" @click="loadMoreContacts()" :disabled="contactsLoadingMore || busy">
<span x-text="contactsLoadingMore ? 'Laden…' : ('Meer laden (' + contacts.length + ' / ' + resultCount + ')')"></span>
</button>
</div>
<div class="ei-empty" x-show="!contacts.length && !busy">
<p>Geen contacten voor deze filters — pas land, type of zoekterm aan.</p>
<button class="btn btn-sm btn-pulse-green" @click="syncKind('contacts')">Sync contacten</button>
</div>
</div>
</div>
</div>
</section>
<!-- Tenders -->
<section x-show="tab==='tenders'" x-cloak>
<div class="ei-tab-panel">
<div class="ei-tab-panel-head">
<div><h2>Tenders</h2><p>TED API · food · catering · meat</p></div>
<button class="btn btn-sm btn-pulse-purple" @click="syncKind('tenders')" :disabled="busy">↻ TED sync</button>
</div>
<div class="ei-tab-panel-body">
<div class="ei-panel" style="border:none;background:transparent;padding:0">
<div class="ei-table-wrap">
<table class="ei-table">
<thead><tr><th>Titel</th><th>Land</th><th>Status</th><th>Link</th></tr></thead>
<tbody>
<template x-for="t in tenders" :key="t.id">
<tr>
<td x-text="t.title"></td>
<td x-text="t.country_iso2||'—'"></td>
<td x-text="t.status"></td>
<td><a :href="t.url" target="_blank" rel="noopener">Open</a></td>
</tr>
</template>
</tbody>
</table>
</div>
<div class="ei-empty" x-show="!tenders.length">
<p>Geen tenders — klik TED sync.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Overheid -->
<section x-show="tab==='gov'" x-cloak>
<div class="ei-tab-panel">
<div class="ei-tab-panel-head">
<div><h2>Overheid & data</h2><p>Statistiek · tender portals per land</p></div>
</div>
<div class="ei-tab-panel-body">
<div class="ei-panel" style="border:none;background:transparent;padding:0">
<div class="ei-table-wrap">
<table class="ei-table">
<thead><tr><th>Land</th><th>Naam</th><th>Categorie</th><th>Link</th></tr></thead>
<tbody>
<template x-for="g in govSources" :key="g.id">
<tr>
<td x-text="g.country_iso2"></td>
<td x-text="g.name"></td>
<td x-text="g.category"></td>
<td><a :href="g.url" target="_blank" rel="noopener">Open</a></td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</div>
</div>
</section>
<!-- Pipeline -->
<section x-show="tab==='pipeline'" x-cloak>
<div class="ei-tab-panel">
<div class="ei-tab-panel-head">
<div><h2>CRM Pipeline</h2><p>Van Wereldexport naar klanten & deals</p></div>
<div class="retail-actions">
<button type="button" class="btn btn-sm btn-pulse-green" :disabled="!selectedIds.length" @click="openCrmModal()"> Geselecteerde naar CRM</button>
<a class="btn btn-sm btn-pulse" href="/clients">Klanten</a>
<a class="btn btn-sm btn-pulse" href="/deals">Deals</a>
</div>
</div>
<div class="ei-tab-panel-body">
<p class="ei-pipeline-hint">Entities die je via <strong>Naar CRM</strong> hebt toegevoegd verschijnen hier met koppeling naar klant + deal.</p>
<div class="ei-table-wrap" x-show="crmPipeline.length">
<table class="ei-table">
<thead>
<tr>
<th>Organisatie</th>
<th>Type</th>
<th>Land</th>
<th>CRM klant</th>
<th>Deal</th>
<th>Toegevoegd</th>
</tr>
</thead>
<tbody>
<template x-for="p in crmPipeline" :key="p.id">
<tr @click="openEntity(p.id)" style="cursor:pointer">
<td x-text="p.name"></td>
<td x-text="typeLabel(p.entity_type)"></td>
<td x-text="p.country_iso2"></td>
<td>
<a x-show="p.client_id" :href="'/clients'" @click.stop x-text="p.crm_client_name || ('#'+p.client_id)"></a>
</td>
<td x-text="p.crm_deal_title || '—'"></td>
<td x-text="p.crm_pushed_at ? new Date(p.crm_pushed_at).toLocaleDateString('nl-NL') : '—'"></td>
</tr>
</template>
</tbody>
</table>
</div>
<div class="ei-empty" x-show="!crmPipeline.length">
<p>Nog geen entities in CRM vanuit Wereldexport.</p>
<p>Selecteer leads op de kaart en klik <strong> Naar CRM</strong>.</p>
</div>
</div>
</div>
</section>
</div>
</div>
<!-- Drawer — volledig profiel -->
<div class="ei-drawer-root" x-show="drawerOpen" x-cloak>
<div class="ei-drawer-backdrop" @click="closeDrawer()"></div>
<aside class="ei-drawer ei-drawer-rich">
<header class="ei-drawer-head">
<button type="button" class="btn btn-sm" @click="closeDrawer()">✕ Sluiten</button>
</header>
<div class="ei-drawer-body" x-show="drawerLoading">
<p class="ei-drawer-empty">Profiel laden…</p>
</div>
<template x-if="selected && !drawerLoading">
<div class="ei-drawer-body">
<div class="ei-drawer-hero">
<span class="ei-drawer-badge" :class="'ei-type-'+selected.entity_type" x-text="typeLabel(selected.entity_type)"></span>
<span class="ei-drawer-flag" x-text="selected.country_iso2"></span>
<span class="ei-halal-pill ei-halal-hot" x-show="selected.halal_score" x-text="Math.round(selected.halal_score)+'/100'"></span>
</div>
<h2 class="ei-drawer-title" x-text="selected.name"></h2>
<p class="ei-drawer-meta">
<span x-text="territoryName(selected.country_iso2)"></span>
· Confidence <strong x-text="(selected.confidence||0)+'%'"></strong>
· Pipeline <strong x-text="selected.pipeline_stage||'—'"></strong>
· Bron <strong x-text="selected.source||'—'"></strong>
</p>
<div class="ei-halal-tags" x-show="(selected.halal_reason_labels||[]).length">
<template x-for="tag in (selected.halal_reason_labels||[])" :key="tag">
<span class="ei-halal-tag" x-text="tag"></span>
</template>
</div>
<section class="ei-drawer-section" x-show="selected.city || selected.address_line || (selected.lat && selected.lon)">
<h4>Locatie</h4>
<p x-show="selected.address_line" x-text="selected.address_line"></p>
<p x-show="selected.city"><span x-text="selected.city"></span><span x-show="selected.postal_code">, </span><span x-text="selected.postal_code"></span></p>
<p x-show="selected.lat && selected.lon" class="ei-drawer-coords">
<span x-text="selected.lat.toFixed(5)+', '+selected.lon.toFixed(5)"></span>
<a x-show="selected.maps_url" :href="selected.maps_url" target="_blank" rel="noopener">Open in Maps →</a>
</p>
</section>
<section class="ei-drawer-section">
<h4>Contact & web</h4>
<div class="ei-drawer-kv" x-show="selected.email">
<span>E-mail</span>
<a :href="'mailto:'+selected.email" x-text="selected.email"></a>
</div>
<div class="ei-drawer-kv" x-show="selected.phone">
<span>Telefoon</span>
<a :href="'tel:'+selected.phone" x-text="selected.phone"></a>
</div>
<div class="ei-drawer-kv" x-show="selected.website">
<span>Website</span>
<a :href="selected.website" target="_blank" rel="noopener" x-text="selected.website"></a>
</div>
<p x-show="!selected.email && !selected.phone && !selected.website && !(selected.contacts||[]).length" class="ei-drawer-empty">
Geen direct contact in OSM — klik <strong>Sync contacten</strong> om website/e-mail te verrijken.
</p>
</section>
<section class="ei-drawer-section" x-show="selected.halal_score != null || selected.volume_band || selected.cold_chain || selected.halal_cert_notes">
<h4>Business & halal</h4>
<div class="ei-drawer-kv" x-show="selected.halal_score != null">
<span>Halal kans</span>
<strong x-text="Math.round(selected.halal_score||0)+'/100'"></strong>
</div>
<div class="ei-drawer-kv" x-show="selected.volume_band">
<span>Volume</span><span x-text="selected.volume_band"></span>
</div>
<div class="ei-drawer-kv" x-show="selected.cold_chain">
<span>Cold chain</span><span>Ja</span>
</div>
<p x-show="selected.halal_cert_notes" x-text="selected.halal_cert_notes"></p>
</section>
<section class="ei-drawer-section" x-show="selected.client_id">
<h4>CRM koppeling</h4>
<div class="ei-drawer-kv">
<span>Klant</span>
<a href="/clients" x-text="selected.crm_client_name || ('Klant #'+selected.client_id)"></a>
</div>
<div class="ei-drawer-kv" x-show="selected.crm_deal_title">
<span>Deal</span><span x-text="selected.crm_deal_title"></span>
</div>
</section>
<section class="ei-drawer-section">
<h4>Contactpersonen (<span x-text="(selected.contacts||[]).length"></span>)</h4>
<template x-for="c in (selected.contacts||[])" :key="c.id">
<div class="ei-contact-row">
<strong x-text="c.name || c.role || 'Contact'"></strong>
<span class="ei-contact-role" x-show="c.role" x-text="c.role"></span>
<span class="ei-contact-source" x-show="c.source" x-text="'Bron: '+c.source"></span>
<div x-show="c.email">📧 <a :href="'mailto:'+c.email" x-text="c.email"></a></div>
<div x-show="c.phone">📞 <a :href="'tel:'+c.phone" x-text="c.phone"></a></div>
<div x-show="c.mobile">📱 <span x-text="c.mobile"></span></div>
</div>
</template>
<p x-show="!(selected.contacts||[]).length" class="ei-drawer-empty">Geen contactpersonen in registry.</p>
</section>
<div class="ei-drawer-actions">
<button type="button" class="btn btn-sm" :class="selected.is_favorite ? 'btn-pulse' : ''" @click="toggleFavorite(selected.id, selected.is_favorite)">
<span x-text="selected.is_favorite ? '★ Favoriet' : '☆ Favoriet maken'"></span>
</button>
<button type="button" class="btn btn-sm btn-pulse-green" x-show="!selected.client_id" @click="pushEntityToCrm(selected.id)" :disabled="crmPushBusy"> Naar CRM</button>
<a class="btn btn-sm btn-pulse" x-show="selected.client_id" href="/clients">Open klant in CRM</a>
<a class="btn btn-sm" href="/deals" x-show="selected.deal_id">Open deal</a>
<a class="btn btn-sm" href="/deals">Pipeline / Deals</a>
<a class="btn btn-sm btn-pulse" :href="'/herman?context=export_catalog&entity='+selected.id">Catalogus via Herman</a>
<button type="button" class="btn btn-sm btn-pulse-green" @click="syncKind('contacts')" :disabled="busy">↻ Sync contacten</button>
</div>
</div>
</template>
</aside>
</div>
<!-- CRM bevestiging -->
<div class="ei-modal-root" x-show="crmModalOpen" x-cloak>
<div class="ei-drawer-backdrop" @click="crmModalOpen=false"></div>
<div class="ei-modal-card">
<h3>Toevoegen aan CRM</h3>
<p><strong x-text="selectedIds.length"></strong> organisatie(s) worden klant in CRM.</p>
<label class="ei-filter-check">
<input type="checkbox" x-model="crmCreateDeals" />
<span>Maak ook een deal per klant (aanbevolen)</span>
</label>
<p class="ei-modal-note">Bestaande klanten met hetzelfde e-mailadres worden gekoppeld i.p.v. dubbel aangemaakt.</p>
<div class="ei-modal-actions">
<button type="button" class="btn btn-sm" @click="crmModalOpen=false">Annuleren</button>
<button type="button" class="btn btn-sm btn-pulse-green" @click="pushToCrm()" :disabled="crmPushBusy">
<span x-text="crmPushBusy ? 'Bezig…' : 'Bevestig → CRM'"></span>
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="/static/js/export-intel.js?v=12"></script>
{% endblock %}