Added working search bar with autocomplete in NavBar.
This commit is contained in:
@@ -1,58 +1,25 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import AppBar from '@mui/material/AppBar';
|
|
||||||
import Box from '@mui/material/Box';
|
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
|
||||||
import IconButton from '@mui/material/IconButton';
|
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import Menu from '@mui/material/Menu';
|
|
||||||
import MenuIcon from '@mui/icons-material/Menu';
|
|
||||||
import Avatar from '@mui/material/Avatar';
|
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
|
||||||
import AdbIcon from '@mui/icons-material/Adb';
|
import AdbIcon from '@mui/icons-material/Adb';
|
||||||
import { alpha, InputBase, styled } from '@mui/material';
|
import MenuIcon from '@mui/icons-material/Menu';
|
||||||
import SearchIcon from '@mui/icons-material/Search';
|
import SearchIcon from '@mui/icons-material/Search';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { Autocomplete, TextField } from '@mui/material';
|
||||||
|
import AppBar from '@mui/material/AppBar';
|
||||||
|
import Avatar from '@mui/material/Avatar';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import Menu from '@mui/material/Menu';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import * as React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import './NavBar.css';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import Item from '../../components/Item';
|
||||||
import ThemeToggle from '../../theme/ThemeToggle';
|
import ThemeToggle from '../../theme/ThemeToggle';
|
||||||
|
import { fetchItemList } from '../query/Queries';
|
||||||
const Search = styled('div')(({ theme }) => ({
|
import './NavBar.css';
|
||||||
position: 'relative',
|
|
||||||
borderRadius: theme.shape.borderRadius,
|
|
||||||
backgroundColor: alpha(theme.palette.common.white, 0.15),
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor: alpha(theme.palette.common.white, 0.25),
|
|
||||||
},
|
|
||||||
marginLeft: 0,
|
|
||||||
width: '100%',
|
|
||||||
[theme.breakpoints.up('sm')]: {
|
|
||||||
marginLeft: theme.spacing(1),
|
|
||||||
width: 'auto',
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const SearchIconWrapper = styled('div')(({ theme }) => ({
|
|
||||||
padding: theme.spacing(0, 2),
|
|
||||||
height: '100%',
|
|
||||||
position: 'absolute',
|
|
||||||
pointerEvents: 'none',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledInputBase = styled(InputBase)(({ theme }) => ({
|
|
||||||
color: 'white',
|
|
||||||
flexGrow: 1,
|
|
||||||
maxWidth: '100%',
|
|
||||||
'& .MuiInputBase-input': {
|
|
||||||
padding: theme.spacing(1, 1, 1, 0),
|
|
||||||
paddingLeft: `calc(1em + ${theme.spacing(3)})`,
|
|
||||||
transition: "none"
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
export default function NavBar() {
|
export default function NavBar() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -60,6 +27,8 @@ export default function NavBar() {
|
|||||||
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(null);
|
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(null);
|
||||||
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(null);
|
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(null);
|
||||||
|
|
||||||
|
const [itemNames, setItemNames] = React.useState<string[]>([]); // Für Autocomplete
|
||||||
|
|
||||||
const pageKeys = ['categories', 'checkout', 'contact'];
|
const pageKeys = ['categories', 'checkout', 'contact'];
|
||||||
const settingKeys = ['account', 'orders', 'logout'];
|
const settingKeys = ['account', 'orders', 'logout'];
|
||||||
|
|
||||||
@@ -84,9 +53,31 @@ export default function NavBar() {
|
|||||||
navigate(`/${link.toLowerCase()}`);
|
navigate(`/${link.toLowerCase()}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// useQuery, um die Item-Namen zu laden
|
||||||
|
const { data: items = [] } = useQuery<Item[]>({
|
||||||
|
queryKey: ["fetchItemList"],
|
||||||
|
queryFn: fetchItemList,
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
// Extrahiere die Namen der Items für Autocomplete
|
||||||
|
setItemNames(items.map((item) => item.name));
|
||||||
|
}, [items]);
|
||||||
|
|
||||||
|
const handleSearch = (event: React.SyntheticEvent, value: string | null) => {
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
// Wenn der Suchwert leer ist, navigiere zur Homepage ohne Suchparameter
|
||||||
|
navigate("/");
|
||||||
|
} else {
|
||||||
|
// Navigiere zur Homepage mit dem Suchparameter
|
||||||
|
navigate(`/?search=${encodeURIComponent(value)}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar position="static" color="primary" elevation={4}>
|
<AppBar position="static" color="primary" elevation={4}>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
disableGutters
|
disableGutters
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -94,38 +85,45 @@ export default function NavBar() {
|
|||||||
px: 3
|
px: 3
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center", minWidth: "0px" }}>
|
<Box sx={{ display: "flex", alignItems: "center", minWidth: "0px" }}>
|
||||||
<AdbIcon className='navbar-logo' />
|
<AdbIcon className='navbar-logo' />
|
||||||
<Typography
|
<Typography
|
||||||
variant="h6"
|
variant="h6"
|
||||||
noWrap
|
noWrap
|
||||||
component="a"
|
component="a"
|
||||||
href="/"
|
href="/"
|
||||||
sx={{
|
sx={{
|
||||||
fontFamily: "monospace",
|
fontFamily: "monospace",
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
letterSpacing: ".3rem",
|
letterSpacing: ".3rem",
|
||||||
color: "white",
|
color: "white",
|
||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
ml: 1,
|
ml: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Digitaler Produktionsshop
|
Digitaler Produktionsshop
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ flexGrow: 1, display: "flex", justifyContent: "center", px: 3 }}>
|
<Box sx={{ flexGrow: 1, display: "flex", justifyContent: "center", px: 3 }}>
|
||||||
<Search sx={{ flexGrow: 1, minWidth: "150px", maxWidth: "600px" }}>
|
<Autocomplete
|
||||||
<SearchIconWrapper>
|
freeSolo
|
||||||
<SearchIcon sx={{ color: "white" }} />
|
options={itemNames} // Item-Namen für Autocomplete
|
||||||
</SearchIconWrapper>
|
onInputChange={handleSearch} // Suche auslösen
|
||||||
<StyledInputBase
|
renderInput={(params) => (
|
||||||
placeholder={t('search') + "..."}
|
<TextField
|
||||||
inputProps={{ "aria-label": t('search') }}
|
{...params}
|
||||||
sx={{ width: "100%" }}
|
placeholder={t("search") + "..."}
|
||||||
|
InputProps={{
|
||||||
|
...params.InputProps,
|
||||||
|
startAdornment: (
|
||||||
|
<SearchIcon sx={{ color: "white", mr: 1 }} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Search>
|
)}
|
||||||
</Box>
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: 2, marginLeft: 'auto' }}>
|
<Box sx={{ display: "flex", alignItems: "center", gap: 2, marginLeft: 'auto' }}>
|
||||||
<Box sx={{ display: { xs: "none", md: "flex" }, gap: 2 }}>
|
<Box sx={{ display: { xs: "none", md: "flex" }, gap: 2 }}>
|
||||||
@@ -184,7 +182,7 @@ export default function NavBar() {
|
|||||||
))}
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
</Box>
|
</Box>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default function ProductInfo({ item }: { item: Item }) {
|
|||||||
console.log(`Added {quantity} of €{item.name} to basket`);
|
console.log(`Added {quantity} of €{item.name} to basket`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const discountedPrice = item.price * (1 - item.discount / 100);
|
const discountedPrice = item.price100 * (1 - item.discount100 / 100);
|
||||||
|
|
||||||
const handleImageLoad = (event: React.SyntheticEvent<HTMLImageElement>) => {
|
const handleImageLoad = (event: React.SyntheticEvent<HTMLImageElement>) => {
|
||||||
const { naturalWidth, naturalHeight } = event.currentTarget;
|
const { naturalWidth, naturalHeight } = event.currentTarget;
|
||||||
@@ -89,7 +89,7 @@ export default function ProductInfo({ item }: { item: Item }) {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Stack direction="row" alignItems="center" spacing={2}>
|
<Stack direction="row" alignItems="center" spacing={2}>
|
||||||
{item.discount > 0 ? (
|
{item.discount100 > 0 ? (
|
||||||
<>
|
<>
|
||||||
<Typography variant="h4" color="green">
|
<Typography variant="h4" color="green">
|
||||||
{discountedPrice.toFixed(2)} €
|
{discountedPrice.toFixed(2)} €
|
||||||
@@ -99,15 +99,15 @@ export default function ProductInfo({ item }: { item: Item }) {
|
|||||||
color="text.secondary"
|
color="text.secondary"
|
||||||
sx={{ textDecoration: 'line-through' }}
|
sx={{ textDecoration: 'line-through' }}
|
||||||
>
|
>
|
||||||
{item.price.toFixed(2)} €
|
{item.price100.toFixed(2)} €
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h6" color="error">
|
<Typography variant="h6" color="error">
|
||||||
-{item.discount} %
|
-{item.discount100} %
|
||||||
</Typography>
|
</Typography>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Typography variant="h4" color="green">
|
<Typography variant="h4" color="green">
|
||||||
{item.price.toFixed(2)} €
|
{item.price100.toFixed(2)} €
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export default function Home() {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const [searchQuery, setSearchQuery] = useState<string | null>(null);
|
||||||
|
|
||||||
const categoriesFilter = useMemo(() => [
|
const categoriesFilter = useMemo(() => [
|
||||||
{ value: "", label: t("all") },
|
{ value: "", label: t("all") },
|
||||||
@@ -67,7 +68,7 @@ export default function Home() {
|
|||||||
}, [location.search, categoriesFilter]);
|
}, [location.search, categoriesFilter]);
|
||||||
|
|
||||||
// Filterfunktion bleibt gleich
|
// Filterfunktion bleibt gleich
|
||||||
const filteredItems = items
|
const filteredItems = useMemo(() => {return items
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
const discountedPrice = item.price100 * (1 - item.discount100 / 100);
|
const discountedPrice = item.price100 * (1 - item.discount100 / 100);
|
||||||
return discountedPrice >= priceRange[0] && discountedPrice <= priceRange[1];
|
return discountedPrice >= priceRange[0] && discountedPrice <= priceRange[1];
|
||||||
@@ -79,7 +80,21 @@ export default function Home() {
|
|||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
if (!selectedRating) return true;
|
if (!selectedRating) return true;
|
||||||
return Math.round(item.rating) >= Number(selectedRating);
|
return Math.round(item.rating) >= Number(selectedRating);
|
||||||
|
})
|
||||||
|
.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]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user