small tweaks

This commit is contained in:
2025-08-18 22:03:09 +02:00
parent fb182a1aec
commit 16c4d58def
7 changed files with 183 additions and 57 deletions

View File

@@ -194,21 +194,32 @@ body {
white-space: nowrap;
}
.btn-pill i { font-size: 1rem; line-height: 1; margin: 0 .5rem; }
.btn-pill i {
font-size: 1rem;
line-height: 1;
margin: 0 0.5rem;
}
.btn-pill span {
width: 0; opacity: 0; overflow: hidden; white-space: nowrap; margin-left: 0;
transition: width .18s ease, opacity .18s ease, margin-left .18s ease;
width: 0;
opacity: 0;
overflow: hidden;
white-space: nowrap;
margin-left: 0;
transition: width 0.18s ease, opacity 0.18s ease, margin-left 0.18s ease;
}
/* Hover: Button wächst, Text klappt auf */
.btn-pill:hover {
width: auto;
flex: 0 1 16rem;
padding: .5rem .8rem;
padding: 0.5rem 0.8rem;
/* Keine Ausrichtung hier setzen die Utility-Klasse bleibt wirksam */
}
.btn-pill:hover span { width: auto; opacity: 1; margin-left: .35rem; }
.btn-pill:hover span {
width: auto;
opacity: 1;
margin-left: 0.35rem;
}
/* Fokus sichtbar (zugänglich) */
.btn-pill:focus-visible {
@@ -216,6 +227,14 @@ body {
outline-offset: 2px;
}
/* ================= Footer ================== */
.icon-sm {
width: 24px; /* oder 24px, wenn du es etwas größer magst */
height: auto;
vertical-align: middle;
}
/* ============ Responsive Tweaks ============ */
@media (max-width: 575.98px) {
.card-body {

BIN
img/heart-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

BIN
img/paw-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
img/shakaru-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -270,12 +270,22 @@ function wishlistMainBuilder(int $ListID, string $sortby = 'priority'): void
// 3) Wünsche laden
$sql = "
SELECT w.ID, w.image, w.title, w.link, w.price, w.description,
w.date, w.qty,
(SELECT COUNT(*) FROM wishes_reservations r WHERE r.wish_id = w.ID) AS reserved_count
SELECT
w.ID,
w.image,
w.title,
w.link,
w.price,
w.description,
w.date,
w.qty,
COALESCE(rc.reserved_count, 0) AS reserved_count
FROM wishes w
LEFT JOIN v_wish_reserved_counts rc
ON rc.wish_id = w.ID
WHERE w.wishlist = ?
ORDER BY {$orderSql}";
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $ListID);
$stmt->execute();
@@ -311,7 +321,7 @@ HTML;
$row['link'] !== null ? (string) $row['link'] : null,
(int) $row['price'],
$row['description'] !== null ? (string) $row['description'] : null,
(int) $row['reserved_count'], // echte Anzahl Reservierungen
(int) $row['reserved_count'],
$row['date'] !== null ? (string) $row['date'] : null,
isset($row['qty']) ? (int) $row['qty'] : 1
);

123
index.php
View File

@@ -77,13 +77,24 @@ function require_csrf(): void
}
}
ensure_csrf_token();
// ===== URL-Param: Liste per UUID (oder Alt-ID -> Redirect) =====
$ListID = -1;
$ListUUID = '';
if (isset($_GET['list'])) {
$showCreateEmptyState = false;
if (!isset($_GET['list'])) {
// Kein Parameter => nur Empty-State ohne Message
$showCreateEmptyState = true;
} else {
$raw = trim((string) $_GET['list']);
if (preg_match('/^[0-9a-fA-F-]{32,36}$/', $raw)) {
if ($raw === '') {
// Leerer Parameter => nur Empty-State ohne Message
$showCreateEmptyState = true;
} elseif (preg_match('/^[0-9a-fA-F-]{32,36}$/', $raw)) {
// UUID übergeben
$c = db();
$s = $c->prepare('SELECT ID, uuid FROM lists WHERE uuid=?');
$s->bind_param('s', $raw);
@@ -92,27 +103,40 @@ if (isset($_GET['list'])) {
if ($r && ($row = $r->fetch_assoc())) {
$ListID = (int) $row['ID'];
$ListUUID = (string) $row['uuid'];
} else {
// UUID-Format ok, aber nicht vorhanden => Message + Empty-State
$message = ['msg' => 'Diese Liste gibt es nicht. Lege eine neue an oder prüfe den Link.', 'type' => 'warning'];
$showCreateEmptyState = true;
}
$s->close();
$c->close();
} else {
$ListID = (int) $raw;
if ($ListID >= 0) {
} elseif (preg_match('/^\d+$/', $raw)) {
// Numerische ID übergeben -> auf UUID umleiten, wenn vorhanden
$id = (int) $raw;
$c = db();
$s = $c->prepare('SELECT uuid FROM lists WHERE ID=?');
$s->bind_param('i', $ListID);
$s->bind_param('i', $id);
$s->execute();
$r = $s->get_result();
if ($r && ($row = $r->fetch_assoc())) {
$ListUUID = (string) $row['uuid'];
$uuid = (string) $row['uuid'];
$scheme = $secure ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? '';
header('Location: ' . $scheme . '://' . $host . '/?list=' . urlencode($ListUUID), true, 301);
header('Location: ' . $scheme . '://' . $host . '/?list=' . urlencode($uuid), true, 301);
exit;
} else {
// ID-Format ok, aber nicht vorhanden => Message + Empty-State
$message = ['msg' => 'Diese Liste gibt es nicht. Lege eine neue an oder prüfe den Link.', 'type' => 'warning'];
$showCreateEmptyState = true;
}
$s->close();
$c->close();
}
} else {
// Weder gültige UUID noch Zahl => Message + Empty-State
$message = ['msg' => 'Diese Liste gibt es nicht. Lege eine neue an oder prüfe den Link.', 'type' => 'warning'];
$showCreateEmptyState = true;
}
}
@@ -277,6 +301,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</a>
<div class="nav navbar-nav navbar-right">
<div class="d-grid gap-2 d-flex">
<button type="button" class="btn btn-outline-success my-2 my-sm-0" data-bs-toggle="modal"
data-bs-target="#createListModal">Neue Liste</button>
<?php if ($showCreateEmptyState == false): ?>
<?php if ($loggedin): ?>
<button type="button" class="btn btn-outline-secondary my-2 my-sm-0" data-bs-toggle="modal"
data-bs-target="#itemModal" data-mode="add" data-listuuid="<?= e($ListUUID) ?>"
@@ -300,6 +327,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<option <?= $sortby === 'random' ? 'selected' : '' ?> value="random">Zufall</option>
</select>
</form>
<?php endif; ?>
</div>
</div>
</div>
@@ -314,13 +342,46 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</div>
<?php endif; ?>
<?php if (!empty($showCreateEmptyState)): ?>
<!-- Empty State -->
<section class="py-5 text-center container">
<div class="row py-lg-4">
<div class="col-lg-8 col-md-10 mx-auto">
<h1 class="fw-light">Willkommen bei Simple Wishlist</h1>
<p class="lead text-muted">
Es wurde keine gültige Liste ausgewählt. Du kannst jetzt eine neue Liste anlegen.
</p>
<p>
<button class="btn btn-success btn-lg" data-bs-toggle="modal" data-bs-target="#createListModal">
Neue Liste anlegen
</button>
</p>
</div>
</div>
</section>
<?php else: ?>
<?php wishlistMainBuilder($ListID, $sortby); ?>
<?php endif; ?>
</main>
<footer class="text-muted py-5">
<footer class="mt-5 text-center text-muted small">
<div class="container">
<p class="float-end mb-1"><a href="#">Back to top</a></p>
<p class="mb-1">Simple Wishlist &copy; by Marcel Peterkau</p>
<hr class="mb-3 opacity-25">
<p class="mb-1">
Made with
<img src="img/heart-icon.png" alt="Shakaru" class="icon-sm mx-1">
and
<img src="img/paw-icon.png" alt="Paw" class="icon-sm mx-1">
by
<strong>
<img src="img/shakaru-icon.png" alt="Shakaru" class="icon-sm mx-1">
Shakaru
</strong>
</p>
<p class="mb-0">
&copy; <?= date('Y') ?> Simple Wishlist · Marcel Peterkau
· <a href="#" class="text-decoration-none">Back to top ↑</a>
</p>
</div>
</footer>
@@ -478,6 +539,42 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</div>
</div>
<!-- Create List Modal -->
<div class="modal fade" id="createListModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Neue Liste anlegen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Schließen"></button>
</div>
<form action="" method="POST">
<div class="modal-body">
<div class="mb-3">
<label for="listName" class="form-label">Titel der Liste</label>
<input type="text" class="form-control" id="listName" name="listName" required maxlength="255">
</div>
<div class="mb-3">
<label for="listDescription" class="form-label">Beschreibung (optional)</label>
<textarea class="form-control" id="listDescription" name="listDescription" rows="3"></textarea>
</div>
<div class="mb-3">
<label for="listPassword" class="form-label">Admin-Passwort (zum Bearbeiten)</label>
<input type="password" class="form-control" id="listPassword" name="listPassword" required>
<div class="form-text">
Dieses Passwort brauchst du später für Login/Bearbeitung der Liste.
</div>
</div>
</div>
<div class="modal-footer">
<input type="hidden" name="csrf" value="<?= e($_SESSION['csrf']) ?>">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" class="btn btn-primary" name="listadd">Anlegen</button>
</div>
</form>
</div>
</div>
</div>
<script src="js/bootstrap.bundle.min.js"></script>
<script src="js/jquery.min.js"></script>
<script src="js/wishlist.js"></script>