222 lines
10 KiB
HTML
222 lines
10 KiB
HTML
{% extends "base.html" %}
|
||
{% block extra_head %}
|
||
<link rel="stylesheet" href="/static/css/hermes.css" />
|
||
<link rel="stylesheet" href="/static/css/beurs.css?v=1" />
|
||
{% endblock %}
|
||
{% block content %}
|
||
<div class="beurs-shell" x-data="beursHub()" x-init="init()">
|
||
|
||
<header class="beurs-hero">
|
||
<div>
|
||
<h1>Beurs & Live Intel <span class="live-badge">Live</span></h1>
|
||
<p class="hero-sub">Supermarkt aandelen · halal trends · concept lab · platform events</p>
|
||
<small x-text="'Laatste refresh: ' + (lastRefresh || '—')"></small>
|
||
</div>
|
||
<div class="beurs-hero-actions">
|
||
<button class="btn btn-pulse" @click="refreshAll()" :disabled="busy">↻ Alles verversen</button>
|
||
<a href="/" class="btn">Herman Dashboard</a>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="neo-tab-bar" role="tablist">
|
||
<button type="button" class="neo-tab" :class="{ active: tab === 'beurs' }" @click="setTab('beurs')">
|
||
<span class="neo-tab-icon">📈</span><span>Beurs Live</span>
|
||
</button>
|
||
<button type="button" class="neo-tab" :class="{ active: tab === 'trends' }" @click="setTab('trends')">
|
||
<span class="neo-tab-icon">☪️</span><span>Halal & Trends</span>
|
||
</button>
|
||
<button type="button" class="neo-tab" :class="{ active: tab === 'concepten' }" @click="setTab('concepten')">
|
||
<span class="neo-tab-icon">💡</span><span>Concept Lab</span>
|
||
</button>
|
||
<button type="button" class="neo-tab" :class="{ active: tab === 'events' }" @click="setTab('events')">
|
||
<span class="neo-tab-icon">⚡</span><span>Platform Live</span>
|
||
<span class="neo-tab-badge" x-show="platformStats.pending_approvals" x-text="platformStats.pending_approvals"></span>
|
||
</button>
|
||
<div class="neo-tab-indicator" :style="indicatorStyle"></div>
|
||
</div>
|
||
|
||
<!-- BEURS LIVE -->
|
||
<section x-show="tab === 'beurs'" x-cloak class="beurs-section">
|
||
<div class="beurs-summary-row">
|
||
<div class="beurs-stat-card">
|
||
<span>Gem. dag %</span>
|
||
<strong :class="(summary.avg_change_pct||0) >= 0 ? 'up' : 'down'" x-text="((summary.avg_change_pct||0) >= 0 ? '+' : '') + (summary.avg_change_pct||0) + '%'"></strong>
|
||
</div>
|
||
<div class="beurs-stat-card">
|
||
<span>Beursgenoteerd</span>
|
||
<strong x-text="summary.listed_count || listed.length"></strong>
|
||
</div>
|
||
<div class="beurs-stat-card">
|
||
<span>NL privé ketens</span>
|
||
<strong x-text="summary.unlisted_nl_count || unlisted.length"></strong>
|
||
</div>
|
||
<div class="beurs-stat-card source-card">
|
||
<span>Databron</span>
|
||
<strong><a href="https://finance.yahoo.com" target="_blank" rel="noopener">Yahoo Finance ↗</a></strong>
|
||
</div>
|
||
</div>
|
||
|
||
<h2 class="section-label"><span class="hm-live-dot"></span> Beursgenoteerde supermarkten</h2>
|
||
<div class="beurs-grid">
|
||
<template x-for="s in listed" :key="s.symbol">
|
||
<article class="beurs-card" :class="'trend-' + (s.trend || 'flat')">
|
||
<div class="beurs-card-head">
|
||
<div class="beurs-chain-dot" :style="'background:' + (s.color || '#38bdf8')"></div>
|
||
<div>
|
||
<strong x-text="s.symbol"></strong>
|
||
<small x-text="s.name"></small>
|
||
</div>
|
||
<span class="beurs-pct" :class="(s.change_pct||0) >= 0 ? 'up' : 'down'"
|
||
x-text="((s.change_pct||0) >= 0 ? '▲' : '▼') + ' ' + Math.abs(s.change_pct||0).toFixed(2) + '%'"></span>
|
||
</div>
|
||
<div class="beurs-chains" x-text="(s.chains||[]).join(' · ')"></div>
|
||
<div class="beurs-price" x-show="s.price != null">
|
||
<span x-text="Number(s.price).toFixed(2)"></span>
|
||
<small x-text="s.currency"></small>
|
||
<span class="market-state" x-text="s.market_state"></span>
|
||
</div>
|
||
<svg class="beurs-spark" x-show="(s.sparkline||[]).length" viewBox="0 0 100 30" preserveAspectRatio="none">
|
||
<polyline fill="none" stroke-width="2" :stroke="(s.change_pct||0) >= 0 ? '#4ade80' : '#fb7185'" :points="sparkPoints(s.sparkline)"/>
|
||
</svg>
|
||
<div class="beurs-eq"><span></span><span></span><span></span><span></span><span></span><span></span></div>
|
||
<div class="beurs-sources">
|
||
<a class="btn-source" :href="s.source_url" target="_blank" rel="noopener">Yahoo Finance ↗</a>
|
||
<small x-text="s.exchange"></small>
|
||
</div>
|
||
</article>
|
||
</template>
|
||
</div>
|
||
|
||
<h2 class="section-label" style="margin-top:1.5rem">Nederlandse ketens · niet beursgenoteerd</h2>
|
||
<p class="hint">Jumbo, Plus, Dirk, Lidl en ALDI zijn geen beursgenoteerde entiteiten — geen live koers via Yahoo Finance.</p>
|
||
<div class="beurs-grid unlisted">
|
||
<template x-for="u in unlisted" :key="u.name">
|
||
<article class="beurs-card unlisted-card">
|
||
<div class="beurs-card-head">
|
||
<div class="beurs-chain-dot" :style="'background:' + (u.color || '#64748b')"></div>
|
||
<div>
|
||
<strong x-text="u.name"></strong>
|
||
<small x-text="u.parent"></small>
|
||
</div>
|
||
<span class="beurs-tag">Privé</span>
|
||
</div>
|
||
<div class="beurs-chains" x-text="(u.chains||[]).join(' · ')"></div>
|
||
<p class="unlisted-note" x-text="u.note || u.exchange"></p>
|
||
<div class="beurs-sources">
|
||
<a class="btn-source" :href="u.source_url" target="_blank" rel="noopener">Keten info ↗</a>
|
||
</div>
|
||
</article>
|
||
</template>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- HALAL & TRENDS -->
|
||
<section x-show="tab === 'trends'" x-cloak class="beurs-section">
|
||
<div class="beurs-two-col">
|
||
<div class="panel-inner">
|
||
<h2>☪️ Halal vlees trends <span class="live-badge">RSS Live</span></h2>
|
||
<template x-for="h in trends.halal_meat_trends" :key="h.link">
|
||
<div class="feed-card">
|
||
<span class="tag tag-halal">HALAL VLEES</span>
|
||
<a :href="h.source_url || h.link" target="_blank" rel="noopener"><strong x-text="h.title"></strong></a>
|
||
<p x-text="(h.summary||'').substring(0,160)"></p>
|
||
<div class="feed-actions">
|
||
<a class="btn-source" :href="h.link" target="_blank">Artikel ↗</a>
|
||
<a class="btn-source" :href="h.feed_url" target="_blank">Bron feed ↗</a>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<p x-show="!trends.halal_meat_trends?.length" class="muted">Geen halal vlees trends — refresh RSS feeds.</p>
|
||
</div>
|
||
<div class="panel-inner">
|
||
<h2>🍱 Top gerechten & maaltijden</h2>
|
||
<template x-for="d in trends.top_dishes" :key="d.dish_keyword">
|
||
<div class="feed-card dish-card">
|
||
<div class="dish-rank">
|
||
<strong x-text="d.dish_keyword"></strong>
|
||
<span class="tag tag-dish" x-text="d.mentions + '× in feed'"></span>
|
||
</div>
|
||
<a x-show="d.latest_link" :href="d.source_url" target="_blank" rel="noopener">
|
||
<small x-text="d.latest_title"></small>
|
||
</a>
|
||
<div class="feed-actions" x-show="d.latest_link">
|
||
<a class="btn-source" :href="d.latest_link" target="_blank">Bron artikel ↗</a>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<h3 style="margin-top:1.25rem">Markt trends DB</h3>
|
||
<template x-for="t in trends.market_trends" :key="t.trend_name">
|
||
<div class="feed-card">
|
||
<strong x-text="t.trend_name"></strong>
|
||
<p x-text="t.description"></p>
|
||
<span class="tag tag-trend" x-text="'Kans ' + Math.round((t.opportunity_score||0)*100) + '%'"></span>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- CONCEPT LAB -->
|
||
<section x-show="tab === 'concepten'" x-cloak class="beurs-section">
|
||
<div class="concept-header">
|
||
<h2>💡 Concept Lab</h2>
|
||
<p class="hint">Automatisch gegenereerde productconcepten op basis van live beurs, halal trends en top gerechten.</p>
|
||
<button class="btn btn-pulse-green" @click="loadConcepts()" :disabled="busy">↻ Nieuwe concepten</button>
|
||
</div>
|
||
<div class="concept-grid">
|
||
<template x-for="c in concepts" :key="c.id">
|
||
<article class="concept-card">
|
||
<span class="concept-num" x-text="'#' + c.id"></span>
|
||
<p x-text="c.concept"></p>
|
||
<div class="concept-based">
|
||
<small x-text="'Gebaseerd op: ' + (c.based_on?.dish || '') + ' · ' + (c.based_on?.market_signal || '')"></small>
|
||
</div>
|
||
<div class="feed-actions">
|
||
<template x-for="(url, i) in (c.source_urls||[])" :key="i">
|
||
<a class="btn-source" :href="url" target="_blank" rel="noopener">Bron <span x-text="i+1"></span> ↗</a>
|
||
</template>
|
||
</div>
|
||
</article>
|
||
</template>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- PLATFORM LIVE -->
|
||
<section x-show="tab === 'events'" x-cloak class="beurs-section">
|
||
<div class="beurs-summary-row">
|
||
<div class="beurs-stat-card"><span>Events totaal</span><strong x-text="platformStats.total_events||0"></strong></div>
|
||
<div class="beurs-stat-card"><span>Laatste uur</span><strong x-text="platformStats.events_last_hour||0"></strong></div>
|
||
<div class="beurs-stat-card warn"><span>Wacht op OK</span><strong x-text="platformStats.pending_approvals||0"></strong></div>
|
||
</div>
|
||
<div class="events-toolbar">
|
||
<select class="form-input" x-model="eventFilter" @change="loadEvents()">
|
||
<option value="">Alle agents</option>
|
||
<option value="herman">Herman</option>
|
||
<option value="retail_intel">Retail Intel</option>
|
||
<option value="rss_feeds">RSS Feeds</option>
|
||
<option value="wholesale_scraper">Wholesale</option>
|
||
<option value="halal_registry">Halal</option>
|
||
<option value="hr">HR</option>
|
||
</select>
|
||
<span class="live-badge">Auto-refresh 5s</span>
|
||
</div>
|
||
<div class="events-stream">
|
||
<template x-for="ev in events" :key="ev.id">
|
||
<a class="event-row" :href="ev.click_url" :target="ev.is_external ? '_blank' : '_self'" rel="noopener">
|
||
<span class="event-time" x-text="(ev.created_at||'').substring(11,16)"></span>
|
||
<span class="event-agent badge" x-text="ev.agent_name"></span>
|
||
<span class="event-title" x-text="ev.title"></span>
|
||
<span class="event-status" :class="'st-'+ev.status" x-text="ev.status"></span>
|
||
<span class="event-source" x-text="ev.is_external ? '↗ extern' : '→ platform'"></span>
|
||
</a>
|
||
</template>
|
||
<p x-show="!events.length" class="muted">Geen events gelogd.</p>
|
||
</div>
|
||
</section>
|
||
|
||
</div>
|
||
{% endblock %}
|
||
{% block scripts %}
|
||
<script src="/static/js/beurs.js?v=1"></script>
|
||
{% endblock %}
|