added frontend and made it usable
This commit is contained in:
1 parent
96408f00d8
commit
b9b15152bc
50 files changed
+7974
-1
No files matched your search
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Repository\CategoryRepository;
|
||||
use App\Repository\EventRepository;
|
||||
use App\Service\EventMapSerializer;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class EventController extends AbstractController
|
||||
{
|
||||
private const PER_PAGE = 24;
|
||||
|
||||
#[Route('/events', name: 'event_list')]
|
||||
public function list(Request $request, EventRepository $events, CategoryRepository $categories): Response
|
||||
{
|
||||
$page = max(1, $request->query->getInt('page', 1));
|
||||
$category = $request->query->get('category') ?: null;
|
||||
$offset = (($page ?: 1) - 1) * self::PER_PAGE;
|
||||
|
||||
$items = $events->findPage(self::PER_PAGE, $offset, $category);
|
||||
$total = $events->countPage($category);
|
||||
|
||||
return $this->render('events/list.html.twig', [
|
||||
'activeNav' => 'events',
|
||||
'events' => $items,
|
||||
'categories' => $categories->findUsedNames(),
|
||||
'activeCategory' => $category,
|
||||
'page' => $page,
|
||||
'totalPages' => max(1, (int) ceil($total / self::PER_PAGE)),
|
||||
'total' => $total,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/events/{id}', name: 'event_show', requirements: ['id' => '\d+'])]
|
||||
public function show(int $id, EventRepository $events, EventMapSerializer $mapSerializer): Response
|
||||
{
|
||||
$event = $events->findOneNotDeleted($id);
|
||||
if (!$event) {
|
||||
throw $this->createNotFoundException('Event nicht gefunden.');
|
||||
}
|
||||
|
||||
return $this->render('events/show.html.twig', [
|
||||
'activeNav' => 'events',
|
||||
'event' => $event,
|
||||
'mapEventJson' => $event->hasCoordinates() ? $mapSerializer->toJson([$event]) : '[]',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Repository\CategoryRepository;
|
||||
use App\Repository\EventRepository;
|
||||
use App\Service\DefaultLocation;
|
||||
use App\Service\EventMapSerializer;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class HomeController extends AbstractController
|
||||
{
|
||||
// Matches /map's MAX_RANGE_KM/POOL_LIMIT: the light map only shows the
|
||||
// nearest 20, but the candidate pool it picks from needs to actually
|
||||
// cover that area, not just be an arbitrary unordered chunk of events.
|
||||
private const POOL_RADIUS_KM = 100;
|
||||
private const POOL_LIMIT = 3000;
|
||||
|
||||
#[Route('/', name: 'home')]
|
||||
public function index(
|
||||
EventRepository $events,
|
||||
CategoryRepository $categories,
|
||||
EventMapSerializer $mapSerializer
|
||||
): Response {
|
||||
// Same fallback as /map: default to Köln until real geolocation
|
||||
// (checked client-side in map.js) says otherwise.
|
||||
$lat = DefaultLocation::LAT;
|
||||
$lon = DefaultLocation::LON;
|
||||
|
||||
$pool = $events->findWithinRadius($lat, $lon, self::POOL_RADIUS_KM, self::POOL_LIMIT);
|
||||
|
||||
return $this->render('home.html.twig', [
|
||||
'activeNav' => 'home',
|
||||
'upcomingEvents' => $events->findRecent(6),
|
||||
'mapEventsJson' => $mapSerializer->toJson($pool),
|
||||
'popularCategories' => $categories->findMostUsedNames(12),
|
||||
'centerLat' => $lat,
|
||||
'centerLon' => $lon,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Repository\CategoryRepository;
|
||||
use App\Repository\EventRepository;
|
||||
use App\Service\DefaultLocation;
|
||||
use App\Service\EventMapSerializer;
|
||||
use App\Service\RangeDefaults;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class MapController extends AbstractController
|
||||
{
|
||||
// Safety cap on the pool shipped to the client, not a practical limit:
|
||||
// even the densest 100km radius seen (NRW around Köln) comes in under
|
||||
// 3000, so this should never actually truncate in normal use.
|
||||
private const POOL_LIMIT = 3000;
|
||||
|
||||
#[Route('/map', name: 'map')]
|
||||
public function index(
|
||||
Request $request,
|
||||
EventRepository $events,
|
||||
CategoryRepository $categories,
|
||||
EventMapSerializer $mapSerializer
|
||||
): Response {
|
||||
$lat = (float) ($request->query->get('lat') ?: DefaultLocation::LAT);
|
||||
$lon = (float) ($request->query->get('lon') ?: DefaultLocation::LON);
|
||||
|
||||
$rangeKm = (int) $request->query->get('range', RangeDefaults::DEFAULT_KM);
|
||||
if ($rangeKm <= 0 || $rangeKm > RangeDefaults::MAX_KM) {
|
||||
$rangeKm = RangeDefaults::DEFAULT_KM;
|
||||
}
|
||||
|
||||
// Ship the pool sized to the *max* selectable range so the range
|
||||
// dropdown and small map clicks/drags can be handled entirely
|
||||
// client-side, without another page load.
|
||||
$pool = $events->findWithinRadius($lat, $lon, RangeDefaults::MAX_KM, self::POOL_LIMIT);
|
||||
|
||||
return $this->render('map.html.twig', [
|
||||
'activeNav' => 'map',
|
||||
'mapEventsJson' => $mapSerializer->toJson($pool),
|
||||
'categories' => $categories->findUsedNames(),
|
||||
'centerLat' => $lat,
|
||||
'centerLon' => $lon,
|
||||
'rangeKm' => $rangeKm,
|
||||
'rangeOptions' => RangeDefaults::OPTIONS_KM,
|
||||
'maxRangeKm' => RangeDefaults::MAX_KM,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Repository\CategoryRepository;
|
||||
use App\Repository\EventRepository;
|
||||
use App\Service\RangeDefaults;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class SearchController extends AbstractController
|
||||
{
|
||||
private const PER_PAGE = 24;
|
||||
|
||||
#[Route('/search', name: 'search')]
|
||||
public function index(Request $request, EventRepository $events, CategoryRepository $categories): Response
|
||||
{
|
||||
$q = trim((string) $request->query->get('q', ''));
|
||||
$category = $request->query->get('category') ?: null;
|
||||
$when = $request->query->get('when') ?: 'any';
|
||||
$page = max(1, $request->query->getInt('page', 1));
|
||||
|
||||
// Location filtering is opt-in — plain text/category search behaves
|
||||
// exactly as before unless a place/"use my location" actually set
|
||||
// lat/lon, unlike /map which always has *some* center.
|
||||
$latParam = $request->query->get('lat');
|
||||
$lonParam = $request->query->get('lon');
|
||||
$hasLocation = null !== $latParam && null !== $lonParam && '' !== $latParam && '' !== $lonParam;
|
||||
$lat = $hasLocation ? (float) $latParam : null;
|
||||
$lon = $hasLocation ? (float) $lonParam : null;
|
||||
|
||||
$rangeKm = (int) $request->query->get('range', RangeDefaults::DEFAULT_KM);
|
||||
if ($rangeKm <= 0 || $rangeKm > RangeDefaults::MAX_KM) {
|
||||
$rangeKm = RangeDefaults::DEFAULT_KM;
|
||||
}
|
||||
|
||||
$distances = [];
|
||||
$locationIds = null;
|
||||
if ($hasLocation) {
|
||||
$distances = $events->distancesWithinRadius($lat, $lon, $rangeKm, 3000);
|
||||
$locationIds = array_keys($distances);
|
||||
}
|
||||
|
||||
$offset = ($page - 1) * self::PER_PAGE;
|
||||
$results = $events->search($q ?: null, $category, $when, self::PER_PAGE, $offset, $locationIds);
|
||||
$total = $events->countSearch($q ?: null, $category, $when, $locationIds);
|
||||
|
||||
return $this->render('search.html.twig', [
|
||||
'activeNav' => 'search',
|
||||
'q' => $q,
|
||||
'events' => $results,
|
||||
'categories' => $categories->findUsedNames(),
|
||||
'activeCategory' => $category,
|
||||
'when' => $when,
|
||||
'hasLocation' => $hasLocation,
|
||||
'lat' => $lat,
|
||||
'lon' => $lon,
|
||||
'rangeKm' => $rangeKm,
|
||||
'rangeOptions' => RangeDefaults::OPTIONS_KM,
|
||||
'distances' => $distances,
|
||||
'page' => $page,
|
||||
'totalPages' => max(1, (int) ceil($total / self::PER_PAGE)),
|
||||
'total' => $total,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user