39656-vm/index.php
2026-04-15 16:05:12 +00:00

213 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
require_once __DIR__ . '/urban_hikes.php';
$filters = urban_hikes_fetch_filters();
$routes = urban_hikes_search($filters);
$cities = urban_hikes_cities();
$stats = urban_hikes_stats();
$flash = urban_hikes_get_flash();
$storage = urban_hikes_storage();
$pageTitle = urban_hikes_project_name() . ' | Urban hiking routes directory';
$pageDescription = 'Find urban hiking routes in major cities by distance, time, and difficulty. Compare highlights, browse by city, and plan a full day on foot.';
urban_hikes_render_head($pageTitle, $pageDescription);
urban_hikes_render_nav('directory');
?>
<main>
<section class="hero-shell border-bottom">
<div class="container-lg px-3 px-lg-4 py-5 py-lg-6">
<div class="row g-4 align-items-end">
<div class="col-lg-7">
<span class="eyebrow">Urban hiking directory</span>
<h1 class="display-title mt-3 mb-3">Big-city routes for locals, visitors, and long walking days.</h1>
<p class="lead-copy mb-4">Search curated city hikes by place, distance, and difficulty. Every route gives you the basics fast: how long it takes, where it starts, and what makes it worth the walk.</p>
<div class="d-flex flex-wrap gap-2">
<a class="btn btn-dark px-4" href="#results">Browse routes</a>
<a class="btn btn-outline-secondary px-4" href="admin.php">Add a route</a>
</div>
</div>
<div class="col-lg-5">
<div class="panel-card stats-panel h-100">
<div class="row g-3">
<div class="col-4">
<p class="metric-label">Routes</p>
<p class="metric-value"><?= (int)$stats['routes'] ?></p>
</div>
<div class="col-4">
<p class="metric-label">Cities</p>
<p class="metric-value"><?= (int)$stats['cities'] ?></p>
</div>
<div class="col-4">
<p class="metric-label">Avg km</p>
<p class="metric-value"><?= htmlspecialchars(number_format((float)$stats['avg_distance'], 1)) ?></p>
</div>
</div>
<div class="divider my-4"></div>
<p class="small text-muted mb-2">Useful when you want:</p>
<ul class="compact-list mb-0">
<li>A scenic 23 hour city plan</li>
<li>A route that matches your walking energy</li>
<li>Quick route comparison before you leave the hotel</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<section class="section-shell border-bottom" id="results">
<div class="container-lg px-3 px-lg-4 py-4 py-lg-5">
<?php if ($flash): ?>
<div class="toast-container position-fixed top-0 end-0 p-3">
<div class="toast app-toast text-bg-dark border-0" id="appToast" role="status" aria-live="polite" aria-atomic="true">
<div class="d-flex">
<div class="toast-body"><?= htmlspecialchars($flash['message']) ?></div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
</div>
<?php endif; ?>
<?php if (!$storage['ready']): ?>
<div class="alert alert-warning mb-4" role="alert">
Route browsing is visible, but saving new routes is temporarily unavailable because the database connection failed.
</div>
<?php endif; ?>
<div class="row g-4 align-items-start">
<div class="col-xl-4">
<div class="panel-card sticky-filter-card">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<span class="eyebrow">Find a route</span>
<h2 class="section-title mb-0 mt-2">Search filters</h2>
</div>
<a class="small text-decoration-none" href="index.php#results">Reset</a>
</div>
<form method="get" action="index.php#results" class="row g-3" data-results-form>
<div class="col-12">
<label class="form-label" for="q">Keyword</label>
<input class="form-control" type="text" id="q" name="q" value="<?= htmlspecialchars($filters['q']) ?>" placeholder="Canal, skyline, park, stairs" />
</div>
<div class="col-md-6 col-xl-12">
<label class="form-label" for="city">City</label>
<select class="form-select" id="city" name="city" data-autosubmit="change">
<option value="">All cities</option>
<?php foreach ($cities as $cityRow): ?>
<option value="<?= htmlspecialchars($cityRow['city']) ?>" <?= $filters['city'] === $cityRow['city'] ? 'selected' : '' ?>><?= htmlspecialchars($cityRow['city']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6 col-xl-12">
<label class="form-label" for="difficulty">Difficulty</label>
<select class="form-select" id="difficulty" name="difficulty" data-autosubmit="change">
<option value="">Any level</option>
<?php foreach (['Easy', 'Moderate', 'Challenging'] as $level): ?>
<option value="<?= htmlspecialchars($level) ?>" <?= $filters['difficulty'] === $level ? 'selected' : '' ?>><?= htmlspecialchars($level) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12">
<label class="form-label" for="max_distance">Maximum distance</label>
<select class="form-select" id="max_distance" name="max_distance" data-autosubmit="change">
<option value="">Any length</option>
<?php foreach ([5, 8, 10, 12] as $distance): ?>
<option value="<?= $distance ?>" <?= (float)$filters['max_distance'] === (float)$distance ? 'selected' : '' ?>>Up to <?= $distance ?> km</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12 d-flex gap-2">
<button class="btn btn-dark flex-grow-1" type="submit">Apply filters</button>
<a class="btn btn-outline-secondary" href="index.php#results">Clear</a>
</div>
</form>
</div>
</div>
<div class="col-xl-8">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-end gap-3 mb-3">
<div>
<span class="eyebrow">Directory</span>
<h2 class="section-title mb-1 mt-2"><?= count($routes) ?> route<?= count($routes) === 1 ? '' : 's' ?> matched</h2>
<p class="text-muted mb-0">Compact route cards with the essentials first: timing, effort, area, and highlights.</p>
</div>
<a class="btn btn-outline-secondary btn-sm align-self-start align-self-md-auto" href="admin.php">Suggest a new route</a>
</div>
<?php if (!$routes): ?>
<div class="panel-card empty-state">
<span class="eyebrow">No results</span>
<h3 class="h5 mt-2">No routes match these filters yet.</h3>
<p class="text-muted mb-4">Try widening the distance or removing the city filter. You can also add your own route to start a new city collection.</p>
<a class="btn btn-dark" href="admin.php">Add a route</a>
</div>
<?php else: ?>
<div class="row g-3">
<?php foreach ($routes as $route): ?>
<?php $highlights = array_slice(urban_hikes_highlight_items((string)$route['highlights']), 0, 3); ?>
<div class="col-12">
<article class="panel-card route-card h-100">
<div class="d-flex flex-column flex-lg-row gap-3 justify-content-between">
<div class="flex-grow-1">
<div class="d-flex flex-wrap gap-2 mb-3">
<span class="badge text-bg-light border"><?= htmlspecialchars($route['city']) ?></span>
<span class="badge text-bg-light border"><?= htmlspecialchars($route['difficulty']) ?></span>
<span class="badge text-bg-light border"><?= htmlspecialchars($route['neighborhood']) ?></span>
</div>
<h3 class="route-title mb-2"><a href="route.php?id=<?= (int)$route['id'] ?>"><?= htmlspecialchars($route['title']) ?></a></h3>
<p class="text-muted mb-3"><?= htmlspecialchars($route['summary']) ?></p>
<div class="route-meta mb-3">
<span><?= htmlspecialchars(number_format((float)$route['distance_km'], 1)) ?> km</span>
<span><?= htmlspecialchars(number_format((float)$route['duration_hours'], 1)) ?> hr</span>
<span>Start: <?= htmlspecialchars($route['start_point']) ?></span>
</div>
<?php if ($highlights): ?>
<ul class="highlights-list mb-0">
<?php foreach ($highlights as $item): ?>
<li><?= htmlspecialchars($item) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<div class="route-actions d-flex flex-lg-column justify-content-between gap-2">
<a class="btn btn-dark" href="route.php?id=<?= (int)$route['id'] ?>">View details</a>
<a class="btn btn-outline-secondary" href="city.php?city=<?= rawurlencode((string)$route['city']) ?>">City guide</a>
<a class="btn btn-outline-secondary" href="<?= htmlspecialchars($route['map_url']) ?>" target="_blank" rel="noopener">Open map</a>
</div>
</div>
</article>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
</section>
<section class="section-shell" id="cities">
<div class="container-lg px-3 px-lg-4 py-4 py-lg-5">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-end gap-3 mb-3">
<div>
<span class="eyebrow">Cities</span>
<h2 class="section-title mb-1 mt-2">Start from a city, then narrow it down.</h2>
<p class="text-muted mb-0">Fast jumping-off points for the places already in the directory.</p>
</div>
</div>
<div class="row g-3">
<?php foreach ($cities as $cityRow): ?>
<div class="col-6 col-md-4 col-xl-2">
<a class="city-card panel-card h-100 d-block text-decoration-none" href="city.php?city=<?= rawurlencode((string)$cityRow['city']) ?>">
<span class="city-name"><?= htmlspecialchars($cityRow['city']) ?></span>
<span class="city-count"><?= (int)$cityRow['route_count'] ?> route<?= (int)$cityRow['route_count'] === 1 ? '' : 's' ?></span>
</a>
</div>
<?php endforeach; ?>
</div>
</div>
</section>
</main>
<?php urban_hikes_render_footer(); ?>