2026-06-23 10:04:23 +00:00
{% extends "base.html" %}
{% block title %}Wereldexport · Foodlinkk B2B{% endblock %}
{% block extra_head %}
< link rel = "stylesheet" href = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
< link rel = "stylesheet" href = "https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css" />
< link rel = "stylesheet" href = "https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css" />
2026-07-19 18:25:01 +00:00
< link rel = "stylesheet" href = "/static/css/export-intel.css?v=12" />
2026-06-23 10:04:23 +00:00
< link rel = "stylesheet" href = "/static/css/export-intel-tabs.css?v=10" />
< script src = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" ></ script >
< script src = "https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js" ></ script >
< script src = "/static/js/export-intel-map.js?v=8" ></ 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 >
2026-07-19 18:25:01 +00:00
< div class = "ei-selection-bar" x-show = "showSelectionBar()" x-cloak >
2026-06-23 10:04:23 +00:00
< div class = "ei-selection-left" >
< span class = "ei-selection-count" x-show = "selectedIds.length" x-text = "selectedIds.length + ' geselecteerd'" ></ span >
2026-07-19 18:25:01 +00:00
< 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 >
2026-06-23 10:04:23 +00:00
</ 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 >
2026-07-19 18:25:01 +00:00
< div class = "ei-filter-group ei-filter-group-collection" x-show = "showCrmFilter()" x-cloak >
< label class = "ei-filter-check" x-show = "tab === 'map'" >
2026-06-23 10:04:23 +00:00
< 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-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" >
2026-07-19 18:25:01 +00:00
< 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 >
2026-06-23 10:04:23 +00:00
</ 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" >
2026-07-19 18:25:01 +00:00
< 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 >
2026-06-23 10:04:23 +00:00
< tbody >
< template x-for = "c in contacts" :key = "c.id" >
2026-07-19 18:25:01 +00:00
< 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 >
2026-06-23 10:04:23 +00:00
< td x-text = "c.country_iso2" ></ td >
2026-07-19 18:25:01 +00:00
< td x-text = "c.role || '—'" ></ td >
2026-06-23 10:04:23 +00:00
< td x-text = "c.name||'—'" ></ td >
< td x-text = "c.email||'—'" ></ td >
< td x-text = "c.phone||c.mobile||'—'" ></ td >
2026-07-19 18:25:01 +00:00
< 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 >
2026-06-23 10:04:23 +00:00
</ tr >
</ template >
</ tbody >
</ table >
</ div >
2026-07-19 18:25:01 +00:00
< 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 >
2026-06-23 10:04:23 +00:00
< 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 %}
2026-07-19 18:25:01 +00:00
< script src = "/static/js/export-intel.js?v=11" ></ script >
2026-06-23 10:04:23 +00:00
{% endblock %}