added frontend and made it usable

This commit is contained in:
Hendrik Behme committed 2026-08-15 14:50:42 +02:00
1 parent 96408f00d8
commit b9b15152bc
50 files changed
+7974 -1

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
{% set category = event.categoryNames|first %}
{% set image = event.primaryImage %}
<a class="event-card" href="{{ path('event_show', {id: event.id}) }}">
{% if image %}
<div class="glyph-tile" style="padding:0;">
<img src="{{ image.storagePath }}" alt="" loading="lazy" style="position:absolute; inset:0; width:100%; height:100%; object-fit:cover;">
</div>
{% else %}
<div class="glyph-tile" style="background:{{ category|category_bg }}; color:{{ category|category_color }};">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="12" cy="12" r="9"/><path d="M12 3v18M3 12h18"/></svg>
</div>
{% endif %}
<div class="body">
{% if category %}
<span class="tag" style="background:{{ category|category_bg }}; color:{{ category|category_color }};">{{ category }}</span>
{% endif %}
<h3>{{ event.title }}</h3>
<p class="when">{{ event.startDateTime ? event.startDateTime|date('D, M j · H:i') : 'Date TBD' }}</p>
<p class="place">{{ [event.street, event.city]|filter(v => v)|join(', ') ?: event.country }}</p>
</div>
</a>
+55
View File
@@ -0,0 +1,55 @@
{% extends 'base.html.twig' %}
{% block title %}Browse events — VINDN{% endblock %}
{% block head_extra %}
<style>
.toolbar{ display:flex; justify-content:space-between; align-items:center; gap:16px; flex-wrap:wrap; margin-bottom:20px; }
</style>
{% endblock %}
{% block body %}
<section class="section" style="padding-bottom:0;">
<div class="wrap">
<p class="eyebrow" style="margin-bottom:10px;">All events</p>
<div class="toolbar">
<h1 style="font-size:clamp(28px,4vw,40px);">What's on{{ activeCategory ? ' — ' ~ activeCategory : '' }}</h1>
<p class="mono" style="font-size:13px; color:var(--muted);">{{ total }} event{{ total == 1 ? '' : 's' }}</p>
</div>
<input type="checkbox" id="catsToggle" class="chip-toggle-checkbox">
<div class="chip-row chip-row-collapsible">
<a href="{{ path('event_list') }}" class="chip {{ activeCategory is null ? 'is-active' : '' }}">All</a>
{% for category in categories %}
<a href="{{ path('event_list', {category: category}) }}" class="chip {{ activeCategory == category ? 'is-active' : '' }}">{{ category }}</a>
{% endfor %}
</div>
<label for="catsToggle" class="chip-toggle-btn" style="margin-bottom:32px;">
<span class="show-more">Show all categories ↑</span>
<span class="show-less">Show fewer categories ↑</span>
</label>
</div>
</section>
<section class="section" style="padding-top:0;">
<div class="wrap">
<div class="card-grid">
{% for event in events %}
{% include 'events/_card.html.twig' with { event: event } %}
{% else %}
<div class="no-results" style="grid-column:1/-1; padding:60px 20px; text-align:center; color:var(--muted); border:1px dashed var(--line-strong); border-radius:var(--r-card);">
<p class="eyebrow" style="margin-bottom:8px;">No events</p>
<p>Nothing matches this filter yet — try "All" or check back once the importer has run.</p>
</div>
{% endfor %}
</div>
{% include '_pager.html.twig' with {
currentPage: page,
totalPages: totalPages,
routeName: 'event_list',
routeParams: { category: activeCategory }
} %}
</div>
</section>
{% endblock %}
+113
View File
@@ -0,0 +1,113 @@
{% extends 'base.html.twig' %}
{% set category = event.categoryNames|first %}
{% set image = event.primaryImage %}
{% block title %}{{ event.title }} — VINDN{% endblock %}
{% block head_extra %}
{% include '_leaflet_head.html.twig' %}
<style>
.breadcrumb{ font-family:var(--f-mono); font-size:12px; color:var(--muted); margin-bottom:18px; }
.breadcrumb a:hover{ color:var(--accent); }
.hero-tile{
aspect-ratio: 16/6; border-radius:var(--r-card); position:relative; overflow:hidden;
display:flex; align-items:center; justify-content:center;
}
.hero-tile img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.hero-tile svg{ width:18%; opacity:.9; }
.detail-grid{ display:grid; grid-template-columns: 1fr 360px; gap:44px; margin-top:36px; align-items:start; }
.detail-grid h2{ font-size:22px; margin-bottom:14px; }
.prose p{ color:var(--ink-soft); margin:0 0 16px; font-size:15.5px; line-height:1.7; }
.info-board{ background:var(--board-bg); border-radius:var(--r-card); color:var(--board-text); padding:24px; position:sticky; top:96px; }
.info-board .row{ display:flex; justify-content:space-between; gap:12px; padding:13px 0; border-bottom:1px solid #2B2B22; font-size:13px; }
.info-board .row:first-child{ padding-top:0; }
.info-board .row:last-child{ border-bottom:none; }
.info-board .row .k{ font-family:var(--f-mono); color:var(--board-muted); flex:none; }
.info-board .row .v{ font-family:var(--f-mono); text-align:right; }
.info-board .row .v a{ color:inherit; }
#eventMapCanvas{ aspect-ratio:16/8; border-radius:var(--r-card); }
@media (max-width:900px){ .detail-grid{ grid-template-columns:1fr; } .info-board{ position:static; } }
</style>
{% endblock %}
{% block body %}
<section class="section">
<div class="wrap">
<p class="breadcrumb">
<a href="{{ path('event_list') }}">Browse events</a>
{% if category %} / <a href="{{ path('event_list', {category: category}) }}">{{ category }}</a>{% endif %}
/ {{ event.title }}
</p>
<div class="hero-tile" style="background:{{ category|category_bg }};">
{% if image %}
<img src="{{ image.storagePath }}" alt="">
{% else %}
<svg viewBox="0 0 24 24" fill="none" stroke="{{ category|category_color }}" stroke-width="1.4"><circle cx="12" cy="12" r="9"/><path d="M12 3v18M3 12h18"/></svg>
{% endif %}
</div>
<div style="display:flex; align-items:flex-start; justify-content:space-between; gap:20px; margin-top:28px; flex-wrap:wrap;">
<div>
{% if category %}<span class="tag" style="background:{{ category|category_bg }}; color:{{ category|category_color }};">{{ category }}</span>{% endif %}
<h1 style="font-size:clamp(28px,4vw,42px); margin-top:14px; max-width:640px;">{{ event.title }}</h1>
<p class="mono" style="color:var(--muted); margin-top:10px; font-size:13px;">
{{ event.startDateTime ? event.startDateTime|date('D, M j · H:i')|upper : 'DATE TBD' }}
{% if event.city %} · {{ event.city|upper }}{% endif %}
</p>
</div>
</div>
<div class="detail-grid">
<div>
{% if event.description %}
<div class="prose">
<h2>About this event</h2>
<p>{{ event.description }}</p>
</div>
{% elseif event.teaser %}
<div class="prose">
<h2>About this event</h2>
<p>{{ event.teaser }}</p>
</div>
{% endif %}
{% if event.hasCoordinates %}
<h2 style="margin-top:36px;">Location</h2>
<div id="eventMapCanvas" role="application" aria-label="Map showing the event location"></div>
{% endif %}
<p style="margin-top:14px; font-size:14px; color:var(--muted);">
{{ [event.street, (event.zip ~ ' ' ~ event.city)|trim, event.country]|filter(v => v)|join(', ') }}
</p>
</div>
<!-- sticky info board -->
<aside class="info-board" aria-label="Event details">
<div class="row"><span class="k">Starts</span><span class="v">{{ event.startDateTime ? event.startDateTime|date('D, M j · H:i') : 'TBD' }}</span></div>
{% if event.endDateTime %}
<div class="row"><span class="k">Ends</span><span class="v">{{ event.endDateTime|date('D, M j · H:i') }}</span></div>
{% endif %}
{% if event.city %}<div class="row"><span class="k">City</span><span class="v">{{ event.city }}</span></div>{% endif %}
{% if event.type %}<div class="row"><span class="k">Type</span><span class="v">{{ event.type }}</span></div>{% endif %}
{% if event.phone %}<div class="row"><span class="k">Phone</span><span class="v"><a href="tel:{{ event.phone }}">{{ event.phone }}</a></span></div>{% endif %}
{% if event.email %}<div class="row"><span class="k">Email</span><span class="v"><a href="mailto:{{ event.email }}">{{ event.email }}</a></span></div>{% endif %}
{% if event.web %}
<div class="row" style="border-bottom:none;"><span class="k">Website</span><span class="v"><a href="{{ event.web }}" target="_blank" rel="noopener">Visit site →</a></span></div>
{% endif %}
</aside>
</div>
</div>
</section>
{% endblock %}
{% block scripts %}
{% if event.hasCoordinates %}
{% include '_leaflet_scripts.html.twig' %}
<script type="application/json" id="event-map-data">{{ mapEventJson|raw }}</script>
<script>
Vindn.renderPinMap('eventMapCanvas', Vindn.readEmbeddedJson('event-map-data'), { zoom: 14, zoomControl: true, attributionControl: true });
</script>
{% endif %}
{% endblock %}