39656-vm/admin.php
2026-04-15 16:00:15 +00:00

188 lines
11 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/urban_hikes.php';
$storage = urban_hikes_storage();
$errors = [];
$formData = [
'city' => '',
'title' => '',
'summary' => '',
'distance_km' => '',
'duration_hours' => '',
'difficulty' => 'Moderate',
'neighborhood' => '',
'start_point' => '',
'highlights' => '',
'map_url' => '',
'best_for' => '',
];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$result = urban_hikes_create($_POST);
if (!empty($result['success'])) {
urban_hikes_set_flash('success', 'Route saved and published to the directory.');
header('Location: route.php?id=' . (int)$result['id']);
exit;
}
$errors = $result['errors'] ?? [];
$formData = array_merge($formData, $result['input'] ?? []);
}
$latestRoutes = urban_hikes_latest();
$pageTitle = 'Add a route | ' . urban_hikes_project_name();
$pageDescription = 'Add an urban hiking route with city, difficulty, distance, highlights, and map link.';
urban_hikes_render_head($pageTitle, $pageDescription, 'noindex, follow');
urban_hikes_render_nav('admin');
?>
<main>
<section class="section-shell border-bottom">
<div class="container-lg px-3 px-lg-4 py-4 py-lg-5">
<div class="row g-4 align-items-start">
<div class="col-lg-7">
<div class="panel-card">
<span class="eyebrow">Content admin</span>
<h1 class="section-title mt-2 mb-2">Add a new urban route</h1>
<p class="text-muted mb-4">This first admin screen keeps the workflow small: add a route once, then browse it in the public directory immediately.</p>
<?php if (!$storage['ready']): ?>
<div class="alert alert-warning" role="alert">
Database saving is unavailable right now, so new routes cannot be published yet.
</div>
<?php endif; ?>
<?php if (isset($errors['storage'])): ?>
<div class="alert alert-warning" role="alert"><?= htmlspecialchars($errors['storage']) ?></div>
<?php endif; ?>
<form method="post" action="admin.php" class="row g-3" novalidate>
<div class="col-md-6">
<label class="form-label" for="city">City</label>
<input class="form-control <?= isset($errors['city']) ? 'is-invalid' : '' ?>" type="text" id="city" name="city" value="<?= htmlspecialchars($formData['city']) ?>" placeholder="Berlin" required />
<?php if (isset($errors['city'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['city']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label" for="difficulty">Difficulty</label>
<select class="form-select <?= isset($errors['difficulty']) ? 'is-invalid' : '' ?>" id="difficulty" name="difficulty">
<?php foreach (['Easy', 'Moderate', 'Challenging'] as $level): ?>
<option value="<?= htmlspecialchars($level) ?>" <?= $formData['difficulty'] === $level ? 'selected' : '' ?>><?= htmlspecialchars($level) ?></option>
<?php endforeach; ?>
</select>
<?php if (isset($errors['difficulty'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['difficulty']) ?></div><?php endif; ?>
</div>
<div class="col-12">
<label class="form-label" for="title">Route title</label>
<input class="form-control <?= isset($errors['title']) ? 'is-invalid' : '' ?>" type="text" id="title" name="title" value="<?= htmlspecialchars($formData['title']) ?>" placeholder="Hilltop parks to market streets walk" required />
<?php if (isset($errors['title'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['title']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label" for="distance_km">Distance (km)</label>
<input class="form-control <?= isset($errors['distance_km']) ? 'is-invalid' : '' ?>" type="number" min="0.1" step="0.1" id="distance_km" name="distance_km" value="<?= htmlspecialchars($formData['distance_km']) ?>" required />
<?php if (isset($errors['distance_km'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['distance_km']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label" for="duration_hours">Duration (hours)</label>
<input class="form-control <?= isset($errors['duration_hours']) ? 'is-invalid' : '' ?>" type="number" min="0.1" step="0.1" id="duration_hours" name="duration_hours" value="<?= htmlspecialchars($formData['duration_hours']) ?>" required />
<?php if (isset($errors['duration_hours'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['duration_hours']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label" for="neighborhood">Neighborhood / area</label>
<input class="form-control <?= isset($errors['neighborhood']) ? 'is-invalid' : '' ?>" type="text" id="neighborhood" name="neighborhood" value="<?= htmlspecialchars($formData['neighborhood']) ?>" placeholder="Waterfront district" required />
<?php if (isset($errors['neighborhood'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['neighborhood']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label" for="start_point">Start point</label>
<input class="form-control <?= isset($errors['start_point']) ? 'is-invalid' : '' ?>" type="text" id="start_point" name="start_point" value="<?= htmlspecialchars($formData['start_point']) ?>" placeholder="Central Station" required />
<?php if (isset($errors['start_point'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['start_point']) ?></div><?php endif; ?>
</div>
<div class="col-12">
<label class="form-label d-flex justify-content-between align-items-center" for="summary">
<span>Summary</span>
<span class="field-count" id="summaryCount">0 chars</span>
</label>
<textarea class="form-control <?= isset($errors['summary']) ? 'is-invalid' : '' ?>" id="summary" name="summary" rows="3" placeholder="What makes this route worth a half day?" data-count-target="summaryCount"><?= htmlspecialchars($formData['summary']) ?></textarea>
<?php if (isset($errors['summary'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['summary']) ?></div><?php endif; ?>
</div>
<div class="col-12">
<label class="form-label d-flex justify-content-between align-items-center" for="highlights">
<span>Highlights</span>
<span class="field-count" id="highlightsCount">0 chars</span>
</label>
<textarea class="form-control <?= isset($errors['highlights']) ? 'is-invalid' : '' ?>" id="highlights" name="highlights" rows="4" placeholder="One highlight per line" data-count-target="highlightsCount"><?= htmlspecialchars($formData['highlights']) ?></textarea>
<div class="form-text">Enter one stop or viewpoint per line.</div>
<?php if (isset($errors['highlights'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['highlights']) ?></div><?php endif; ?>
</div>
<div class="col-md-7">
<label class="form-label" for="map_url">Map link</label>
<input class="form-control <?= isset($errors['map_url']) ? 'is-invalid' : '' ?>" type="url" id="map_url" name="map_url" value="<?= htmlspecialchars($formData['map_url']) ?>" placeholder="https://maps.google.com/..." required />
<?php if (isset($errors['map_url'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['map_url']) ?></div><?php endif; ?>
</div>
<div class="col-md-5">
<label class="form-label" for="best_for">Best for</label>
<input class="form-control <?= isset($errors['best_for']) ? 'is-invalid' : '' ?>" type="text" id="best_for" name="best_for" value="<?= htmlspecialchars($formData['best_for']) ?>" placeholder="Early morning walkers" required />
<?php if (isset($errors['best_for'])): ?><div class="invalid-feedback"><?= htmlspecialchars($errors['best_for']) ?></div><?php endif; ?>
</div>
<div class="col-12 d-flex gap-2 pt-2">
<button class="btn btn-dark" type="submit" <?= !$storage['ready'] ? 'disabled' : '' ?>>Publish route</button>
<a class="btn btn-outline-secondary" href="index.php#results">Back to directory</a>
</div>
</form>
</div>
</div>
<div class="col-lg-5">
<div class="panel-card mb-3">
<span class="eyebrow">Workflow</span>
<h2 class="section-title h5 mt-2">What this first slice covers</h2>
<ul class="compact-list mb-0 mt-3">
<li>Add a route with the key planning fields</li>
<li>Store it in MariaDB with prepared statements</li>
<li>Redirect to a detail page with a confirmation toast</li>
<li>See it in the public directory immediately</li>
</ul>
</div>
<div class="panel-card">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<span class="eyebrow">Latest entries</span>
<h2 class="section-title h5 mt-2 mb-0">Recent routes</h2>
</div>
<a class="small text-decoration-none" href="index.php#results">Open directory</a>
</div>
<?php if (!$latestRoutes): ?>
<p class="text-muted mb-0">No routes published yet.</p>
<?php else: ?>
<div class="table-responsive">
<table class="table table-sm align-middle admin-table mb-0">
<thead>
<tr>
<th>Route</th>
<th>City</th>
<th class="text-end">View</th>
</tr>
</thead>
<tbody>
<?php foreach ($latestRoutes as $route): ?>
<tr>
<td>
<div class="fw-semibold"><?= htmlspecialchars($route['title']) ?></div>
<div class="text-muted small"><?= htmlspecialchars($route['difficulty']) ?> · <?= htmlspecialchars(number_format((float)$route['distance_km'], 1)) ?> km</div>
</td>
<td><?= htmlspecialchars($route['city']) ?></td>
<td class="text-end"><a class="btn btn-sm btn-outline-secondary" href="route.php?id=<?= (int)$route['id'] ?>">Open</a></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</section>
</main>
<?php urban_hikes_render_footer(); ?>