250 lines
15 KiB
PHP
250 lines
15 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/urban_hikes.php';
|
|
|
|
$storage = urban_hikes_storage();
|
|
$routeId = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
|
$editingRoute = $routeId > 0 ? urban_hikes_find($routeId) : null;
|
|
$isEditMode = $editingRoute !== null;
|
|
$missingRoute = $routeId > 0 && !$editingRoute;
|
|
$errors = [];
|
|
$formData = [
|
|
'city' => '',
|
|
'title' => '',
|
|
'summary' => '',
|
|
'distance_km' => '',
|
|
'duration_hours' => '',
|
|
'difficulty' => 'Moderate',
|
|
'neighborhood' => '',
|
|
'start_point' => '',
|
|
'highlights' => '',
|
|
'map_url' => '',
|
|
'best_for' => '',
|
|
];
|
|
|
|
if ($editingRoute) {
|
|
$formData = array_merge($formData, $editingRoute);
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$postedId = isset($_POST['route_id']) ? (int)$_POST['route_id'] : 0;
|
|
if ($postedId > 0) {
|
|
$result = urban_hikes_update($postedId, $_POST);
|
|
$routeId = $postedId;
|
|
$isEditMode = true;
|
|
} else {
|
|
$result = urban_hikes_create($_POST);
|
|
$routeId = 0;
|
|
$isEditMode = false;
|
|
}
|
|
|
|
if (!empty($result['success'])) {
|
|
urban_hikes_set_flash('success', $postedId > 0 ? 'Route updated successfully.' : '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'] ?? []);
|
|
$editingRoute = $routeId > 0 ? urban_hikes_find($routeId) : null;
|
|
$missingRoute = $routeId > 0 && !$editingRoute;
|
|
}
|
|
|
|
if ($missingRoute) {
|
|
http_response_code(404);
|
|
}
|
|
|
|
$latestRoutes = urban_hikes_latest(10);
|
|
$pageTitle = ($isEditMode ? 'Edit route' : 'Add a route') . ' | ' . urban_hikes_project_name();
|
|
$pageDescription = $isEditMode
|
|
? 'Update an existing urban hiking route and republish the latest details.'
|
|
: 'Add an urban hiking route with city, difficulty, distance, highlights, and map link.';
|
|
$submitLabel = $isEditMode ? 'Save changes' : 'Publish route';
|
|
|
|
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">
|
|
<?php if ($missingRoute): ?>
|
|
<div class="panel-card empty-state text-center">
|
|
<span class="eyebrow">Route missing</span>
|
|
<h1 class="section-title mt-2 mb-2">That route could not be opened for editing.</h1>
|
|
<p class="text-muted mb-4">Try returning to the directory and opening a valid route first.</p>
|
|
<div class="d-flex flex-column flex-sm-row gap-2 justify-content-center">
|
|
<a class="btn btn-dark" href="index.php#results">Back to directory</a>
|
|
<a class="btn btn-outline-secondary" href="admin.php">Create a new route instead</a>
|
|
</div>
|
|
</div>
|
|
<?php else: ?>
|
|
<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"><?= $isEditMode ? 'Edit urban route' : 'Add a new urban route' ?></h1>
|
|
<p class="text-muted mb-4">
|
|
<?= $isEditMode
|
|
? 'Update the route details below. After saving, the public directory and route page will immediately show the latest version.'
|
|
: 'This 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 changes 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<?= $isEditMode ? '?id=' . (int)$routeId : '' ?>" class="row g-3" novalidate>
|
|
<?php if ($isEditMode): ?>
|
|
<input type="hidden" name="route_id" value="<?= (int)$routeId ?>" />
|
|
<?php endif; ?>
|
|
<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((string)$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) ?>" <?= (string)$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((string)$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((string)$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((string)$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((string)$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((string)$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((string)$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((string)$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((string)$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((string)$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' : '' ?>><?= htmlspecialchars($submitLabel) ?></button>
|
|
<?php if ($isEditMode): ?>
|
|
<a class="btn btn-outline-secondary" href="route.php?id=<?= (int)$routeId ?>">Cancel</a>
|
|
<?php else: ?>
|
|
<a class="btn btn-outline-secondary" href="index.php#results">Back to directory</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-5">
|
|
<div class="panel-card mb-3">
|
|
<span class="eyebrow"><?= $isEditMode ? 'Editing' : 'Workflow' ?></span>
|
|
<h2 class="section-title h5 mt-2"><?= $isEditMode ? 'What changes update immediately' : 'What this slice covers' ?></h2>
|
|
<ul class="compact-list mb-0 mt-3">
|
|
<?php if ($isEditMode): ?>
|
|
<li>Update route content without touching the database manually</li>
|
|
<li>Keep validation and safe prepared statements in one place</li>
|
|
<li>Return to the public route page after saving</li>
|
|
<li>Refresh the route everywhere it appears in the directory</li>
|
|
<?php else: ?>
|
|
<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>
|
|
<?php endif; ?>
|
|
</ul>
|
|
</div>
|
|
<div class="panel-card">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<div>
|
|
<span class="eyebrow">Manage routes</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">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($latestRoutes as $route): ?>
|
|
<tr>
|
|
<td>
|
|
<div class="fw-semibold"><?= htmlspecialchars((string)$route['title']) ?></div>
|
|
<div class="text-muted small"><?= htmlspecialchars((string)$route['difficulty']) ?> · <?= htmlspecialchars(number_format((float)$route['distance_km'], 1)) ?> km</div>
|
|
</td>
|
|
<td><a class="text-decoration-none" href="city.php?city=<?= rawurlencode((string)$route['city']) ?>"><?= htmlspecialchars((string)$route['city']) ?></a></td>
|
|
<td class="text-end">
|
|
<div class="d-inline-flex gap-2 flex-wrap justify-content-end">
|
|
<a class="btn btn-sm btn-outline-secondary" href="admin.php?id=<?= (int)$route['id'] ?>">Edit</a>
|
|
<a class="btn btn-sm btn-outline-secondary" href="route.php?id=<?= (int)$route['id'] ?>">Open</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
<?php urban_hikes_render_footer(); ?>
|