This commit is contained in:
@@ -3,6 +3,7 @@ $app = app();
|
|||||||
$flash = $app->flash()->get();
|
$flash = $app->flash()->get();
|
||||||
|
|
||||||
$eventsForJs = [];
|
$eventsForJs = [];
|
||||||
|
$threadsForJs = [];
|
||||||
try {
|
try {
|
||||||
$pdo = $app->pdo();
|
$pdo = $app->pdo();
|
||||||
if ($pdo) {
|
if ($pdo) {
|
||||||
@@ -29,9 +30,24 @@ try {
|
|||||||
'lng' => $r['lng'] !== null ? (float)$r['lng'] : null,
|
'lng' => $r['lng'] !== null ? (float)$r['lng'] : null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$communityCfg = require __DIR__ . '/../../../config/community.php';
|
||||||
|
$community = new \App\Community($pdo, $communityCfg);
|
||||||
|
$threads = $community->listThreads(8);
|
||||||
|
foreach ($threads as $thread) {
|
||||||
|
$threadsForJs[] = [
|
||||||
|
'id' => (int)$thread['id'],
|
||||||
|
'title' => (string)$thread['title'],
|
||||||
|
'body' => (string)$thread['body'],
|
||||||
|
'displayName' => (string)($thread['display_name'] ?: 'Mitglied'),
|
||||||
|
'createdAt' => (string)$thread['created_at'],
|
||||||
|
'answers' => (int)$thread['answers'],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
$eventsForJs = [];
|
$eventsForJs = [];
|
||||||
|
$threadsForJs = [];
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<main>
|
<main>
|
||||||
@@ -110,6 +126,21 @@ try {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="section alt" id="community-preview">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section__intro">
|
||||||
|
<h2>Tausch dich in der Community aus</h2>
|
||||||
|
</div>
|
||||||
|
<div class="slider">
|
||||||
|
<button class="btn ghost slider__nav" type="button" data-thread-slider-prev aria-label="Zurück">‹</button>
|
||||||
|
<div class="slider__viewport">
|
||||||
|
<div class="slider__track" id="threadSlider"></div>
|
||||||
|
</div>
|
||||||
|
<button class="btn ghost slider__nav" type="button" data-thread-slider-next aria-label="Weiter">›</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="section alt" id="mitgliederbereich">
|
<section class="section alt" id="mitgliederbereich">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section__intro">
|
<div class="section__intro">
|
||||||
@@ -146,6 +177,7 @@ try {
|
|||||||
</main>
|
</main>
|
||||||
<script>
|
<script>
|
||||||
window.__events = <?= json_encode($eventsForJs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
|
window.__events = <?= json_encode($eventsForJs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
|
||||||
|
window.__threads = <?= json_encode($threadsForJs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
|
||||||
</script>
|
</script>
|
||||||
<div class="modal" id="eventModal">
|
<div class="modal" id="eventModal">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ $isDebug = defined('APP_DEBUG') && APP_DEBUG === true;
|
|||||||
<a href="/">Home</a>
|
<a href="/">Home</a>
|
||||||
<a href="/search">Suche</a>
|
<a href="/search">Suche</a>
|
||||||
<a href="/#events">Termine</a>
|
<a href="/#events">Termine</a>
|
||||||
|
<a href="/community">Community</a>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="nav-actions">
|
<div class="nav-actions">
|
||||||
<?php if ($isLoggedIn): ?>
|
<?php if ($isLoggedIn): ?>
|
||||||
@@ -35,6 +36,7 @@ $isDebug = defined('APP_DEBUG') && APP_DEBUG === true;
|
|||||||
<a href="/">Home</a>
|
<a href="/">Home</a>
|
||||||
<a href="/search">Suche</a>
|
<a href="/search">Suche</a>
|
||||||
<a href="/#events">Termine</a>
|
<a href="/#events">Termine</a>
|
||||||
|
<a href="/community">Community</a>
|
||||||
<?php if ($isLoggedIn): ?>
|
<?php if ($isLoggedIn): ?>
|
||||||
<a class="btn ghost" href="/dashboard">Dashboard</a>
|
<a class="btn ghost" href="/dashboard">Dashboard</a>
|
||||||
<a class="btn block" href="/logout">Logout</a>
|
<a class="btn block" href="/logout">Logout</a>
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ body {
|
|||||||
.event__meta { display:flex; gap: 12px; flex-wrap: wrap; font-size: 13px; color: var(--color-muted); }
|
.event__meta { display:flex; gap: 12px; flex-wrap: wrap; font-size: 13px; color: var(--color-muted); }
|
||||||
.event__tags { display:flex; gap: 6px; flex-wrap: wrap; }
|
.event__tags { display:flex; gap: 6px; flex-wrap: wrap; }
|
||||||
.event__access { font-size: 12px; color: var(--color-highlight); font-weight: 700; text-transform: uppercase; letter-spacing: .5px; }
|
.event__access { font-size: 12px; color: var(--color-highlight); font-weight: 700; text-transform: uppercase; letter-spacing: .5px; }
|
||||||
|
.thread-card-small { min-width: 300px; max-width: 340px; padding: 18px; }
|
||||||
|
.clamp-4 { display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical; overflow:hidden; }
|
||||||
|
|
||||||
.label { font-size: 13px; color: var(--color-muted); }
|
.label { font-size: 13px; color: var(--color-muted); }
|
||||||
.input, .select { border-radius: var(--radius-sm); border-color: var(--color-border); background: #fff; }
|
.input, .select { border-radius: var(--radius-sm); border-color: var(--color-border); background: #fff; }
|
||||||
|
|||||||
@@ -51,12 +51,17 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
// Events from backend (injected on page); fallback to empty
|
// Events from backend (injected on page); fallback to empty
|
||||||
const events = Array.isArray(window.__events) ? window.__events : [];
|
const events = Array.isArray(window.__events) ? window.__events : [];
|
||||||
|
const threads = Array.isArray(window.__threads) ? window.__threads : [];
|
||||||
|
|
||||||
const el = {
|
const el = {
|
||||||
sliderTrack: document.getElementById('eventSlider'),
|
sliderTrack: document.getElementById('eventSlider'),
|
||||||
sliderViewport: document.querySelector('.slider__viewport'),
|
sliderViewport: document.querySelector('.slider__viewport'),
|
||||||
sliderPrev: document.querySelector('[data-slider-prev]'),
|
sliderPrev: document.querySelector('[data-slider-prev]'),
|
||||||
sliderNext: document.querySelector('[data-slider-next]'),
|
sliderNext: document.querySelector('[data-slider-next]'),
|
||||||
|
threadSliderTrack: document.getElementById('threadSlider'),
|
||||||
|
threadSliderViewport: document.getElementById('threadSlider')?.closest('.slider__viewport') || null,
|
||||||
|
threadSliderPrev: document.querySelector('[data-thread-slider-prev]'),
|
||||||
|
threadSliderNext: document.querySelector('[data-thread-slider-next]'),
|
||||||
modal: document.getElementById('eventModal'),
|
modal: document.getElementById('eventModal'),
|
||||||
modalBody: document.getElementById('eventModalBody'),
|
modalBody: document.getElementById('eventModalBody'),
|
||||||
modalTitle: document.getElementById('eventModalTitle'),
|
modalTitle: document.getElementById('eventModalTitle'),
|
||||||
@@ -118,14 +123,42 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const scrollSlider = (dir) => {
|
const renderThreadCard = (item) => {
|
||||||
if (!el.sliderViewport) return;
|
const preview = item.body.length > 170 ? `${item.body.slice(0, 170)}…` : item.body;
|
||||||
const amount = dir === 'next' ? 320 : -320;
|
return `
|
||||||
el.sliderViewport.scrollBy({ left: amount, behavior: 'smooth' });
|
<article class="card thread-card-small">
|
||||||
|
<div class="muted small">${fmtDate(item.createdAt)}</div>
|
||||||
|
<h3>${item.title}</h3>
|
||||||
|
<p class="muted clamp-4">${preview}</p>
|
||||||
|
<div class="event__meta">
|
||||||
|
<span>${item.displayName}</span>
|
||||||
|
<span>Antworten: ${item.answers}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-8" style="margin-top:8px;">
|
||||||
|
<a class="btn ghost" href="/community_thread?id=${item.id}">Beitrag öffnen</a>
|
||||||
|
</div>
|
||||||
|
</article>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
el.sliderPrev?.addEventListener('click', () => scrollSlider('prev'));
|
const renderThreadSlider = () => {
|
||||||
el.sliderNext?.addEventListener('click', () => scrollSlider('next'));
|
if (!el.threadSliderTrack) return;
|
||||||
|
if (!threads.length) {
|
||||||
|
el.threadSliderTrack.innerHTML = '<p class="muted">Noch keine Community-Beiträge vorhanden.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
el.threadSliderTrack.innerHTML = threads.map(renderThreadCard).join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollSlider = (viewport, dir) => {
|
||||||
|
if (!viewport) return;
|
||||||
|
const amount = dir === 'next' ? 320 : -320;
|
||||||
|
viewport.scrollBy({ left: amount, behavior: 'smooth' });
|
||||||
|
};
|
||||||
|
|
||||||
|
el.sliderPrev?.addEventListener('click', () => scrollSlider(el.sliderViewport, 'prev'));
|
||||||
|
el.sliderNext?.addEventListener('click', () => scrollSlider(el.sliderViewport, 'next'));
|
||||||
|
el.threadSliderPrev?.addEventListener('click', () => scrollSlider(el.threadSliderViewport, 'prev'));
|
||||||
|
el.threadSliderNext?.addEventListener('click', () => scrollSlider(el.threadSliderViewport, 'next'));
|
||||||
|
|
||||||
// Quick search with geocoding
|
// Quick search with geocoding
|
||||||
const geocode = async (query) => {
|
const geocode = async (query) => {
|
||||||
@@ -183,4 +216,5 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
el.modal?.addEventListener('click', (e) => { if (e.target === el.modal) el.modal.classList.remove('open'); });
|
el.modal?.addEventListener('click', (e) => { if (e.target === el.modal) el.modal.classList.remove('open'); });
|
||||||
|
|
||||||
renderSlider();
|
renderSlider();
|
||||||
|
renderThreadSlider();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user