refactor: add sorting and filtering to search box
This commit is contained in:
parent
5e34d9f68d
commit
28ac29a21c
|
@ -1,6 +1,10 @@
|
|||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { CustomLink } from './styled/views/Home.styled';
|
||||
import { useResetRecoilState } from 'recoil';
|
||||
import cartState from '../atoms/shop/cartState';
|
||||
import cartCountState from '../atoms/shop/cartCountState';
|
||||
import productsState from '../atoms/shop/productsState';
|
||||
|
||||
const SidebarContainer = styled.div`
|
||||
width: 250px;
|
||||
|
@ -33,11 +37,29 @@ const ToggleButton = styled.button`
|
|||
|
||||
function SidebarMenu() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const customer_id = localStorage.getItem('customer_id');
|
||||
const resetCart = useResetRecoilState(cartState);
|
||||
const resetCartCount = useResetRecoilState(cartCountState);
|
||||
const resetProduct = useResetRecoilState(productsState);
|
||||
|
||||
const toggleSidebar = () => {
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
const handleSignOut = () => {
|
||||
localStorage.removeItem('customer_id');
|
||||
localStorage.removeItem('cartId');
|
||||
localStorage.removeItem('login_token');
|
||||
localStorage.removeItem('email');
|
||||
localStorage.removeItem('jwt');
|
||||
|
||||
resetCart();
|
||||
resetCartCount();
|
||||
resetProduct();
|
||||
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToggleButton isOpen={isOpen} onClick={toggleSidebar}>
|
||||
|
@ -50,6 +72,13 @@ function SidebarMenu() {
|
|||
<CustomLink to='/shop'>
|
||||
<MenuItem>Shop</MenuItem>
|
||||
</CustomLink>
|
||||
{customer_id?.length > 0 ? (
|
||||
<MenuItem onClick={handleSignOut}>Sign Out</MenuItem>
|
||||
) : (
|
||||
<CustomLink to='/sign-in'>
|
||||
<MenuItem>Sign In</MenuItem>
|
||||
</CustomLink>
|
||||
)}
|
||||
</SidebarContainer>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -46,16 +46,17 @@ export const Logo = styled.div`
|
|||
`;
|
||||
|
||||
export const Search = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
input[type='text'] {
|
||||
width: 400px;
|
||||
height: 30px;
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
padding: 3px 10px;
|
||||
box-sizing: border-box;
|
||||
-webkit-appearance: none;
|
||||
-webkit-border-radius: 0;
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
|
@ -94,9 +95,9 @@ export const Search = styled.div`
|
|||
text-align: center;
|
||||
|
||||
input[type='text'] {
|
||||
width: 80%;
|
||||
width: 220px;
|
||||
height: 35px;
|
||||
font-size: 20px;
|
||||
font-size: 14px;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 0;
|
||||
|
@ -479,3 +480,29 @@ export const LoadingContainer = styled.div`
|
|||
font-size: 20px;
|
||||
padding: 20px;
|
||||
`;
|
||||
|
||||
export const SearchContainer = styled.div`
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
`;
|
||||
|
||||
export const AutocompleteList = styled.ul`
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
width: calc(100% - 4px);
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
`;
|
||||
|
||||
export const ListItem = styled.li`
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -13,6 +13,9 @@ import {
|
|||
BoardsContent,
|
||||
LoadingContainer,
|
||||
CustomLink,
|
||||
SearchContainer,
|
||||
AutocompleteList,
|
||||
ListItem,
|
||||
} from '../styled/views/Home.styled';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Product from '../Shop/Product';
|
||||
|
@ -27,12 +30,17 @@ import SocialMedia from '../Shop/SocialMedia';
|
|||
|
||||
const Shop = () => {
|
||||
const inputRef = useRef(null);
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [products, setProducts] = useRecoilState(productsState);
|
||||
const [items, setItems] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [suggestions, setSuggestions] = useState([]);
|
||||
|
||||
const customerEmail = localStorage.getItem('email') ?? '';
|
||||
|
||||
const [sortType, setSortType] = useState('asc');
|
||||
|
||||
useEffect(() => {
|
||||
const fetchProducts = async () => {
|
||||
try {
|
||||
|
@ -40,6 +48,8 @@ const Shop = () => {
|
|||
|
||||
setLoading(false);
|
||||
setProducts(res?.data);
|
||||
|
||||
setItems(res?.data);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
|
@ -49,11 +59,45 @@ const Shop = () => {
|
|||
fetchProducts();
|
||||
}, []);
|
||||
|
||||
const handleSearchSubmit = () => {
|
||||
setKeyword(inputRef.current.value);
|
||||
const handleSearchSubmit = (keyword) => {
|
||||
if (!keyword?.length) {
|
||||
setItems(products);
|
||||
} else {
|
||||
const filteredProducts = products.filter((product) => product.name.toLowerCase().includes(keyword.toLowerCase()));
|
||||
setItems(filteredProducts);
|
||||
}
|
||||
setSuggestions([]);
|
||||
};
|
||||
|
||||
const items = products.filter((product) => product.name.toLowerCase().includes(keyword.toLowerCase()));
|
||||
const onTextChanged = (e) => {
|
||||
const value = e.target.value;
|
||||
setSearchTerm(value);
|
||||
|
||||
if (value.length > 0) {
|
||||
const regex = new RegExp(`^${value}`, 'i');
|
||||
setSuggestions(products.filter((product) => regex.test(product.name)));
|
||||
} else {
|
||||
setSuggestions([]);
|
||||
}
|
||||
};
|
||||
|
||||
const suggestionSelected = (value) => {
|
||||
handleSearchSubmit(value);
|
||||
setSearchTerm(value);
|
||||
setSuggestions([]);
|
||||
};
|
||||
|
||||
const handleSort = () => {
|
||||
if (sortType === 'asc') {
|
||||
setSortType('desc');
|
||||
const sortedItems = [...items].sort((a, b) => (a.name < b.name ? 1 : -1));
|
||||
setItems(sortedItems);
|
||||
} else {
|
||||
setSortType('asc');
|
||||
const sortedItems = [...items].sort((a, b) => (a.name > b.name ? 1 : -1));
|
||||
setItems(sortedItems);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -74,9 +118,21 @@ const Shop = () => {
|
|||
</AboutContent>
|
||||
<Page>
|
||||
<Search>
|
||||
<input type='text' placeholder='Search products..' ref={inputRef} />
|
||||
<input type='submit' value='Search' onClick={handleSearchSubmit} />
|
||||
<SearchContainer>
|
||||
<input type='text' placeholder='Search products..' value={searchTerm} ref={inputRef} onChange={onTextChanged} />
|
||||
{suggestions.length > 0 && (
|
||||
<AutocompleteList>
|
||||
{suggestions.map((item) => (
|
||||
<ListItem key={item.id} onClick={() => suggestionSelected(item.name)}>
|
||||
{item.name}
|
||||
</ListItem>
|
||||
))}
|
||||
</AutocompleteList>
|
||||
)}
|
||||
</SearchContainer>
|
||||
<input type='submit' value='Search' onClick={() => handleSearchSubmit(searchTerm)} />
|
||||
</Search>
|
||||
|
||||
<About>
|
||||
<AboutTitle>
|
||||
<h2>AcidChan Webshop</h2>
|
||||
|
@ -107,6 +163,8 @@ const Shop = () => {
|
|||
<div className='boxbar'>
|
||||
<h2>Products</h2>
|
||||
</div>
|
||||
<button onClick={handleSort}>Sort: {sortType === 'asc' ? 'ASC' : 'DESC'}</button>
|
||||
<br />
|
||||
<BoardsContent>
|
||||
{loading ? (
|
||||
<LoadingContainer>Loading..</LoadingContainer>
|
||||
|
@ -123,7 +181,7 @@ const Shop = () => {
|
|||
<h2>Featured</h2>
|
||||
</div>
|
||||
<BoardsContent>
|
||||
{loading ? <LoadingContainer>Loading..</LoadingContainer> : items.slice(0, 4).map((product) => <Product key={product.id} product={product} />)}
|
||||
{loading ? <LoadingContainer>Loading..</LoadingContainer> : products.slice(0, 4).map((product) => <Product key={product.id} product={product} />)}
|
||||
</BoardsContent>
|
||||
</BoardsBox>
|
||||
<br />
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Header, Logo, AboutContent, CustomLink } from '../styled/views/Home.sty
|
|||
import { Link } from 'react-router-dom';
|
||||
import { Container, Form, Input, Button } from '../styled/Auth.styled';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import SidebarMenu from '../SidebarMenu';
|
||||
|
||||
const SignIn = () => {
|
||||
const [email, setEmail] = useState('');
|
||||
|
@ -57,6 +58,7 @@ const SignIn = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<SidebarMenu />
|
||||
<AboutContent>
|
||||
<Header>
|
||||
<Logo>
|
||||
|
|
|
@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
|||
import { Header, Logo, AboutContent, CustomLink } from '../styled/views/Home.styled';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Container, Form, Input, Button } from '../styled/Auth.styled';
|
||||
import SidebarMenu from '../SidebarMenu';
|
||||
|
||||
const SignUp = () => {
|
||||
const [email, setEmail] = useState('');
|
||||
|
@ -10,7 +11,7 @@ const SignUp = () => {
|
|||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
console.log('Attempting to Sign In', { email, password });
|
||||
|
||||
const base_url = window.location.origin;
|
||||
|
||||
try {
|
||||
|
@ -44,6 +45,7 @@ const SignUp = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<SidebarMenu />
|
||||
<AboutContent>
|
||||
<Header>
|
||||
<Logo>
|
||||
|
|
Loading…
Reference in New Issue