184 lines
6.4 KiB
TypeScript
184 lines
6.4 KiB
TypeScript
import { Alert, Box, useTheme } from "@mui/material";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useLocation, useNavigate } from "react-router-dom";
|
|
import ItemWithImage from "../components/Item";
|
|
import FilterItem from "../helper/homepage/FilterItem";
|
|
import ItemCard from "../helper/homepage/ItemCard";
|
|
import PriceSlider from "../helper/homepage/PriceSlider";
|
|
import { fetchItemListWithImage } from '../helper/query/Queries';
|
|
import "./pages.css"; // Import der CSS-Datei
|
|
|
|
export default function Home() {
|
|
|
|
const { t } = useTranslation();
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const theme = useTheme();
|
|
const [searchQuery, setSearchQuery] = useState<string | null>(null);
|
|
|
|
const categoriesFilter = useMemo(() => [
|
|
{ value: "", label: t("allCategories") },
|
|
{ value: "Seeds", label: t("seeds") },
|
|
{ value: "GardenSupplies", label: t("gardenSupplies") },
|
|
{ value: "TechnicalComponents", label: t("technicalComponents") },
|
|
{ value: "Other", label: t("other") }
|
|
], [t]);
|
|
|
|
const ratingFilter = [
|
|
{ value: "", label: t('allRatings') },
|
|
...[5, 4, 3, 2, 1].map(value => ({
|
|
value: value.toString(),
|
|
label: value.toString()
|
|
}))
|
|
];
|
|
|
|
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
|
const [selectedRating, setSelectedRating] = useState<string | null>(null);
|
|
|
|
const { data = [] } = useQuery<ItemWithImage[]>({
|
|
queryKey: ['fetchItemListWithImage'],
|
|
queryFn: fetchItemListWithImage,
|
|
retry: 3, // Versucht es 3-mal erneut
|
|
retryDelay: 1000, // Wartezeit zwischen den Versuchen (in ms)
|
|
});
|
|
|
|
const items:ItemWithImage[] = useMemo(() => data || [], [data]);
|
|
|
|
const discountedPrices = items.map(
|
|
(item) => item.price100 * (1 - item.discount100 / 100)
|
|
);
|
|
const minPrice = discountedPrices.length > 0 ? Math.min(...discountedPrices) : 0;
|
|
const maxPrice = discountedPrices.length > 0 ? Math.max(...discountedPrices) : 1000;
|
|
const [priceRange, setPriceRange] = useState<[number, number]>([
|
|
minPrice,
|
|
maxPrice,
|
|
]);
|
|
|
|
// Filter aus URL übernehmen
|
|
useEffect(() => {
|
|
const params = new URLSearchParams(location.search);
|
|
const category = params.get("category");
|
|
if (category && categoriesFilter.some((f) => f.value === category)) {
|
|
setSelectedCategory(category);
|
|
} else {
|
|
setSelectedCategory(null);
|
|
}
|
|
}, [location.search, categoriesFilter]);
|
|
|
|
// Filterfunktion bleibt gleich
|
|
const filteredItems: ItemWithImage[] = useMemo(() => {return items
|
|
.filter((item) => {
|
|
const discountedPrice = item.price100 * (1 - item.discount100 / 100);
|
|
return discountedPrice >= priceRange[0] && discountedPrice <= priceRange[1];
|
|
})
|
|
.filter((item) => {
|
|
if (!selectedCategory) return true;
|
|
return item.category.toLowerCase() === selectedCategory.toLowerCase();
|
|
})
|
|
.filter((item) => {
|
|
if (!selectedRating) return true;
|
|
const rating = Math.trunc(item.rating);
|
|
return rating === (Number(selectedRating) * 2) -1 || rating === (Number(selectedRating) * 2);
|
|
})
|
|
.filter((item) => {
|
|
if (!searchQuery) return true;
|
|
return (item.name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
);
|
|
});
|
|
}, [items, priceRange, selectedCategory, selectedRating, searchQuery]);
|
|
|
|
|
|
// Lese die Suchanfrage aus der URL
|
|
useEffect(() => {
|
|
const params = new URLSearchParams(location.search);
|
|
const query = params.get("search");
|
|
setSearchQuery(query);
|
|
}, [location.search]);
|
|
|
|
|
|
|
|
// Items, die aktuell angezeigt werden
|
|
const visibleItems: ItemWithImage[] = filteredItems;
|
|
|
|
// Container Ref
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
const prevItemsLength = useRef(items.length);
|
|
|
|
useEffect(() => {
|
|
if (items.length >= prevItemsLength.current) {
|
|
prevItemsLength.current = items.length;
|
|
return;
|
|
}
|
|
|
|
setTimeout(() => {
|
|
containerRef.current?.scrollTo(0, 0);
|
|
}, 50);
|
|
|
|
prevItemsLength.current = items.length;
|
|
}, [items]);
|
|
|
|
// Kategorie-Änderung
|
|
const handleCategoryChange = (category: string) => {
|
|
if (category === "") {
|
|
setSelectedCategory(null);
|
|
navigate(`/`);
|
|
} else {
|
|
setSelectedCategory(category);
|
|
navigate(`/?category=${encodeURIComponent(category)}`);
|
|
}
|
|
};
|
|
|
|
// Rating-Änderung (bleibt gleich)
|
|
const handleRatingChange = (rating: string) => {
|
|
if (rating === "") {
|
|
setSelectedRating(null);
|
|
} else {
|
|
setSelectedRating(rating);
|
|
}
|
|
};
|
|
|
|
|
|
return (
|
|
<div
|
|
className="home-page-background"
|
|
style={{ backgroundColor: theme.palette.homepage }}
|
|
>
|
|
<div className="sidebar sidebar-filter">
|
|
<FilterItem
|
|
filterName={t("category")}
|
|
filterItems={categoriesFilter}
|
|
value={selectedCategory}
|
|
onChange={handleCategoryChange}
|
|
/>
|
|
<PriceSlider
|
|
min={minPrice}
|
|
max={maxPrice}
|
|
onChange={(range) => {
|
|
setPriceRange(range);
|
|
}}
|
|
/>
|
|
<FilterItem
|
|
filterName={t("rating")}
|
|
filterItems={ratingFilter}
|
|
value={selectedRating}
|
|
onChange={handleRatingChange}
|
|
/>
|
|
</div>
|
|
<div className="page-background page-background-center" ref={containerRef}>
|
|
<Box className="cardgrid">
|
|
{visibleItems.length === 0 ? (
|
|
<Alert variant="filled" severity="warning" className="no-results">{t('noItemsFound')}</Alert>
|
|
) : (
|
|
visibleItems.map((item) => (
|
|
<ItemCard key={item.id} item={item} />
|
|
))
|
|
)}
|
|
</Box>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|