Implemented infinite scroll to replace malfunctioning pagination

This commit is contained in:
Laura Dolibois
2025-06-01 15:48:46 +02:00
parent 2a9629ea22
commit e30e36ee45
3 changed files with 152 additions and 39 deletions

View File

@@ -1,5 +1,5 @@
import { AddShoppingCart } from "@mui/icons-material"; import { AddShoppingCart } from "@mui/icons-material";
import { Card, CardActionArea, CardContent, CardMedia, IconButton, Paper, Rating, Typography } from "@mui/material"; import {Box, Card, CardActionArea, CardContent, CardMedia, IconButton, Paper, Rating, Typography } from "@mui/material";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import Item from "../../components/Item"; import Item from "../../components/Item";
import { useBasket } from "../BasketProvider"; import { useBasket } from "../BasketProvider";
@@ -36,16 +36,20 @@ export default function ItemCard({ item }: { item: Item }) {
<Typography gutterBottom variant="h5" component="div"> <Typography gutterBottom variant="h5" component="div">
{item.name} {item.name}
</Typography> </Typography>
<Rating name="half-rating" readOnly defaultValue={item.rating} precision={0.5} /> <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end" }}>
<Typography variant="body2" sx={{ color: 'text.secondary' }} className="item-description"> <Typography variant="body2" sx={{ color: 'text.secondary' }} className="item-description">
{(item.price * (1 - item.discount / 100)).toFixed(2)} {(item.price * (1 - item.discount / 100)).toFixed(2)}
</Typography> </Typography>
<IconButton aria-label={t('addToCart')} onClick={handleAddToCart}>
<AddShoppingCart />
</IconButton>
</Box>
{item.stock > 10 ? ( {item.stock > 10 ? (
<Typography variant="body2"> <Typography variant="body2">
{t('inStock')} {t('inStock')}
</Typography> </Typography>
) : item.stock > 0 ?( ) : item.stock > 0 ? (
<Typography variant="body2" sx={{ color: 'orange' }}> <Typography variant="body2" sx={{ color: 'orange' }}>
{t('almostSoldOut')} {t('almostSoldOut')}
</Typography> </Typography>
@@ -56,11 +60,6 @@ export default function ItemCard({ item }: { item: Item }) {
)} )}
</CardContent> </CardContent>
</CardActionArea> </CardActionArea>
<CardContent>
<IconButton aria-label={t('addToCart')} onClick={handleAddToCart}>
<AddShoppingCart />
</IconButton>
</CardContent>
</Card> </Card>
</Paper> </Paper>
) )

View File

@@ -4,7 +4,7 @@ import Item from "../components/Item";
import FilterItem from "../helper/homepage/FilterItem"; import FilterItem from "../helper/homepage/FilterItem";
import ItemCard from "../helper/homepage/ItemCard"; import ItemCard from "../helper/homepage/ItemCard";
import "./pages.css"; // Import der CSS-Datei import "./pages.css"; // Import der CSS-Datei
import { useNavigate } from "react-router-dom" import { useLocation, useNavigate } from "react-router-dom"
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import PriceSlider from "../helper/homepage/PriceSlider"; import PriceSlider from "../helper/homepage/PriceSlider";
@@ -12,6 +12,7 @@ export default function Home() {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation();
const items: Item[] = [ const items: Item[] = [
{ {
@@ -245,14 +246,114 @@ export default function Home() {
discount: 15, discount: 15,
}, },
{ {
id: "112", id: "201",
name: "Item 4", name: "Item 1",
description: "Description 1",
price: 10,
stock: 100,
category: "Seeds",
rating: 4.5,
discount: 10,
},
{
id: "202",
name: "Item 2",
description: "Description 2",
price: 20,
stock: 9,
category: "GardenSupplies",
rating: 4.0,
discount: 20,
},
{
id: "203",
name: "Item 3",
description: "Description 3", description: "Description 3",
price: 50, price: 30,
stock: 10,
category: "TechnicalComponents",
rating: 4.8,
discount: 15,
},
{
id: "204",
name: "Item 3",
description: "Description 3",
price: 30,
stock: 0,
category: "TechnicalComponents",
rating: 1.8,
discount: 15,
},
{
id: "205",
name: "Item 3",
description: "Description 3",
price: 30,
stock: 300, stock: 300,
category: "Other", category: "TechnicalComponents",
rating: 1.0, rating: 2.2,
discount: 0, discount: 15,
},
{
id: "206",
name: "Item 3",
description: "Description 3",
price: 30,
stock: 300,
category: "TechnicalComponents",
rating: 3.8,
discount: 15,
},
{
id: "207",
name: "Item 3",
description: "Description 3",
price: 30,
stock: 300,
category: "TechnicalComponents",
rating: 4.8,
discount: 15,
},
{
id: "208",
name: "Item 3",
description: "Description 3",
price: 30,
stock: 300,
category: "TechnicalComponents",
rating: 3.4,
discount: 15,
},
{
id: "209",
name: "Item 3",
description: "Description 3",
price: 30,
stock: 300,
category: "TechnicalComponents",
rating: 2.9,
discount: 15,
},
{
id: "210",
name: "Item 3",
description: "Description 3",
price: 30,
stock: 300,
category: "TechnicalComponents",
rating: 4.4,
discount: 15,
},
{
id: "211",
name: "Item 3",
description: "Description 3",
price: 30,
stock: 300,
category: "TechnicalComponents",
rating: 4.0,
discount: 15,
}, },
{ {
id: "212", id: "212",
@@ -329,6 +430,9 @@ export default function Home() {
// Intersection Observer Ref // Intersection Observer Ref
const loaderRef = useRef<HTMLDivElement | null>(null); const loaderRef = useRef<HTMLDivElement | null>(null);
// Container Ref
const containerRef = useRef<HTMLDivElement>(null);
// Callback für Observer // Callback für Observer
const handleObserver = useCallback( const handleObserver = useCallback(
(entries: IntersectionObserverEntry[]) => { (entries: IntersectionObserverEntry[]) => {
@@ -349,7 +453,7 @@ export default function Home() {
const option = { const option = {
root: null, root: null,
rootMargin: "20px", rootMargin: "20px",
threshold: 1.0, threshold: 1,
}; };
const observer = new IntersectionObserver(handleObserver, option); const observer = new IntersectionObserver(handleObserver, option);
if (loaderRef.current) observer.observe(loaderRef.current); if (loaderRef.current) observer.observe(loaderRef.current);
@@ -361,8 +465,27 @@ export default function Home() {
// Reset itemsToShow bei Filteränderungen // Reset itemsToShow bei Filteränderungen
useEffect(() => { useEffect(() => {
setItemsToShow(20); setItemsToShow(20);
requestAnimationFrame(() => {
containerRef.current?.scrollTo(0, 0);
});
}, [selectedCategory, selectedRating, priceRange]); }, [selectedCategory, selectedRating, priceRange]);
useEffect(() => {
if (items.length > 0) {
setTimeout(() => {
containerRef.current?.scrollTo(0, 0);
}, 50);
}
}, [items]);
useEffect(() => {
console.log("Container Höhe:", containerRef.current?.scrollHeight);
console.log("Container sichtbare Höhe:", containerRef.current?.clientHeight);
console.log("page-background Scrollhöhe:", document.querySelector(".page-background")?.scrollHeight);
console.log("page-background Sichtbare Höhe:", document.querySelector(".page-background")?.clientHeight);
}, []);
// Kategorie-Änderung // Kategorie-Änderung
const handleCategoryChange = (category: string) => { const handleCategoryChange = (category: string) => {
if (category === "") { if (category === "") {
@@ -406,7 +529,7 @@ export default function Home() {
onChange={handleRatingChange} onChange={handleRatingChange}
/> />
</div> </div>
<div className="page-background"> <div className="page-background" ref={containerRef}>
<Box className="cardgrid"> <Box className="cardgrid">
{visibleItems.map((item) => ( {visibleItems.map((item) => (
<ItemCard key={item.id} item={item} /> <ItemCard key={item.id} item={item} />
@@ -416,7 +539,6 @@ export default function Home() {
{/* Loader für Intersection Observer */} {/* Loader für Intersection Observer */}
<div ref={loaderRef} className="loader-container"> <div ref={loaderRef} className="loader-container">
{/* Hier kann ein Lade-Spinner rein, wenn gewünscht */} {/* Hier kann ein Lade-Spinner rein, wenn gewünscht */}
{itemsToShow < filteredItems.length ? "Laden..." : "Keine weiteren Items"}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -44,13 +44,13 @@
.cardgrid { .cardgrid {
display: grid; display: grid;
gap: 2%; gap: 24px;
width: 90%; width: 90%;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
padding: 16px; padding: 16px;
margin: 0 auto; margin-right: auto;
margin-left: auto;
box-sizing: border-box; box-sizing: border-box;
padding-bottom: 50px;
} }
.page-background { .page-background {
@@ -61,18 +61,12 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
overflow-y: auto; overflow: auto;
padding: 20px 0; /* Abstand oben und unten */ padding: 20px 0; /* Abstand oben und unten */
box-sizing: border-box; /* Stellt sicher, dass Padding in die Höhe einbezogen wird */ box-sizing: border-box; /* Stellt sicher, dass Padding in die Höhe einbezogen wird */
width: 100%; /* Damit der Hintergrund die gesamte Breite abdeckt */ width: 100%; /* Damit der Hintergrund die gesamte Breite abdeckt */
} }
.pagination {
display: flex;
justify-content: center;
flex-shrink: 0;
}
.impressum-container { .impressum-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -127,7 +121,8 @@
display: flex; display: flex;
align-items: stretch; align-items: stretch;
width: 100%; width: 100%;
overflow: auto; overflow: auto !important;
scroll-behavior: smooth;
height: calc(100vh - 3rem); /* Damit der Hintergrund die gesamte Seite abdeckt */ height: calc(100vh - 3rem); /* Damit der Hintergrund die gesamte Seite abdeckt */
min-height: 600px; min-height: 600px;
/* padding: 20px 0 40px 0; /* Abstand oben und unten */ /* padding: 20px 0 40px 0; /* Abstand oben und unten */
@@ -138,10 +133,7 @@
.filter-container { .filter-container {
width: 17%; width: 17%;
display: grid; display: grid;
margin-left: 20px; margin: 20px -20px 20px 20px;
margin-right: -20px;
margin-top: 20px;
margin-bottom: 20px;
place-self: start; place-self: start;
} }
@@ -149,5 +141,5 @@
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-top: 60px; /* Abstand zu den Items */ margin-top: 24px;
} }