gesamtliste.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <?php
  2. require_once 'config.php';
  3. require_once 'db_config.php';
  4. $filterPlayerId = isset($_GET['team_filter']) ? (int)$_GET['team_filter'] : 0;
  5. $sql = "SELECT sp.*, t.bezeichnung as typ_name, MAX(sc.sterne) as best_sterne,
  6. (SELECT status FROM besitzstatus WHERE spiel_id = sp.id AND spieler_id = :fid) as einzel_status,
  7. (SELECT GROUP_CONCAT(CONCAT(p.name, ': ', b.status) SEPARATOR '||')
  8. FROM besitzstatus b JOIN spieler p ON b.spieler_id = p.id
  9. WHERE b.spiel_id = sp.id AND b.status != 'nicht besitzt') as besitz_info
  10. FROM spiele sp
  11. LEFT JOIN game_typen t ON sp.typ_id = t.id
  12. LEFT JOIN scores sc ON sp.id = sc.spiel_id " .
  13. ($filterPlayerId > 0 ? " AND sc.spieler_id = :pid " : "") . "
  14. GROUP BY sp.id ORDER BY sp.id DESC";
  15. $stmt = $pdo->prepare($sql);
  16. $params = ['fid' => $filterPlayerId];
  17. if ($filterPlayerId > 0) { $params['pid'] = $filterPlayerId; }
  18. $stmt->execute($params);
  19. $inventory = $stmt->fetchAll();
  20. $spieler = $pdo->query("SELECT * FROM spieler ORDER BY name ASC")->fetchAll();
  21. ?>
  22. <!DOCTYPE html>
  23. <html lang="de">
  24. <head>
  25. <meta charset="UTF-8">
  26. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  27. <title>EXIT - Lokale Gesamtliste</title>
  28. <style>
  29. body { font-family: 'Segoe UI', sans-serif; background: #f8f9fa; margin: 0; padding: 20px; color: #333; }
  30. .container { max-width: 1400px; margin: 0 auto; }
  31. .header-area { display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px; }
  32. .back-link { color: #e67e22; text-decoration: none; font-weight: bold; padding: 8px 15px; border: 2px solid #e67e22; border-radius: 6px; }
  33. .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; }
  34. select, input { padding: 12px; border: 1px solid #ddd; border-radius: 8px; }
  35. input { flex-grow: 1; }
  36. table { width: 100%; border-collapse: collapse; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.08); }
  37. th { background: #2c3e50; color: white; text-align: left; padding: 15px; cursor: pointer; font-size: 0.8em; }
  38. td { padding: 12px 15px; border-bottom: 1px solid #eee; }
  39. /* Thumbnails */
  40. .game-thumb {
  41. width: 55px; height: 55px; object-fit: cover; border-radius: 8px;
  42. border: 1px solid #ddd; cursor: pointer; transition: 0.2s;
  43. }
  44. .game-thumb:hover { transform: scale(1.1); border-color: #e67e22; }
  45. /* Badges */
  46. .lvl-badge { padding: 4px 10px; border-radius: 20px; color: white; font-size: 11px; font-weight: bold; }
  47. .lvl-Einsteiger { background: #27ae60; } .lvl-Fortgeschrittene { background: #2980b9; } .lvl-Profi { background: #c0392b; }
  48. .st-badge { padding: 5px 12px; border-radius: 15px; font-size: 11px; font-weight: bold; }
  49. .st-besitzt { background: #d4edda; color: #155724; }
  50. .st-verkauft { background: #fff3cd; color: #856404; }
  51. .st-keins { background: #f8d7da; color: #721c24; }
  52. .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; }
  53. .dot { height: 8px; width: 8px; border-radius: 50%; display: inline-block; margin-right: 6px; }
  54. /* Hover-Vorschau */
  55. .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; }
  56. /* --- SELF-MADE MODAL (LIGHTBOX) --- */
  57. #imgModal {
  58. display: none; position: fixed; z-index: 10000; left: 0; top: 0; width: 100%; height: 100%;
  59. background-color: rgba(0,0,0,0.9); cursor: zoom-out; align-items: center; justify-content: center;
  60. }
  61. #modalContent { max-width: 90%; max-height: 90%; border-radius: 10px; box-shadow: 0 0 20px rgba(0,0,0,0.5); cursor: default; }
  62. .close-modal { position: absolute; top: 20px; right: 35px; color: #fff; font-size: 40px; font-weight: bold; cursor: pointer; }
  63. </style>
  64. </head>
  65. <body>
  66. <div class="container">
  67. <div class="header-area">
  68. <h1>📝 Sammlungs-Übersicht</h1>
  69. <a href="index.php" class="back-link">← Zum Dashboard</a>
  70. </div>
  71. <div class="filter-bar">
  72. <form method="GET"><select name="team_filter" onchange="this.form.submit()">
  73. <option value="0">-- Alle Teams --</option>
  74. <?php foreach ($spieler as $p): ?>
  75. <option value="<?= $p['id'] ?>" <?= $filterPlayerId == $p['id'] ? 'selected' : '' ?>><?= htmlspecialchars($p['name']) ?></option>
  76. <?php endforeach; ?>
  77. </select></form>
  78. <input type="text" id="searchInput" placeholder="Suchen..." onkeyup="filterTable()">
  79. </div>
  80. <table id="exitTable">
  81. <thead>
  82. <tr>
  83. <th onclick="sortTable(0)">ID</th>
  84. <th>Bild</th>
  85. <th onclick="sortTable(2)">Titel</th>
  86. <th>Level</th>
  87. <th>Bestwert</th>
  88. <th>Besitzstatus</th>
  89. </tr>
  90. </thead>
  91. <tbody>
  92. <?php foreach ($inventory as $row): ?>
  93. <tr>
  94. <td>#<?= $row['id'] ?></td>
  95. <td>
  96. <img src="<?= htmlspecialchars($row['bild_url']) ?>"
  97. class="game-thumb"
  98. onclick="openModal('<?= htmlspecialchars($row['bild_url']) ?>')"
  99. onmouseover="showPreview(event, '<?= htmlspecialchars($row['bild_url']) ?>')"
  100. onmouseout="hidePreview()"
  101. onerror="this.src='https://via.placeholder.com/60?text=?'">
  102. </td>
  103. <td><strong><?= htmlspecialchars($row['titel']) ?></strong><br><small><?= htmlspecialchars($row['typ_name'] ?? 'Klassisch') ?></small></td>
  104. <td><span class="lvl-badge lvl-<?= $row['level'] ?>"><?= $row['level'] ?></span></td>
  105. <td><?= $row['best_sterne'] ? "<b>{$row['best_sterne']} ★</b>" : "-" ?></td>
  106. <td>
  107. <?php if ($filterPlayerId > 0):
  108. $st = $row['einzel_status'] ?: 'nicht besitzt';
  109. $class = ($st == 'besitzt') ? 'st-besitzt' : (($st == 'verkauft') ? 'st-verkauft' : 'st-keins');
  110. echo "<span class='st-badge $class'>".strtoupper($st)."</span>";
  111. else:
  112. if ($row['besitz_info']) {
  113. foreach (explode('||', $row['besitz_info']) as $t) {
  114. list($tn, $ts) = explode(': ', $t);
  115. $dot = ($ts == 'besitzt') ? '#27ae60' : '#f39c12';
  116. echo "<span class='team-pill'><span class='dot' style='background:$dot'></span>$tn ($ts)</span>";
  117. }
  118. } else { echo "<small>-</small>"; }
  119. endif; ?>
  120. </td>
  121. </tr>
  122. <?php endforeach; ?>
  123. </tbody>
  124. </table>
  125. </div>
  126. <div id="imgModal" onclick="this.style.display='none'">
  127. <span class="close-modal">&times;</span>
  128. <img id="modalContent">
  129. </div>
  130. <img id="hoverPreview" class="image-preview" src="">
  131. <script>
  132. // --- MODAL LOGIK ---
  133. const modal = document.getElementById("imgModal");
  134. const modalImg = document.getElementById("modalContent");
  135. function openModal(url) {
  136. modal.style.display = "flex";
  137. modalImg.src = url;
  138. hidePreview(); // Vorschau ausblenden, wenn Modal öffnet
  139. }
  140. // --- HOVER LOGIK ---
  141. const preview = document.getElementById('hoverPreview');
  142. function showPreview(e, url) {
  143. if(modal.style.display === "flex") return; // Keine Vorschau wenn Modal offen
  144. preview.src = url;
  145. preview.style.display = 'block';
  146. updatePos(e);
  147. }
  148. function hidePreview() { preview.style.display = 'none'; }
  149. function updatePos(e) { preview.style.left = (e.pageX + 20) + 'px'; preview.style.top = (e.pageY - 100) + 'px'; }
  150. document.addEventListener('mousemove', (e) => { if(preview.style.display==='block') updatePos(e); });
  151. // --- FILTER & SORTIERUNG ---
  152. function filterTable() {
  153. let val = document.getElementById("searchInput").value.toUpperCase();
  154. let tr = document.getElementById("exitTable").getElementsByTagName("tr");
  155. for (let i = 1; i < tr.length; i++) tr[i].style.display = tr[i].innerText.toUpperCase().includes(val) ? "" : "none";
  156. }
  157. function sortTable(n) {
  158. var table = document.getElementById("exitTable"), rows, switching = true, i, x, y, should, dir = "asc", cnt = 0;
  159. while (switching) {
  160. switching = false; rows = table.rows;
  161. for (i = 1; i < (rows.length - 1); i++) {
  162. should = false; x = rows[i].getElementsByTagName("TD")[n]; y = rows[i + 1].getElementsByTagName("TD")[n];
  163. let valX = x.innerText.toLowerCase().replace('#', '');
  164. let valY = y.innerText.toLowerCase().replace('#', '');
  165. if (n === 0) { valX = parseInt(valX); valY = parseInt(valY); }
  166. if (dir == "asc") { if (valX > valY) { should = true; break; } }
  167. else { if (valX < valY) { should = true; break; } }
  168. }
  169. if (should) { rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); switching = true; cnt++; }
  170. else if (cnt == 0 && dir == "asc") { dir = "desc"; switching = true; }
  171. }
  172. }
  173. </script>
  174. </body>
  175. </html>