-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7cfff14
commit b305f89
Showing
4 changed files
with
396 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import * as React from 'react'; | ||
import Box from '@mui/material/Box'; | ||
import CssBaseline from '@mui/material/CssBaseline'; | ||
import BottomNavigation from '@mui/material/BottomNavigation'; | ||
import BottomNavigationAction from '@mui/material/BottomNavigationAction'; | ||
import HomeIcon from '@mui/icons-material/Home'; | ||
import SearchIcon from '@mui/icons-material/Search'; | ||
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; | ||
import PersonIcon from '@mui/icons-material/Person'; | ||
import Paper from '@mui/material/Paper'; | ||
import { useMediaQuery } from '@mui/material'; | ||
import Badge, { BadgeProps } from '@mui/material/Badge'; | ||
import { styled } from '@mui/material/styles'; | ||
import { useTheme } from '@mui/material'; | ||
import { useLocation, useNavigate } from 'react-router-dom'; | ||
import { useCart } from '../../lib/store'; | ||
|
||
export default function MobileNavBar() { | ||
const theme = useTheme(); | ||
const { items: cartItems } = useCart(); | ||
const cartItemsCount = cartItems.length; | ||
const matchesXS = useMediaQuery(theme.breakpoints.down('sm')); | ||
const navigate = useNavigate(); | ||
const location = useLocation(); | ||
|
||
const routeToValueMap: { [key: string]: number } = { | ||
'/': 0, | ||
'/search': 1, | ||
'/cart': 2, | ||
'/user': 3, | ||
}; | ||
|
||
const currentValue = routeToValueMap[location.pathname]; | ||
|
||
const handleChange = (_: React.ChangeEvent<{}>, newValue: number) => { | ||
switch (newValue) { | ||
case 0: | ||
navigate('/'); | ||
break; | ||
case 1: | ||
navigate('/search'); | ||
break; | ||
case 2: | ||
navigate('/cart'); | ||
break; | ||
case 3: | ||
navigate('/user'); | ||
break; | ||
} | ||
}; | ||
|
||
return ( | ||
<Box sx={{ pb: 7 }}> | ||
<CssBaseline /> | ||
{matchesXS && ( | ||
<Paper | ||
sx={{ | ||
position: 'fixed', | ||
bottom: 0, | ||
left: 0, | ||
right: 0, | ||
borderTop: '1px solid rgba(0, 0, 0, 0.12)', | ||
}} | ||
elevation={0} | ||
> | ||
<BottomNavigation | ||
value={currentValue} | ||
onChange={handleChange} | ||
showLabels | ||
> | ||
<BottomNavigationAction label="홈" icon={<HomeIcon />} /> | ||
<BottomNavigationAction label="검색" icon={<SearchIcon />} /> | ||
<BottomNavigationAction | ||
label="장바구니" | ||
icon={ | ||
<StyledBadge badgeContent={cartItemsCount} color="error"> | ||
<ShoppingCartIcon /> | ||
</StyledBadge> | ||
} | ||
/> | ||
<BottomNavigationAction label="마이" icon={<PersonIcon />} /> | ||
</BottomNavigation> | ||
</Paper> | ||
)} | ||
</Box> | ||
); | ||
} | ||
|
||
const StyledBadge = styled(Badge)<BadgeProps>(({ theme }) => ({ | ||
'& .MuiBadge-badge': { | ||
right: -3, | ||
top: 13, | ||
border: `2px solid ${theme.palette.background.paper}`, | ||
padding: '0 4px', | ||
}, | ||
})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import Box from '@mui/material/Box'; | ||
import CssBaseline from '@mui/material/CssBaseline'; | ||
import Button from '@mui/material/Button'; | ||
import Paper from '@mui/material/Paper'; | ||
import { useMediaQuery, useTheme } from '@mui/material'; | ||
import { useNavigate } from 'react-router-dom'; | ||
|
||
export default function MobilePaymentBar({ totalCost }: { totalCost: number }) { | ||
const theme = useTheme(); | ||
const matchesXS = useMediaQuery(theme.breakpoints.down('sm')); | ||
const navigate = useNavigate(); | ||
|
||
const handlePaymentClick = () => { | ||
navigate('/checkout'); | ||
}; | ||
|
||
const formattedTotalCost = totalCost.toLocaleString('ko-KR', { | ||
style: 'currency', | ||
currency: 'KRW', | ||
}); | ||
|
||
return ( | ||
<Box sx={{ pb: 7 }}> | ||
<CssBaseline /> | ||
{matchesXS && ( | ||
<Paper | ||
sx={{ | ||
position: 'fixed', | ||
bottom: 0, // Height of your mobile nav bar | ||
left: 0, | ||
right: 0, | ||
display: 'flex', | ||
justifyContent: 'center', | ||
borderRadius: 0, | ||
borderTop: '1px solid rgba(0, 0, 0, 0.12)', | ||
height: '70px', | ||
}} | ||
elevation={0} | ||
> | ||
<Button | ||
variant="contained" | ||
sx={{ | ||
margin: theme.spacing(1), | ||
backgroundColor: 'primary', // Match the background color to your design | ||
color: '#fff', // Match the text color to your design | ||
width: 'calc(100% - 16px)', // Full width with padding | ||
maxWidth: theme.breakpoints.values.sm, // Max width at small breakpoint | ||
fontSize: '1rem', | ||
fontWeight: 700, | ||
}} | ||
onClick={handlePaymentClick} | ||
> | ||
{formattedTotalCost} 주문하기 | ||
</Button> | ||
</Paper> | ||
)} | ||
</Box> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import Box from '@mui/material/Box'; | ||
import Button from '@mui/material/Button'; | ||
import { styled } from '@mui/material/styles'; | ||
import FavoriteIcon from '@mui/icons-material/Favorite'; | ||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder'; | ||
import ShoppingCartOutlinedIcon from '@mui/icons-material/ShoppingCartOutlined'; | ||
import PaymentIcon from '@mui/icons-material/Payment'; | ||
import useBookmark from '../../hooks/useBookmark'; | ||
import useAddToCart from '../../hooks/useAddToCart'; | ||
|
||
const StyledButton = styled(Button)(({ theme }) => ({ | ||
flexGrow: 1, | ||
margin: theme.spacing(1), | ||
maxWidth: '100%', | ||
})); | ||
|
||
interface ActionBarProps { | ||
product: any; | ||
userId: any; | ||
productId: any; | ||
handlePurchase: any; | ||
isLoggedIn: boolean; | ||
} | ||
|
||
export default function MobileProductActionBar({ | ||
product, | ||
userId, | ||
productId, | ||
handlePurchase, | ||
isLoggedIn, | ||
}: ActionBarProps) { | ||
const { isBookmarked, addBookmark, removeBookmark } = useBookmark( | ||
userId, | ||
productId, | ||
); | ||
const addProductToCart = useAddToCart(); | ||
|
||
const handleToggleBookmark = () => { | ||
if (!isLoggedIn) { | ||
return; | ||
} else { | ||
if (isBookmarked) { | ||
removeBookmark(); | ||
alert('북마크가 삭제되었습니다.'); | ||
} else { | ||
addBookmark(); | ||
alert('북마크가 추가되었습니다.'); | ||
} | ||
} | ||
}; | ||
|
||
return ( | ||
<Box | ||
sx={{ | ||
position: 'fixed', | ||
bottom: 0, | ||
left: 0, | ||
right: 0, | ||
display: 'flex', | ||
justifyContent: 'space-around', | ||
borderTop: '1px solid rgba(0, 0, 0, 0.12)', | ||
backgroundColor: 'white', | ||
zIndex: 1100, | ||
}} | ||
> | ||
<StyledButton | ||
variant="contained" | ||
startIcon={<PaymentIcon />} | ||
onClick={handlePurchase} | ||
> | ||
구매하기 | ||
</StyledButton> | ||
<StyledButton | ||
variant="outlined" | ||
startIcon={<ShoppingCartOutlinedIcon />} | ||
onClick={() => addProductToCart(product)} | ||
> | ||
장바구니 | ||
</StyledButton> | ||
|
||
<StyledButton | ||
variant="outlined" | ||
startIcon={isBookmarked ? <FavoriteIcon /> : <FavoriteBorderIcon />} | ||
onClick={handleToggleBookmark} | ||
> | ||
찜하기 | ||
</StyledButton> | ||
</Box> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import { | ||
AppBar, | ||
Toolbar, | ||
Typography, | ||
Button, | ||
Box, | ||
IconButton, | ||
useMediaQuery, | ||
FormControl, | ||
Select, | ||
MenuItem, | ||
} from '@mui/material'; | ||
import GridViewRoundedIcon from '@mui/icons-material/GridViewRounded'; | ||
import TuneIcon from '@mui/icons-material/Tune'; | ||
import AppsRoundedIcon from '@mui/icons-material/AppsRounded'; | ||
import { styled, useTheme } from '@mui/material/styles'; | ||
import { useState } from 'react'; | ||
import { SORT_OPTIONS } from '../../constants'; | ||
import CustomTooltip from '../CustomTooltip'; | ||
|
||
interface INavigationBar { | ||
totalProducts: number; | ||
handleToggel: () => void; | ||
handleSort: (value: string) => void; | ||
handleDisplayChange: (value: number) => void; | ||
isSidebarOpen: boolean; | ||
} | ||
|
||
export default function NavigationBar({ | ||
totalProducts, | ||
handleToggel, | ||
isSidebarOpen, | ||
handleSort, | ||
handleDisplayChange, | ||
}: INavigationBar) { | ||
const [selectedSortOrder, setSelectedSortOrder] = useState<string>('최신순'); | ||
const [itemsPerPage, setItemsPerPage] = useState<number>(4); | ||
const theme = useTheme(); | ||
const isMdDown = useMediaQuery(theme.breakpoints.down('md')); | ||
|
||
const onSortChange = (sortValue: string) => { | ||
setSelectedSortOrder(sortValue); | ||
handleSort(sortValue); | ||
}; | ||
|
||
const handleGridChange = (value: number) => { | ||
setItemsPerPage(value); | ||
handleDisplayChange(value); | ||
}; | ||
|
||
const isSortSelected = (sortOrder: string) => { | ||
return selectedSortOrder === sortOrder; | ||
}; | ||
|
||
return ( | ||
<StickyNavbar color="inherit"> | ||
<NavbarContent> | ||
<Box sx={{ display: 'flex', flexGrow: 1, alignItems: 'center' }}> | ||
<Button variant="text" color="inherit" onClick={handleToggel}> | ||
{isSidebarOpen ? '필터 닫기' : '필터 열기'} | ||
</Button> | ||
<CustomTooltip title="필터 열기/닫기"> | ||
<IconButton onClick={handleToggel}> | ||
<TuneIcon | ||
style={{ | ||
color: isSidebarOpen ? 'inherit' : 'darkgray', | ||
}} | ||
/> | ||
</IconButton> | ||
</CustomTooltip> | ||
<Typography variant="h6">총 {totalProducts}개의 상품</Typography> | ||
</Box> | ||
|
||
{isMdDown ? ( | ||
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}> | ||
<FormControl sx={{ m: 1, minWidth: 120 }} size="small"> | ||
<Select | ||
labelId="filter-select-label" | ||
id="filter-select" | ||
value={selectedSortOrder} | ||
sx={{ fontSize: '0.9rem', borderRadius: 0 }} | ||
onChange={(event) => onSortChange(event.target.value as string)} | ||
> | ||
{SORT_OPTIONS.map((option) => ( | ||
<MenuItem key={option.value} value={option.value}> | ||
{option.label} | ||
</MenuItem> | ||
))} | ||
</Select> | ||
</FormControl> | ||
</Box> | ||
) : ( | ||
<Box sx={{ marginLeft: 'auto' }}> | ||
{SORT_OPTIONS.map((option) => ( | ||
<Button | ||
key={option.value} | ||
value={option.value} | ||
color="inherit" | ||
onClick={() => onSortChange(option.value)} | ||
sx={{ | ||
fontWeight: isSortSelected(option.value) ? '700' : '300', | ||
}} | ||
> | ||
{option.label} | ||
</Button> | ||
))} | ||
<CustomTooltip title="아이템 크게 보기"> | ||
<IconButton | ||
onClick={() => handleGridChange(4)} | ||
sx={{ | ||
color: itemsPerPage === 4 ? 'inherit' : 'darkgray', | ||
}} | ||
> | ||
<GridViewRoundedIcon /> | ||
</IconButton> | ||
</CustomTooltip> | ||
<CustomTooltip title="아이템 많이 보기"> | ||
<IconButton | ||
onClick={() => handleGridChange(8)} | ||
sx={{ | ||
color: itemsPerPage === 8 ? 'inherit' : 'darkgray', | ||
}} | ||
> | ||
<AppsRoundedIcon /> | ||
</IconButton> | ||
</CustomTooltip> | ||
</Box> | ||
)} | ||
</NavbarContent> | ||
</StickyNavbar> | ||
); | ||
} | ||
|
||
const StickyNavbar = styled(AppBar)(({ theme }) => ({ | ||
position: 'sticky', | ||
top: '64px', | ||
width: '100%', | ||
margin: 0, | ||
padding: 0, | ||
boxShadow: 'none', | ||
borderBottom: '1px solid rgba(0, 0, 0, 0.12)', | ||
zIndex: theme.zIndex.drawer + 1, | ||
})); | ||
|
||
const NavbarContent = styled(Toolbar)({ | ||
display: 'flex', | ||
justifyContent: 'space-between', | ||
padding: 0, | ||
margin: '0 auto', | ||
width: '100%', | ||
}); |