refactor: add sorting and filtering to search box

This commit is contained in:
jinhojang6 2024-03-07 01:46:32 +09:00
parent 5e34d9f68d
commit 28ac29a21c
6 changed files with 130 additions and 12 deletions

View File

@ -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>
</>
);

View File

@ -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;
}
`;

View File

@ -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 />

View File

@ -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>

View File

@ -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>