Public:
- /escape-rooms: Leaflet + OpenStreetMap map with marker clustering and a
searchable list, both built from a cached slim JSON feed
- /escape-rooms/{id}-{slug}: room detail with a mini-map and user reviews
(rating + title + body, login required)
- /escape-rooms/suggest: visitor submission form with click-to-drop-pin map
and reCAPTCHA v3; new rooms land as pending
Data:
- EscapeRoom / EscapeRoomReview entities (soft-delete), migration
- app:escaperooms:import-osm console command pulls leisure=escape_game from
the Overpass API, upserts by (osmType, osmId), skips locked rows;
--tiled / --area / --bbox options
Admin (new "Website Admin" area, separate from Game Admin):
- src/Website/Controller/AdminEscapeRoomController.php at /admin/escape-rooms:
status-filtered list, new/edit, publish/hide/reject, delete, review removal
- game admin sidebar extracted into a block and cross-linked
Assets: leaflet + leaflet.markercluster; escaperoom-map and escaperoom-detail
Encore entries.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
75 lines
2.4 KiB
Twig
75 lines
2.4 KiB
Twig
{% extends 'layout/site.html.twig' %}
|
|
|
|
{% block title %}Escape room map | {{ 'site.name'|trans }}{% endblock %}
|
|
|
|
{% block stylesheets %}
|
|
{{ parent() }}
|
|
{{ encore_entry_link_tags('escaperoom-map') }}
|
|
<style>
|
|
#escaperoom-map {
|
|
height: 70vh;
|
|
min-height: 420px;
|
|
width: 100%;
|
|
border-radius: 8px;
|
|
z-index: 0;
|
|
}
|
|
.escaperoom-list-wrap {
|
|
background: #fff;
|
|
border-radius: 8px;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, .07);
|
|
overflow: hidden;
|
|
}
|
|
.escaperoom-list-wrap table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: .9rem;
|
|
}
|
|
.escaperoom-list-wrap th,
|
|
.escaperoom-list-wrap td {
|
|
padding: .6rem .9rem;
|
|
text-align: left;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block body %}
|
|
{% set detailTemplate = path('website_escaperoom_show', {id: 987654321, slug: 'zzslugzz'})|replace({'987654321': '__ID__', 'zzslugzz': '__SLUG__'}) %}
|
|
|
|
<div class="d-flex flex-wrap align-items-baseline justify-content-between mb-3">
|
|
<div>
|
|
<h1 class="display-6 fw-bold mb-1">Physical escape rooms</h1>
|
|
<p class="text-secondary mb-0">Zoom in to find rooms near you, then open one to read and leave reviews.</p>
|
|
</div>
|
|
<div class="text-end">
|
|
<div id="escaperoom-status" class="small text-secondary">{{ publishedCount }} rooms on the map</div>
|
|
<a href="{{ path('website_escaperoom_suggest') }}" class="btn btn-sm btn-outline-primary mt-1">Suggest a room</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="escaperoom-map"
|
|
data-endpoint="{{ path('website_escaperooms_data') }}"
|
|
data-detail-template="{{ detailTemplate }}"
|
|
class="mb-4"></div>
|
|
|
|
<div class="escaperoom-list-wrap">
|
|
<table id="escaperoom-list">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>City</th>
|
|
<th>Country</th>
|
|
<th>Rating</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td colspan="4" class="text-center text-secondary py-4">Loading rooms…</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block javascripts %}
|
|
{{ parent() }}
|
|
{{ encore_entry_script_tags('escaperoom-map') }}
|
|
{% endblock %}
|