From 1ef0f7c8bf49597d8511077f616a453c559281fd Mon Sep 17 00:00:00 2001 From: Fabian Schieder Date: Mon, 30 Mar 2026 23:02:35 +0200 Subject: [PATCH] Add attribute filter bar to product listing with dynamic selection --- assets/css/catbar.css | 65 ++++++++++++++++++++ attrbar.php | 137 ++++++++++++++++++++++++++++++++++++++++++ compcards.php | 38 +++++++++--- 3 files changed, 233 insertions(+), 7 deletions(-) create mode 100644 attrbar.php diff --git a/assets/css/catbar.css b/assets/css/catbar.css index daece0b..6eb5f3f 100644 --- a/assets/css/catbar.css +++ b/assets/css/catbar.css @@ -132,3 +132,68 @@ min-height: 36px; } } + +@media (min-width: 1024px) { + .home-nav__item a { + font-size: 1rem; + padding: 5px 24px; + } +} + +/* ========================================================== + ATTRIBUTE FILTER BAR + ========================================================== */ + +.attrbar { + width: 100%; + background-color: #f8f9fa; + border-bottom: 1px solid #ddd; + padding: 10px 0; +} + +.attrbar__inner { + display: flex; + justify-content: center; + align-items: center; +} + +.attr-filter-form { + display: flex; + flex-wrap: wrap; + gap: 15px; + align-items: center; + justify-content: center; +} + +.attr-filter-item { + display: flex; + align-items: center; + gap: 5px; + font-size: 0.9rem; + color: #333; +} + +.attr-filter-item select { + padding: 4px 8px; + border: 1px solid #ccc; + border-radius: 4px; + background-color: #fff; + cursor: pointer; + font-size: 0.85rem; +} + +.attr-filter-reset { + font-size: 0.85rem; + color: #e63946; + text-decoration: none; + padding: 4px 8px; + border: 1px solid #e63946; + border-radius: 4px; + background-color: transparent; + transition: all 0.2s ease; +} + +.attr-filter-reset:hover { + background-color: #e63946; + color: #fff; +} diff --git a/attrbar.php b/attrbar.php new file mode 100644 index 0000000..7d02885 --- /dev/null +++ b/attrbar.php @@ -0,0 +1,137 @@ + 20, + 'ipad' => 21, + 'macbook' => 22, + 'airpods' => 23, + 'accessories' => 24, +]; + +if (isset($categoriesConfig[$currentCategory])) { + $catId = $categoriesConfig[$currentCategory]; +} + +// Fetch available attributes for the category if selected +$attributes = []; +if ($catId) { + // Only show attributes related to the products in the active category + $stmtAttr = $conn->prepare(" + SELECT DISTINCT a.attributeID, a.name, a.unit + FROM attributes a + JOIN productAttributes pa ON a.attributeID = pa.attributeID + JOIN products p ON pa.productID = p.productID + WHERE p.categoryID = ? + ORDER BY a.name + "); + $stmtAttr->bind_param("i", $catId); + $stmtAttr->execute(); + $resAttr = $stmtAttr->get_result(); + while ($row = $resAttr->fetch_assoc()) { + $attributes[] = $row; + } + $stmtAttr->close(); +} else { + // Or all active across any product? + // User typically filters by category first. But if 'all', maybe just get top attributes. + $resAttr = $conn->query(" + SELECT DISTINCT a.attributeID, a.name, a.unit + FROM attributes a + JOIN productAttributes pa ON a.attributeID = pa.attributeID + ORDER BY a.name LIMIT 5 + "); + if ($resAttr) { + while ($row = $resAttr->fetch_assoc()) { + $attributes[] = $row; + } + } +} +?> + + +
+
+
+ + + + + + + + prepare(" + SELECT DISTINCT pa.valueString, pa.valueNumber, pa.valueBool + FROM productAttributes pa + JOIN products p ON pa.productID = p.productID + WHERE p.categoryID = ? AND pa.attributeID = ? + ORDER BY pa.valueString, pa.valueNumber + "); + $vStmt->bind_param("ii", $catId, $attrId); + } else { + $vStmt = $conn->prepare(" + SELECT DISTINCT valueString, valueNumber, valueBool + FROM productAttributes + WHERE attributeID = ? + ORDER BY valueString, valueNumber + "); + $vStmt->bind_param("i", $attrId); + } + $vStmt->execute(); + $vRes = $vStmt->get_result(); + $values = []; + while ($vRow = $vRes->fetch_assoc()) { + if ($vRow['valueString'] !== null) $values[] = $vRow['valueString']; + elseif ($vRow['valueNumber'] !== null) $values[] = $vRow['valueNumber']; + elseif ($vRow['valueBool'] !== null) $values[] = $vRow['valueBool'] ? 'Ja' : 'Nein'; + } + $vStmt->close(); + + if (empty($values)) continue; + $paramName = "attr_" . $attrId; + $selectedValue = isset($_GET[$paramName]) ? $_GET[$paramName] : ''; + ?> +
+ + +
+ + + + $v) { + if (strpos($k, 'attr_') === 0 && $v !== '') { + $hasActiveFilter = true; + break; + } + } + if ($hasActiveFilter): ?> + Filter zurücksetzen + +
+
+
+ + + diff --git a/compcards.php b/compcards.php index 081bc84..ee6fd95 100644 --- a/compcards.php +++ b/compcards.php @@ -92,15 +92,39 @@ $categories = [ prepare(" - SELECT productID, model, description, imagePath - FROM products - WHERE categoryID = ? - "); + $baseQuery = "SELECT DISTINCT p.productID, p.model, p.description, p.imagePath FROM products p "; + $whereClauses = ["p.categoryID = ?"]; + $params = [$cat['id']]; + $types = "i"; + + // Find attribute filters from $_GET + $attrIndex = 0; + foreach ($_GET as $k => $v) { + if ($v !== '' && strpos($k, 'attr_') === 0) { + $attrId = (int)substr($k, 5); + $attrAlias = "pa" . $attrIndex; + $baseQuery .= " JOIN productAttributes $attrAlias ON p.productID = $attrAlias.productID "; + + // Assume string or number comparison. For simplicity, check string or number. + // In DB, valueString, valueNumber, valueBool can be checked. + $whereClauses[] = "($attrAlias.attributeID = ? AND ($attrAlias.valueString = ? OR $attrAlias.valueNumber = ? OR ($attrAlias.valueBool = 1 AND ? = 'Ja') OR ($attrAlias.valueBool = 0 AND ? = 'Nein')))"; - $stmt->bind_param("i", $cat['id']); // i = integer + $params[] = $attrId; + $params[] = $v; + $params[] = is_numeric($v) ? (float)$v : 0; + $params[] = $v; + $params[] = $v; + $types .= "isdss"; + + $attrIndex++; + } + } + + $sql = $baseQuery . " WHERE " . implode(" AND ", $whereClauses); + + $stmt = $conn->prepare($sql); + $stmt->bind_param($types, ...$params); $stmt->execute(); - $result = $stmt->get_result(); ?>