|
|
@@ -1,9 +1,9 @@
|
|
|
<?php
|
|
|
+require_once 'config.php';
|
|
|
require_once 'db_config.php';
|
|
|
|
|
|
$filterPlayerId = isset($_GET['team_filter']) ? (int)$_GET['team_filter'] : 0;
|
|
|
|
|
|
-// SQL-Abfrage: :fid wird für den Einzel-Status genutzt, :pid für den globalen Filter
|
|
|
$sql = "SELECT sp.*, t.bezeichnung as typ_name, MAX(sc.sterne) as best_sterne,
|
|
|
(SELECT status FROM besitzstatus WHERE spiel_id = sp.id AND spieler_id = :fid) as einzel_status,
|
|
|
(SELECT GROUP_CONCAT(CONCAT(p.name, ': ', b.status) SEPARATOR '||')
|
|
|
@@ -16,56 +16,78 @@ $sql = "SELECT sp.*, t.bezeichnung as typ_name, MAX(sc.sterne) as best_sterne,
|
|
|
GROUP BY sp.id ORDER BY sp.id DESC";
|
|
|
|
|
|
$stmt = $pdo->prepare($sql);
|
|
|
-
|
|
|
-// Parameter-Array vorbereiten
|
|
|
-$params = ['fid' => $filterPlayerId]; // :fid wird immer benötigt für die Subquery
|
|
|
-if ($filterPlayerId > 0) {
|
|
|
- $params['pid'] = $filterPlayerId; // :pid nur hinzufügen, wenn der Filter aktiv ist
|
|
|
-}
|
|
|
-
|
|
|
+$params = ['fid' => $filterPlayerId];
|
|
|
+if ($filterPlayerId > 0) { $params['pid'] = $filterPlayerId; }
|
|
|
$stmt->execute($params);
|
|
|
$inventory = $stmt->fetchAll();
|
|
|
|
|
|
-$spieler = $pdo->query("SELECT * FROM spieler ORDER BY id ASC")->fetchAll();
|
|
|
+$spieler = $pdo->query("SELECT * FROM spieler ORDER BY name ASC")->fetchAll();
|
|
|
?>
|
|
|
<!DOCTYPE html>
|
|
|
<html lang="de">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
- <title>EXIT Gesamtliste</title>
|
|
|
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
+ <title>EXIT - Lokale Gesamtliste</title>
|
|
|
<style>
|
|
|
- body { font-family: 'Segoe UI', sans-serif; background: #f8f9fa; padding: 20px; }
|
|
|
+ body { font-family: 'Segoe UI', sans-serif; background: #f8f9fa; margin: 0; padding: 20px; color: #333; }
|
|
|
.container { max-width: 1400px; margin: 0 auto; }
|
|
|
- table { width: 100%; border-collapse: collapse; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
|
|
|
- th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #eee; }
|
|
|
- th { background: #e67e22; color: white; cursor: pointer; font-size: 0.8em; text-transform: uppercase; }
|
|
|
- .game-thumb { width: 50px; height: 50px; object-fit: cover; border-radius: 8px; }
|
|
|
- .lvl-badge { padding: 4px 8px; border-radius: 4px; color: white; font-size: 0.7em; font-weight: bold; }
|
|
|
+ .header-area { display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px; }
|
|
|
+ .back-link { color: #e67e22; text-decoration: none; font-weight: bold; padding: 8px 15px; border: 2px solid #e67e22; border-radius: 6px; }
|
|
|
+
|
|
|
+ .filter-bar { background: white; padding: 15px; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); display: flex; gap: 15px; margin-bottom: 20px; align-items: center; }
|
|
|
+ select, input { padding: 12px; border: 1px solid #ddd; border-radius: 8px; }
|
|
|
+ input { flex-grow: 1; }
|
|
|
+
|
|
|
+ table { width: 100%; border-collapse: collapse; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.08); }
|
|
|
+ th { background: #2c3e50; color: white; text-align: left; padding: 15px; cursor: pointer; font-size: 0.8em; }
|
|
|
+ td { padding: 12px 15px; border-bottom: 1px solid #eee; }
|
|
|
+
|
|
|
+ /* Thumbnails */
|
|
|
+ .game-thumb {
|
|
|
+ width: 55px; height: 55px; object-fit: cover; border-radius: 8px;
|
|
|
+ border: 1px solid #ddd; cursor: pointer; transition: 0.2s;
|
|
|
+ }
|
|
|
+ .game-thumb:hover { transform: scale(1.1); border-color: #e67e22; }
|
|
|
+
|
|
|
+ /* Badges */
|
|
|
+ .lvl-badge { padding: 4px 10px; border-radius: 20px; color: white; font-size: 11px; font-weight: bold; }
|
|
|
.lvl-Einsteiger { background: #27ae60; } .lvl-Fortgeschrittene { background: #2980b9; } .lvl-Profi { background: #c0392b; }
|
|
|
- .st-badge { padding: 4px 8px; border-radius: 12px; font-size: 0.7em; font-weight: bold; display: inline-block; }
|
|
|
+ .st-badge { padding: 5px 12px; border-radius: 15px; font-size: 11px; font-weight: bold; }
|
|
|
.st-besitzt { background: #d4edda; color: #155724; }
|
|
|
.st-verkauft { background: #fff3cd; color: #856404; }
|
|
|
.st-keins { background: #f8d7da; color: #721c24; }
|
|
|
- .team-pill { display: inline-block; background: #eee; padding: 2px 6px; border-radius: 4px; font-size: 0.8em; margin: 2px; border: 1px solid #ddd; }
|
|
|
+ .team-pill { display: inline-flex; align-items: center; background: #f1f2f6; padding: 4px 10px; border-radius: 6px; font-size: 12px; margin: 2px; border: 1px solid #dfe4ea; }
|
|
|
+ .dot { height: 8px; width: 8px; border-radius: 50%; display: inline-block; margin-right: 6px; }
|
|
|
+
|
|
|
+ /* Hover-Vorschau */
|
|
|
+ .image-preview { position: absolute; display: none; width: 220px; border: 4px solid white; border-radius: 12px; z-index: 999; box-shadow: 0 15px 35px rgba(0,0,0,0.4); pointer-events: none; }
|
|
|
+
|
|
|
+ /* --- SELF-MADE MODAL (LIGHTBOX) --- */
|
|
|
+ #imgModal {
|
|
|
+ display: none; position: fixed; z-index: 10000; left: 0; top: 0; width: 100%; height: 100%;
|
|
|
+ background-color: rgba(0,0,0,0.9); cursor: zoom-out; align-items: center; justify-content: center;
|
|
|
+ }
|
|
|
+ #modalContent { max-width: 90%; max-height: 90%; border-radius: 10px; box-shadow: 0 0 20px rgba(0,0,0,0.5); cursor: default; }
|
|
|
+ .close-modal { position: absolute; top: 20px; right: 35px; color: #fff; font-size: 40px; font-weight: bold; cursor: pointer; }
|
|
|
</style>
|
|
|
</head>
|
|
|
<body>
|
|
|
+
|
|
|
<div class="container">
|
|
|
- <div style="display:flex; justify-content: space-between; align-items:center; margin-bottom:20px;">
|
|
|
+ <div class="header-area">
|
|
|
<h1>📝 Sammlungs-Übersicht</h1>
|
|
|
- <a href="index.php" style="color:#e67e22; font-weight:bold; text-decoration:none;">← Dashboard</a>
|
|
|
+ <a href="index.php" class="back-link">← Zum Dashboard</a>
|
|
|
</div>
|
|
|
|
|
|
- <div style="background:white; padding:15px; border-radius:10px; margin-bottom:20px; display:flex; gap:15px;">
|
|
|
- <form method="GET">
|
|
|
- <select name="team_filter" onchange="this.form.submit()" style="padding:10px; border-radius:5px;">
|
|
|
- <option value="0">-- Alle Teams --</option>
|
|
|
- <?php foreach ($spieler as $p): ?>
|
|
|
- <option value="<?= $p['id'] ?>" <?= $filterPlayerId == $p['id'] ? 'selected' : '' ?>><?= htmlspecialchars($p['name']) ?></option>
|
|
|
- <?php endforeach; ?>
|
|
|
- </select>
|
|
|
- </form>
|
|
|
- <input type="text" id="searchInput" placeholder="Suchen..." onkeyup="filterTable()" style="flex-grow:1; padding:10px; border:1px solid #ddd; border-radius:5px;">
|
|
|
+ <div class="filter-bar">
|
|
|
+ <form method="GET"><select name="team_filter" onchange="this.form.submit()">
|
|
|
+ <option value="0">-- Alle Teams --</option>
|
|
|
+ <?php foreach ($spieler as $p): ?>
|
|
|
+ <option value="<?= $p['id'] ?>" <?= $filterPlayerId == $p['id'] ? 'selected' : '' ?>><?= htmlspecialchars($p['name']) ?></option>
|
|
|
+ <?php endforeach; ?>
|
|
|
+ </select></form>
|
|
|
+ <input type="text" id="searchInput" placeholder="Suchen..." onkeyup="filterTable()">
|
|
|
</div>
|
|
|
|
|
|
<table id="exitTable">
|
|
|
@@ -83,30 +105,31 @@ $spieler = $pdo->query("SELECT * FROM spieler ORDER BY id ASC")->fetchAll();
|
|
|
<?php foreach ($inventory as $row): ?>
|
|
|
<tr>
|
|
|
<td>#<?= $row['id'] ?></td>
|
|
|
- <td><img src="<?= $row['bild_url'] ?>" class="game-thumb" onerror="this.src='https://via.placeholder.com/50?text=?'"></td>
|
|
|
- <td><strong><?= htmlspecialchars($row['titel']) ?></strong><br><small style="color:#666;"><?= htmlspecialchars($row['typ_name'] ?? 'Unbekannt') ?></small></td>
|
|
|
+ <td>
|
|
|
+ <img src="<?= htmlspecialchars($row['bild_url']) ?>"
|
|
|
+ class="game-thumb"
|
|
|
+ onclick="openModal('<?= htmlspecialchars($row['bild_url']) ?>')"
|
|
|
+ onmouseover="showPreview(event, '<?= htmlspecialchars($row['bild_url']) ?>')"
|
|
|
+ onmouseout="hidePreview()"
|
|
|
+ onerror="this.src='https://via.placeholder.com/60?text=?'">
|
|
|
+ </td>
|
|
|
+ <td><strong><?= htmlspecialchars($row['titel']) ?></strong><br><small><?= htmlspecialchars($row['typ_name'] ?? 'Klassisch') ?></small></td>
|
|
|
<td><span class="lvl-badge lvl-<?= $row['level'] ?>"><?= $row['level'] ?></span></td>
|
|
|
- <td><?= $row['best_sterne'] ? "<b style='color:#e67e22'>{$row['best_sterne']} ★</b>" : "-" ?></td>
|
|
|
+ <td><?= $row['best_sterne'] ? "<b>{$row['best_sterne']} ★</b>" : "-" ?></td>
|
|
|
<td>
|
|
|
<?php if ($filterPlayerId > 0):
|
|
|
$st = $row['einzel_status'] ?: 'nicht besitzt';
|
|
|
$class = ($st == 'besitzt') ? 'st-besitzt' : (($st == 'verkauft') ? 'st-verkauft' : 'st-keins');
|
|
|
- ?>
|
|
|
- <span class="st-badge <?= $class ?>"><?= strtoupper($st) ?></span>
|
|
|
- <?php else: ?>
|
|
|
- <?php
|
|
|
+ echo "<span class='st-badge $class'>".strtoupper($st)."</span>";
|
|
|
+ else:
|
|
|
if ($row['besitz_info']) {
|
|
|
- $teams = explode('||', $row['besitz_info']);
|
|
|
- foreach ($teams as $t) {
|
|
|
- $parts = explode(': ', $t);
|
|
|
- if(count($parts) < 2) continue;
|
|
|
- $tname = $parts[0]; $tstat = $parts[1];
|
|
|
- $color = ($tstat == 'besitzt') ? '#27ae60' : '#f39c12';
|
|
|
- echo "<span class='team-pill'><b style='color:$color'>●</b> " . htmlspecialchars($tname) . " (" . htmlspecialchars($tstat) . ")</span>";
|
|
|
+ foreach (explode('||', $row['besitz_info']) as $t) {
|
|
|
+ list($tn, $ts) = explode(': ', $t);
|
|
|
+ $dot = ($ts == 'besitzt') ? '#27ae60' : '#f39c12';
|
|
|
+ echo "<span class='team-pill'><span class='dot' style='background:$dot'></span>$tn ($ts)</span>";
|
|
|
}
|
|
|
- } else { echo "<small style='color:#ccc'>Kein Team besitzt es</small>"; }
|
|
|
- ?>
|
|
|
- <?php endif; ?>
|
|
|
+ } else { echo "<small>-</small>"; }
|
|
|
+ endif; ?>
|
|
|
</td>
|
|
|
</tr>
|
|
|
<?php endforeach; ?>
|
|
|
@@ -114,38 +137,57 @@ $spieler = $pdo->query("SELECT * FROM spieler ORDER BY id ASC")->fetchAll();
|
|
|
</table>
|
|
|
</div>
|
|
|
|
|
|
+<div id="imgModal" onclick="this.style.display='none'">
|
|
|
+ <span class="close-modal">×</span>
|
|
|
+ <img id="modalContent">
|
|
|
+</div>
|
|
|
+
|
|
|
+<img id="hoverPreview" class="image-preview" src="">
|
|
|
+
|
|
|
<script>
|
|
|
+ // --- MODAL LOGIK ---
|
|
|
+ const modal = document.getElementById("imgModal");
|
|
|
+ const modalImg = document.getElementById("modalContent");
|
|
|
+
|
|
|
+ function openModal(url) {
|
|
|
+ modal.style.display = "flex";
|
|
|
+ modalImg.src = url;
|
|
|
+ hidePreview(); // Vorschau ausblenden, wenn Modal öffnet
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- HOVER LOGIK ---
|
|
|
+ const preview = document.getElementById('hoverPreview');
|
|
|
+ function showPreview(e, url) {
|
|
|
+ if(modal.style.display === "flex") return; // Keine Vorschau wenn Modal offen
|
|
|
+ preview.src = url;
|
|
|
+ preview.style.display = 'block';
|
|
|
+ updatePos(e);
|
|
|
+ }
|
|
|
+ function hidePreview() { preview.style.display = 'none'; }
|
|
|
+ function updatePos(e) { preview.style.left = (e.pageX + 20) + 'px'; preview.style.top = (e.pageY - 100) + 'px'; }
|
|
|
+ document.addEventListener('mousemove', (e) => { if(preview.style.display==='block') updatePos(e); });
|
|
|
+
|
|
|
+ // --- FILTER & SORTIERUNG ---
|
|
|
function filterTable() {
|
|
|
- let input = document.getElementById("searchInput"),
|
|
|
- filter = input.value.toUpperCase(),
|
|
|
- tr = document.getElementById("exitTable").getElementsByTagName("tr");
|
|
|
- for (let i = 1; i < tr.length; i++) {
|
|
|
- tr[i].style.display = tr[i].innerText.toUpperCase().includes(filter) ? "" : "none";
|
|
|
- }
|
|
|
+ let val = document.getElementById("searchInput").value.toUpperCase();
|
|
|
+ let tr = document.getElementById("exitTable").getElementsByTagName("tr");
|
|
|
+ for (let i = 1; i < tr.length; i++) tr[i].style.display = tr[i].innerText.toUpperCase().includes(val) ? "" : "none";
|
|
|
}
|
|
|
|
|
|
function sortTable(n) {
|
|
|
- var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
|
|
|
- table = document.getElementById("exitTable");
|
|
|
- switching = true; dir = "asc";
|
|
|
+ var table = document.getElementById("exitTable"), rows, switching = true, i, x, y, should, dir = "asc", cnt = 0;
|
|
|
while (switching) {
|
|
|
switching = false; rows = table.rows;
|
|
|
for (i = 1; i < (rows.length - 1); i++) {
|
|
|
- shouldSwitch = false;
|
|
|
- x = rows[i].getElementsByTagName("TD")[n];
|
|
|
- y = rows[i + 1].getElementsByTagName("TD")[n];
|
|
|
- var valX = x.innerText.toLowerCase().replace('#', '');
|
|
|
- var valY = y.innerText.toLowerCase().replace('#', '');
|
|
|
+ should = false; x = rows[i].getElementsByTagName("TD")[n]; y = rows[i + 1].getElementsByTagName("TD")[n];
|
|
|
+ let valX = x.innerText.toLowerCase().replace('#', '');
|
|
|
+ let valY = y.innerText.toLowerCase().replace('#', '');
|
|
|
if (n === 0) { valX = parseInt(valX); valY = parseInt(valY); }
|
|
|
- if (dir == "asc") { if (valX > valY) { shouldSwitch = true; break; }
|
|
|
- } else { if (valX < valY) { shouldSwitch = true; break; } }
|
|
|
- }
|
|
|
- if (shouldSwitch) {
|
|
|
- rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
|
|
|
- switching = true; switchcount ++;
|
|
|
- } else {
|
|
|
- if (switchcount == 0 && dir == "asc") { dir = "desc"; switching = true; }
|
|
|
+ if (dir == "asc") { if (valX > valY) { should = true; break; } }
|
|
|
+ else { if (valX < valY) { should = true; break; } }
|
|
|
}
|
|
|
+ if (should) { rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); switching = true; cnt++; }
|
|
|
+ else if (cnt == 0 && dir == "asc") { dir = "desc"; switching = true; }
|
|
|
}
|
|
|
}
|
|
|
</script>
|