0) { // Einen zufälligen auswählen $randomFile = $files[array_rand($files)]; // In relative URL umwandeln $relativePath = str_replace($_SERVER['DOCUMENT_ROOT'], '', $randomFile); return $relativePath; } // Fallback: statisches no-image return '/img/no-image-katie-1.png'; } } /** * DB-Connector (keine Fehlerdetails nach außen) */ function lg_db(): mysqli { global $servername, $username, $password, $db; $conn = new mysqli($servername, $username, $password, $db); if ($conn->connect_error) { http_response_code(500); exit('Interner Fehler (DB)'); } $conn->set_charset('utf8mb4'); return $conn; } /** * Einzelne Karten-Kachel rendern */ function generateListItem( int $ListItemID, ?string $ItemImage, string $ItemTitle, ?string $ItemLink, int $ItemPriceCents, ?string $ItemComment, int $ItemReserved, ?string $ItemDate, int $ItemQty, ): void { global $loggedin, $imagedir; // Preis formatieren (de_DE) $priceText = ''; if ($ItemPriceCents > 0) { if (class_exists('NumberFormatter')) { $fmt = new NumberFormatter('de_DE', NumberFormatter::CURRENCY); $priceText = $fmt->formatCurrency($ItemPriceCents / 100, 'EUR'); } else { $priceText = number_format($ItemPriceCents / 100, 2, ',', '.') . ' €'; } } // Kommentar (2 Zeilen max., CSS macht den Clamp) $commentPlain = trim((string) $ItemComment); $commentHtml = ($commentPlain === '') ? ' ' : e($commentPlain); // Bild (lokal oder Placeholder) $srcPath = ''; if (!empty($ItemImage)) { $local = rtrim($imagedir, '/') . '/' . $ItemImage; $srcPath = @is_file($local) ? $local : lg_default_image(); } else { $srcPath = lg_default_image(); } $imageTag = '
Bild zum Wunsch
'; // Titelzeile: Qty-Pill (nur wenn >1) + einzeiliger Titel $qtyPill = ($ItemQty > 1) ? '×' . (int) $ItemQty . '' : ''; $titleHtml = '
' . $qtyPill . '
' . e($ItemTitle) . '
' . '
'; // Status-Badge links $statusLeft = ''; if ($ItemReserved >= $ItemQty) { $statusLeft = 'alle reserviert'; } elseif ($ItemReserved > 0) { $statusLeft = '' . (int) $ItemReserved . ' / ' . (int) $ItemQty . ' reserviert'; } // Datum (lokal lesbar) $dateHtml = ''; if (!empty($ItemDate)) { $ts = strtotime($ItemDate); $dateHtml = $ts ? date('d.m.Y', $ts) : e($ItemDate); } // User-Buttons (Reserve / Cancel / Vendor) // --- unten: Aktionsleiste (Icons, wachsen bei Hover) --- // (Reserve nur wenn möglich; Cancel immer; Vendor nur wenn Link) $userBtns = []; if ($ItemReserved < $ItemQty) { $userBtns[] = sprintf( '', $ListItemID ); } if ($ItemReserved > 0) { $userBtns[] = sprintf( '', $ListItemID ); } if (!empty($ItemLink)) { $safeLink = e($ItemLink); $userBtns[] = ' Zum Anbieter '; } $userBtnRow = implode("\n", $userBtns); // ... in deinem Echo-Heredoc: //
// {$userBtnRow} //
// Admin-Icons (nur eingeloggt) $adminFloating = ''; if (!empty($loggedin)) { $adminFloating = '
'; } // Preis-Badge rechts $priceHtml = $priceText !== '' ? '' . e($priceText) . '' : ''; echo <<
{$adminFloating} {$imageTag}
{$titleHtml}

{$commentHtml}

{$statusLeft}
{$priceHtml}
{$dateHtml}
{$userBtnRow}
HTML; } /** * Haupt-Builder für die Listenansicht * $ListID: aktive Liste (int, intern) * $sortby: einer aus der Whitelist unten */ function wishlistMainBuilder(int $ListID, string $sortby = 'priority'): void { global $loggedin; $conn = lg_db(); // 1) Listen-Metadaten holen $stmt = $conn->prepare('SELECT title, description FROM lists WHERE ID = ?'); $stmt->bind_param('i', $ListID); $stmt->execute(); $res = $stmt->get_result(); $listTitle = 'Unbekannte Liste'; $listDesc = ''; if ($res && $row = $res->fetch_assoc()) { $listTitle = e((string) $row['title']); $listDesc = e((string) $row['description']); } $stmt->close(); // 2) ORDER BY Whitelist $orderWhitelist = [ 'priority' => 'priority DESC', 'price_asc' => 'price ASC', 'price_desc' => 'price DESC', 'date_desc' => 'date DESC', 'date_asc' => 'date ASC', 'random' => 'RAND()', ]; $orderSql = $orderWhitelist[$sortby] ?? $orderWhitelist['priority']; // 3) Wünsche laden $sql = " 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(); $items = $stmt->get_result(); // 4) Header-Text (vorher berechnen, kein Ternary im Heredoc) $loginMsg = $loggedin ? 'Eingeloggt: Du kannst Einträge bearbeiten, löschen und priorisieren.' : 'Tipp: Du kannst Einträge reservieren. Nur mit deinem Reservierungs-Passwort lässt sich die Reservierung wieder lösen.'; echo <<

{$listTitle}

{$listDesc}

{$loginMsg}

HTML; if ($items !== false && $items->num_rows > 0) { while ($row = $items->fetch_assoc()) { generateListItem( (int) $row['ID'], $row['image'] !== null ? (string) $row['image'] : null, (string) $row['title'], $row['link'] !== null ? (string) $row['link'] : null, (int) $row['price'], $row['description'] !== null ? (string) $row['description'] : null, (int) $row['reserved_count'], $row['date'] !== null ? (string) $row['date'] : null, isset($row['qty']) ? (int) $row['qty'] : 1 ); } } else { echo '
Keine Einträge vorhanden.
'; } echo <<
HTML; $stmt->close(); $conn->close(); }