Favoritenseite jetzt dynamisch

This commit is contained in:
2026-04-10 19:55:48 +02:00
parent 4eff1f02a3
commit 82c7b73f77
3 changed files with 565 additions and 37 deletions
+62
View File
@@ -1317,6 +1317,68 @@ footer a:hover { color: var(--gold); }
cursor: wait;
}
.favorites-search-form {
margin-top: 0.9rem;
}
.favorites-search-results {
margin-top: 1rem;
border: 1px solid rgba(79, 195, 216, 0.18);
border-radius: calc(var(--radius) + 2px);
background:
linear-gradient(180deg, rgba(79, 195, 216, 0.08), rgba(255, 255, 255, 0.02)),
rgba(7, 16, 27, 0.42);
overflow: hidden;
}
.favorites-search-results.has-results {
box-shadow: 0 18px 45px rgba(0, 0, 0, 0.18);
}
.favorites-search-summary,
.favorites-search-empty,
.favorites-search-results > .mt-xl {
padding: 1rem 1.1rem;
}
.favorites-search-summary {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 0.8rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.favorites-search-summary h2,
.favorites-search-results > .mt-xl h2,
.favorites-search-empty p {
margin: 0;
}
.favorites-search-empty {
display: grid;
gap: 0.45rem;
}
.favorites-search-table-wrap {
padding: 0.3rem 0.7rem 0.85rem;
}
.favorites-search-results .data-table {
margin: 0;
}
@media (max-width: 720px) {
.favorites-search-summary {
flex-direction: column;
align-items: flex-start;
}
.favorites-search-table-wrap {
padding: 0.2rem 0.4rem 0.6rem;
}
}
.admin-users-layout {
display: flex;
flex-direction: column;
+1
View File
File diff suppressed because one or more lines are too long
+494 -29
View File
@@ -40,17 +40,375 @@ $pageTitle = 'Favoriten - AstroTools';
$headerIntroPre = 'Merkliste';
$headerIntroTitle = 'Favoriten';
$headerIntroSub = 'Satelliten und Kleinplaneten zentral verwalten.';
$htmxTriggers = [];
function h(?string $value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
function isHtmxRequest(): bool
{
return strtolower((string) ($_SERVER['HTTP_HX_REQUEST'] ?? '')) === 'true';
}
function renderMessages(string $successMessage, array $errors, bool $oob = false): string
{
ob_start();
?>
<div id="favorites-messages"<?= $oob ? ' hx-swap-oob="outerHTML"' : '' ?>>
<?php if ($successMessage !== ''): ?>
<p class="msg-success"><?= h($successMessage) ?></p>
<?php endif; ?>
<?php if (!empty($errors)): ?>
<ul class="msg-error">
<?php foreach ($errors as $error): ?>
<li><?= h($error) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<?php
return (string) ob_get_clean();
}
function renderLocationsSection(array $locations, bool $oob = false): string
{
ob_start();
?>
<div class="card" id="locations-section"<?= $oob ? ' hx-swap-oob="outerHTML"' : '' ?>>
<h2>Standorte</h2>
<?php if (empty($locations)): ?>
<p class="hint">Es sind noch keine Standorte gespeichert.</p>
<?php else: ?>
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>Breitengrad</th>
<th>Laengengrad</th>
<th>Hoehe</th>
<th>Zeitzone</th>
<th>Standard</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($locations as $location): ?>
<tr>
<td><?= h($location['name']) ?></td>
<td><?= h((string) $location['latitude']) ?></td>
<td><?= h((string) $location['longitude']) ?></td>
<td><?= h($location['elevation'] !== null ? (string) $location['elevation'] . ' m' : '-') ?></td>
<td><?= h($location['timezone']) ?></td>
<td><?= (int) $location['is_default'] === 1 ? '*' : '-' ?></td>
<td class="action-cell">
<button
type="button"
class="btn btn-sm btn-primary js-edit-location"
data-location-id="<?= (int) $location['id'] ?>"
data-location-name="<?= h((string) $location['name']) ?>"
data-location-latitude="<?= h((string) $location['latitude']) ?>"
data-location-longitude="<?= h((string) $location['longitude']) ?>"
data-location-elevation="<?= h($location['elevation'] !== null ? (string) $location['elevation'] : '') ?>"
data-location-timezone="<?= h((string) $location['timezone']) ?>"
data-location-default="<?= (int) $location['is_default'] === 1 ? '1' : '0' ?>"
>Bearbeiten</button>
<?php if ((int) $location['is_default'] !== 1): ?>
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Standard-Standort wird gesetzt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="set_default_location">
<input type="hidden" name="location_id" value="<?= (int) $location['id'] ?>">
<button type="submit" class="btn btn-sm btn-secondary">Als Standard</button>
</form>
<?php endif; ?>
<?php if (count($locations) > 1): ?>
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Standort wird entfernt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML" onsubmit="return confirm('Standort wirklich loeschen?');">
<input type="hidden" name="action" value="delete_location">
<input type="hidden" name="location_id" value="<?= (int) $location['id'] ?>">
<button type="submit" class="btn btn-sm btn-danger">Loeschen</button>
</form>
<?php else: ?>
<span class="hint">Letzter Standort</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php
return (string) ob_get_clean();
}
function renderSatelliteSearchResults(string $satelliteSearchQuery, array $satelliteSearchResults, bool $oob = false): string
{
ob_start();
?>
<div
id="satellite-search-results"
class="favorites-search-results<?= $satelliteSearchQuery !== '' ? ' has-results' : '' ?>"
aria-live="polite"
<?= $oob ? ' hx-swap-oob="outerHTML"' : '' ?>
>
<?php if ($satelliteSearchQuery === ''): ?>
<div class="favorites-search-empty">
<p>Noch keine Suche gestartet.</p>
<p class="hint">Suche nach Namen oder NORAD-ID, um passende Satelliten direkt hier einzublenden.</p>
</div>
<?php else: ?>
<div class="favorites-search-summary">
<h2>Suchergebnisse</h2>
<p class="hint">
<?= count($satelliteSearchResults) ?> Treffer fuer <strong><?= h($satelliteSearchQuery) ?></strong>
</p>
</div>
<?php if (empty($satelliteSearchResults)): ?>
<div class="favorites-search-empty">
<p>Keine passenden Satelliten gefunden.</p>
<p class="hint">Probiere einen Namen wie <strong>ISS</strong> oder eine NORAD-ID wie <strong>25544</strong>.</p>
</div>
<?php else: ?>
<div class="favorites-search-table-wrap">
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>NORAD</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($satelliteSearchResults as $satellite): ?>
<tr>
<td><?= h($satellite['object_name']) ?></td>
<td><?= h((string) $satellite['norad_cat_id']) ?></td>
<td>
<?php if ((int) $satellite['already_added'] === 1): ?>
<span class="hint">Bereits hinzugefuegt</span>
<?php else: ?>
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Satellit wird hinzugefuegt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="add_satellite">
<input type="hidden" name="satellite_id" value="<?= (int) $satellite['id'] ?>">
<button type="submit" class="btn btn-sm btn-primary">Hinzufuegen</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<?php
return (string) ob_get_clean();
}
function renderMinorplanetSearchResults(string $minorplanetSearchQuery, array $minorplanetSearchResults, bool $oob = false): string
{
ob_start();
?>
<div
id="minorplanet-search-results"
class="favorites-search-results<?= $minorplanetSearchQuery !== '' ? ' has-results' : '' ?>"
aria-live="polite"
<?= $oob ? ' hx-swap-oob="outerHTML"' : '' ?>
>
<?php if ($minorplanetSearchQuery === ''): ?>
<div class="favorites-search-empty">
<p>Noch keine Suche gestartet.</p>
<p class="hint">Suche nach Nummer oder Bezeichnung, um passende Kleinplaneten direkt hier einzublenden.</p>
</div>
<?php else: ?>
<div class="favorites-search-summary">
<h2>Suchergebnisse</h2>
<p class="hint">
<?= count($minorplanetSearchResults) ?> Treffer fuer <strong><?= h($minorplanetSearchQuery) ?></strong>
</p>
</div>
<?php if (empty($minorplanetSearchResults)): ?>
<div class="favorites-search-empty">
<p>Keine passenden Kleinplaneten gefunden.</p>
<p class="hint">Probiere eine Nummer wie <strong>1</strong> oder eine Bezeichnung wie <strong>Ceres</strong>.</p>
</div>
<?php else: ?>
<div class="favorites-search-table-wrap">
<table class="data-table">
<thead>
<tr>
<th>Bezeichnung</th>
<th>Nummer</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($minorplanetSearchResults as $minorplanet): ?>
<tr>
<td><?= h($minorplanet['designation_text'] ?: $minorplanet['desig_packed']) ?></td>
<td><?= h($minorplanet['mp_number'] !== null ? (string) $minorplanet['mp_number'] : '-') ?></td>
<td>
<?php if ((int) $minorplanet['already_added'] === 1): ?>
<span class="hint">Bereits hinzugefuegt</span>
<?php else: ?>
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Kleinplanet wird hinzugefuegt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="add_minorplanet">
<input type="hidden" name="minorplanet_id" value="<?= (int) $minorplanet['id'] ?>">
<button type="submit" class="btn btn-sm btn-primary">Hinzufuegen</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<?php
return (string) ob_get_clean();
}
function renderTrackedSatellitesSection(array $trackedSatellites, bool $oob = false): string
{
ob_start();
?>
<div class="card" id="tracked-satellites-section"<?= $oob ? ' hx-swap-oob="outerHTML"' : '' ?>>
<h2>Meine Satelliten</h2>
<?php if (empty($trackedSatellites)): ?>
<p class="hint">Du hast noch keine Satelliten ausgewaehlt.</p>
<?php else: ?>
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>NORAD</th>
<th>Favorit</th>
<th>Hinzugefuegt</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($trackedSatellites as $satellite): ?>
<tr>
<td><?= h($satellite['object_name']) ?></td>
<td><?= h((string) $satellite['norad_cat_id']) ?></td>
<td><?= (int) $satellite['is_favorite'] === 1 ? '*' : '-' ?></td>
<td><?= h($satellite['created_at']) ?></td>
<td class="action-cell">
<?php if (count($trackedSatellites) <= 1 && (int) $satellite['is_favorite'] === 1): ?>
<span class="hint">Favorit bleibt gesetzt</span>
<?php else: ?>
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Satelliten-Favorit wird aktualisiert..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="toggle_satellite_favorite">
<input type="hidden" name="satellite_id" value="<?= (int) $satellite['id'] ?>">
<button type="submit" class="btn btn-sm btn-secondary">
<?= (int) $satellite['is_favorite'] === 1 ? 'Favorit entfernen' : 'Als Favorit' ?>
</button>
</form>
<?php endif; ?>
<?php if (count($trackedSatellites) > 1): ?>
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Satellit wird entfernt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML" onsubmit="return confirm('Satellit wirklich entfernen?');">
<input type="hidden" name="action" value="remove_satellite">
<input type="hidden" name="satellite_id" value="<?= (int) $satellite['id'] ?>">
<button type="submit" class="btn btn-sm btn-danger">Entfernen</button>
</form>
<?php else: ?>
<span class="hint">Letzter Satellit</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php
return (string) ob_get_clean();
}
function renderTrackedMinorplanetsSection(array $trackedMinorplanets, bool $oob = false): string
{
ob_start();
?>
<div class="card" id="tracked-minorplanets-section"<?= $oob ? ' hx-swap-oob="outerHTML"' : '' ?>>
<h2>Meine Kleinplaneten</h2>
<?php if (empty($trackedMinorplanets)): ?>
<p class="hint">Du hast noch keine Kleinplaneten ausgewaehlt.</p>
<?php else: ?>
<table class="data-table">
<thead>
<tr>
<th>Bezeichnung</th>
<th>Nummer</th>
<th>Favorit</th>
<th>Hinzugefuegt</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($trackedMinorplanets as $minorplanet): ?>
<tr>
<td><?= h($minorplanet['designation_text'] ?: $minorplanet['desig_packed']) ?></td>
<td><?= h($minorplanet['mp_number'] !== null ? (string) $minorplanet['mp_number'] : '-') ?></td>
<td><?= (int) $minorplanet['is_favorite'] === 1 ? '*' : '-' ?></td>
<td><?= h($minorplanet['created_at']) ?></td>
<td class="action-cell">
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Kleinplaneten-Favorit wird aktualisiert..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="toggle_minorplanet_favorite">
<input type="hidden" name="minorplanet_id" value="<?= (int) $minorplanet['id'] ?>">
<button type="submit" class="btn btn-sm btn-secondary">
<?= (int) $minorplanet['is_favorite'] === 1 ? 'Favorit entfernen' : 'Als Favorit' ?>
</button>
</form>
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Kleinplanet wird entfernt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML" onsubmit="return confirm('Kleinplanet wirklich entfernen?');">
<input type="hidden" name="action" value="remove_minorplanet">
<input type="hidden" name="minorplanet_id" value="<?= (int) $minorplanet['id'] ?>">
<button type="submit" class="btn btn-sm btn-danger">Entfernen</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php
return (string) ob_get_clean();
}
function validTimezone(string $timezone): bool
{
return in_array($timezone, timezone_identifiers_list(), true);
}
if (isHtmxRequest()) {
$hxCurrentUrl = trim((string) ($_SERVER['HTTP_HX_CURRENT_URL'] ?? ''));
if ($hxCurrentUrl !== '') {
$queryString = (string) parse_url($hxCurrentUrl, PHP_URL_QUERY);
if ($queryString !== '') {
parse_str($queryString, $hxQueryParams);
if ($satelliteSearchQuery === '' && isset($hxQueryParams['sat_q'])) {
$satelliteSearchQuery = trim((string) $hxQueryParams['sat_q']);
}
if ($minorplanetSearchQuery === '' && isset($hxQueryParams['mp_q'])) {
$minorplanetSearchQuery = trim((string) $hxQueryParams['mp_q']);
}
}
}
}
function setExclusiveFavorite(PDO $pdo, string $tableName, string $idColumn, int $userId, int $entityId): bool
{
$pdo->beginTransaction();
@@ -611,6 +969,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo->commit();
$successMessage = 'Der Standort wurde aktualisiert.';
$htmxTriggers['favorites:location-updated'] = true;
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
@@ -949,6 +1308,32 @@ $stmtTrackedMinorplanets->execute([
]);
$trackedMinorplanets = $stmtTrackedMinorplanets->fetchAll();
if (isHtmxRequest()) {
if ($_SERVER['REQUEST_METHOD'] === 'GET' && array_key_exists('sat_q', $_GET)) {
echo renderSatelliteSearchResults($satelliteSearchQuery, $satelliteSearchResults);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'GET' && array_key_exists('mp_q', $_GET)) {
echo renderMinorplanetSearchResults($minorplanetSearchQuery, $minorplanetSearchResults);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($htmxTriggers !== []) {
header('HX-Trigger: ' . (string) json_encode($htmxTriggers));
}
echo renderMessages($successMessage, $errors);
echo renderLocationsSection($locations, true);
echo renderSatelliteSearchResults($satelliteSearchQuery, $satelliteSearchResults, true);
echo renderMinorplanetSearchResults($minorplanetSearchQuery, $minorplanetSearchResults, true);
echo renderTrackedSatellitesSection($trackedSatellites, true);
echo renderTrackedMinorplanetsSection($trackedMinorplanets, true);
exit;
}
}
$loggedIn = true;
?>
<?php require __DIR__ . '/header.php'; ?>
@@ -960,20 +1345,10 @@ $loggedIn = true;
crossorigin=""
>
<?php if ($successMessage !== ''): ?>
<p class="msg-success"><?= h($successMessage) ?></p>
<?php endif; ?>
<?php if (!empty($errors)): ?>
<ul class="msg-error">
<?php foreach ($errors as $error): ?>
<li><?= h($error) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?= renderMessages($successMessage, $errors) ?>
<div class="grid-2">
<div class="card">
<div class="card" id="locations-section">
<h2>Standorte</h2>
<?php if (empty($locations)): ?>
<p class="hint">Es sind noch keine Standorte gespeichert.</p>
@@ -1012,14 +1387,14 @@ $loggedIn = true;
data-location-default="<?= (int) $location['is_default'] === 1 ? '1' : '0' ?>"
>Bearbeiten</button>
<?php if ((int) $location['is_default'] !== 1): ?>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Standard-Standort wird gesetzt...">
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Standard-Standort wird gesetzt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="set_default_location">
<input type="hidden" name="location_id" value="<?= (int) $location['id'] ?>">
<button type="submit" class="btn btn-sm btn-secondary">Als Standard</button>
</form>
<?php endif; ?>
<?php if (count($locations) > 1): ?>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Standort wird entfernt..." onsubmit="return confirm('Standort wirklich löschen?');">
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Standort wird entfernt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML" onsubmit="return confirm('Standort wirklich löschen?');">
<input type="hidden" name="action" value="delete_location">
<input type="hidden" name="location_id" value="<?= (int) $location['id'] ?>">
<button type="submit" class="btn btn-sm btn-danger">Löschen</button>
@@ -1048,7 +1423,7 @@ $loggedIn = true;
<div id="map" class="map"></div>
<form method="post" action="" id="add-location-form" class="form-stack mt-xl favorites-loading-form" data-loading-message="Standort wird gespeichert...">
<form method="post" action="" id="add-location-form" class="form-stack mt-xl favorites-loading-form favorites-loading-form-htmx" data-loading-message="Standort wird gespeichert..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="add_location">
<div class="grid-2 gap-sm">
<div class="form-group">
@@ -1096,7 +1471,7 @@ $loggedIn = true;
</div>
<dialog class="admin-dialog" id="editLocationDialog">
<form method="post" action="" class="admin-dialog-card form-stack favorites-loading-form" data-loading-message="Standort wird aktualisiert...">
<form method="post" action="" class="admin-dialog-card form-stack favorites-loading-form favorites-loading-form-htmx" data-loading-message="Standort wird aktualisiert..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="update_location">
<input type="hidden" name="location_id" id="edit-location-id" value="">
<h3>Standort bearbeiten</h3>
@@ -1137,7 +1512,7 @@ $loggedIn = true;
<div class="grid-2">
<div class="card">
<h2>Satelliten suchen</h2>
<form method="get" action="" class="favorites-loading-form" data-loading-message="Satelliten werden gesucht. Das kann kurz dauern...">
<form method="get" action="" class="favorites-loading-form favorites-loading-form-htmx favorites-search-form" data-loading-message="Satelliten werden gesucht. Das kann kurz dauern..." hx-get="my_favorites.php" hx-target="#satellite-search-results" hx-swap="outerHTML" hx-push-url="true">
<div class="input-row">
<input
type="text"
@@ -1151,6 +1526,7 @@ $loggedIn = true;
<p class="favorites-loading-hint" data-loading-hint hidden>Satelliten werden gesucht. Das kann kurz dauern...</p>
</form>
<div id="satellite-search-results" class="favorites-search-results<?= $satelliteSearchQuery !== '' ? ' has-results' : '' ?>" aria-live="polite">
<?php if ($satelliteSearchQuery !== ''): ?>
<div class="mt-xl">
<h2>Suchergebnisse</h2>
@@ -1174,7 +1550,7 @@ $loggedIn = true;
<?php if ((int) $satellite['already_added'] === 1): ?>
<span class="hint">Bereits hinzugefuegt</span>
<?php else: ?>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Satellit wird hinzugefuegt...">
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Satellit wird hinzugefuegt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="add_satellite">
<input type="hidden" name="satellite_id" value="<?= (int) $satellite['id'] ?>">
<button type="submit" class="btn btn-sm btn-primary">Hinzufuegen</button>
@@ -1187,12 +1563,28 @@ $loggedIn = true;
</table>
<?php endif; ?>
</div>
<?php else: ?>
<div class="favorites-search-empty">
<p>Noch keine Suche gestartet.</p>
<p class="hint">Suche nach Namen oder NORAD-ID, um passende Satelliten direkt hier einzublenden.</p>
</div>
<?php endif; ?>
</div>
</div>
<div class="card">
<h2>Kleinplaneten suchen</h2>
<form method="get" action="" class="favorites-loading-form" data-loading-message="Kleinplaneten werden gesucht. Das kann etwas dauern...">
<p class="hint">Die Treffer werden direkt in der Karte aktualisiert, ohne die ganze Seite neu zu laden.</p>
<form
method="get"
action=""
class="favorites-loading-form favorites-loading-form-htmx favorites-search-form"
data-loading-message="Kleinplaneten werden gesucht. Das kann etwas dauern..."
hx-get="my_favorites.php"
hx-target="#minorplanet-search-results"
hx-swap="outerHTML"
hx-push-url="true"
>
<div class="input-row">
<input
type="text"
@@ -1206,6 +1598,7 @@ $loggedIn = true;
<p class="favorites-loading-hint" data-loading-hint hidden>Kleinplaneten werden gesucht. Das kann etwas dauern...</p>
</form>
<div id="minorplanet-search-results" class="favorites-search-results<?= $minorplanetSearchQuery !== '' ? ' has-results' : '' ?>" aria-live="polite">
<?php if ($minorplanetSearchQuery !== ''): ?>
<div class="mt-xl">
<h2>Suchergebnisse</h2>
@@ -1229,7 +1622,7 @@ $loggedIn = true;
<?php if ((int) $minorplanet['already_added'] === 1): ?>
<span class="hint">Bereits hinzugefuegt</span>
<?php else: ?>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Kleinplanet wird hinzugefuegt...">
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Kleinplanet wird hinzugefuegt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="add_minorplanet">
<input type="hidden" name="minorplanet_id" value="<?= (int) $minorplanet['id'] ?>">
<button type="submit" class="btn btn-sm btn-primary">Hinzufuegen</button>
@@ -1242,12 +1635,18 @@ $loggedIn = true;
</table>
<?php endif; ?>
</div>
<?php else: ?>
<div class="favorites-search-empty">
<p>Noch keine Suche gestartet.</p>
<p class="hint">Suche nach Nummer oder Bezeichnung, um passende Kleinplaneten direkt hier einzublenden.</p>
</div>
<?php endif; ?>
</div>
</div>
</div>
<div class="grid-2">
<div class="card">
<div class="card" id="tracked-satellites-section">
<h2>Meine Satelliten</h2>
<?php if (empty($trackedSatellites)): ?>
<p class="hint">Du hast noch keine Satelliten ausgewählt.</p>
@@ -1273,7 +1672,7 @@ $loggedIn = true;
<?php if (count($trackedSatellites) <= 1 && (int) $satellite['is_favorite'] === 1): ?>
<span class="hint">Favorit bleibt gesetzt</span>
<?php else: ?>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Satelliten-Favorit wird aktualisiert...">
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Satelliten-Favorit wird aktualisiert..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="toggle_satellite_favorite">
<input type="hidden" name="satellite_id" value="<?= (int) $satellite['id'] ?>">
<button type="submit" class="btn btn-sm btn-secondary">
@@ -1282,7 +1681,7 @@ $loggedIn = true;
</form>
<?php endif; ?>
<?php if (count($trackedSatellites) > 1): ?>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Satellit wird entfernt..." onsubmit="return confirm('Satellit wirklich entfernen?');">
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Satellit wird entfernt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML" onsubmit="return confirm('Satellit wirklich entfernen?');">
<input type="hidden" name="action" value="remove_satellite">
<input type="hidden" name="satellite_id" value="<?= (int) $satellite['id'] ?>">
<button type="submit" class="btn btn-sm btn-danger">Entfernen</button>
@@ -1298,7 +1697,7 @@ $loggedIn = true;
<?php endif; ?>
</div>
<div class="card">
<div class="card" id="tracked-minorplanets-section">
<h2>Meine Kleinplaneten</h2>
<?php if (empty($trackedMinorplanets)): ?>
<p class="hint">Du hast noch keine Kleinplaneten ausgewählt.</p>
@@ -1321,14 +1720,14 @@ $loggedIn = true;
<td><?= (int) $minorplanet['is_favorite'] === 1 ? '*' : '-' ?></td>
<td><?= h($minorplanet['created_at']) ?></td>
<td class="action-cell">
<form method="post" action="" class="favorites-loading-form" data-loading-message="Kleinplaneten-Favorit wird aktualisiert...">
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Kleinplaneten-Favorit wird aktualisiert..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML">
<input type="hidden" name="action" value="toggle_minorplanet_favorite">
<input type="hidden" name="minorplanet_id" value="<?= (int) $minorplanet['id'] ?>">
<button type="submit" class="btn btn-sm btn-secondary">
<?= (int) $minorplanet['is_favorite'] === 1 ? 'Favorit entfernen' : 'Als Favorit' ?>
</button>
</form>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Kleinplanet wird entfernt..." onsubmit="return confirm('Kleinplanet wirklich entfernen?');">
<form method="post" action="" class="favorites-loading-form favorites-loading-form-htmx" data-loading-message="Kleinplanet wird entfernt..." hx-post="my_favorites.php" hx-target="#favorites-messages" hx-swap="outerHTML" onsubmit="return confirm('Kleinplanet wirklich entfernen?');">
<input type="hidden" name="action" value="remove_minorplanet">
<input type="hidden" name="minorplanet_id" value="<?= (int) $minorplanet['id'] ?>">
<button type="submit" class="btn btn-sm btn-danger">Entfernen</button>
@@ -1342,6 +1741,7 @@ $loggedIn = true;
</div>
</div>
<script src="js/htmx.min.js"></script>
<script
src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
@@ -1526,6 +1926,10 @@ $loggedIn = true;
document.querySelectorAll('.favorites-loading-form').forEach((form) => {
form.addEventListener('submit', (event) => {
if (form.classList.contains('favorites-loading-form-htmx')) {
return;
}
if (form.id === 'add-location-form') {
const nameField = form.querySelector('#name');
const nameError = document.getElementById('add-location-name-error');
@@ -1578,6 +1982,60 @@ document.querySelectorAll('.favorites-loading-form').forEach((form) => {
});
});
(function () {
function setLoadingState(form) {
if (!form) {
return;
}
const message = form.dataset.loadingMessage || 'Bitte warten...';
let hint = form.querySelector('[data-loading-hint]');
if (!hint) {
hint = document.createElement('p');
hint.className = 'favorites-loading-hint';
hint.dataset.loadingHint = 'true';
form.appendChild(hint);
}
hint.textContent = message;
hint.hidden = false;
form.classList.add('is-loading');
form.querySelectorAll('button').forEach((button) => {
button.disabled = true;
button.setAttribute('aria-busy', 'true');
});
}
function clearLoadingState(form) {
if (!form) {
return;
}
form.classList.remove('is-loading');
form.querySelectorAll('button').forEach((button) => {
button.disabled = false;
button.removeAttribute('aria-busy');
});
}
document.body.addEventListener('htmx:beforeRequest', (event) => {
const form = event.target.closest('.favorites-loading-form-htmx');
setLoadingState(form);
});
document.body.addEventListener('htmx:afterRequest', (event) => {
const form = event.target.closest('.favorites-loading-form-htmx');
clearLoadingState(form);
});
document.body.addEventListener('htmx:responseError', (event) => {
const form = event.target.closest('.favorites-loading-form-htmx');
clearLoadingState(form);
});
})();
(function () {
const dialog = document.getElementById('editLocationDialog');
const cancelButton = document.getElementById('editLocationCancel');
@@ -1594,8 +2052,12 @@ document.querySelectorAll('.favorites-loading-form').forEach((form) => {
const timezoneInput = document.getElementById('edit-location-timezone');
const defaultInput = document.getElementById('edit-location-default');
document.querySelectorAll('.js-edit-location').forEach((button) => {
button.addEventListener('click', () => {
document.addEventListener('click', (event) => {
const button = event.target.closest('.js-edit-location');
if (!button) {
return;
}
idInput.value = button.dataset.locationId || '';
nameInput.value = button.dataset.locationName || '';
latitudeInput.value = button.dataset.locationLatitude || '';
@@ -1605,11 +2067,14 @@ document.querySelectorAll('.favorites-loading-form').forEach((form) => {
defaultInput.checked = button.dataset.locationDefault === '1';
dialog.showModal();
});
});
cancelButton?.addEventListener('click', () => {
dialog.close();
});
document.body.addEventListener('favorites:location-updated', () => {
dialog.close();
});
})();
</script>