-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
주요 변경 사항: - 장바구니 페이지 (Cart.tsx) 를 추가하여 장바구니 목록, 선택 및 삭제 기능을 구현했습니다. - 장바구니 API 엔드포인트를 추가했습니다 (/carts). - 상품 상세 페이지에서 장바구니 담기 기능을 구현했습니다 (useBook.ts). - 장바구니 데이터를 다루는 useCart.ts hook을 추가했습니다. - 장바구니 목록 항목 컴포넌트 (CartItem.tsx) 를 제작했습니다. - 선택 아이콘 기능을 위한 CheckIconButton.tsx 컴포넌트를 제작했습니다. 관련 변경 사항: - client/src/api/cart.api.ts (신규) - client/src/components/cart/CartItem.tsx (신규) - client/src/components/cart/CheckIconButton.tsx (신규) - client/src/hooks/useAlert.ts (확장) - client/src/hooks/useBook.ts (확장) - client/src/hooks/useCart.ts (신규) - client/src/pages/Cart.tsx (신규) - client/src/pages/Signin.tsx (최소 변경 - useAlert hook 업데이트로 인한 영향) - client/src/pages/Signup.tsx (최소 변경 - useAlert hook 업데이트로 인한 영향) - client/src/router/index.tsx (경로 추가 - /carts)
- Loading branch information
Showing
11 changed files
with
232 additions
and
37 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
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 |
---|---|---|
@@ -1,14 +1,25 @@ | ||
import { Cart } from "src/models/cart.model"; | ||
import { httpClient } from "./http"; | ||
|
||
interface AddCartParams { | ||
book_id: number; | ||
quantity: number; | ||
} | ||
|
||
const addCart = async (params: AddCartParams) => { | ||
export const addCart = async (params: AddCartParams) => { | ||
const response = await httpClient.post('/cart', params) | ||
|
||
return response.data | ||
}; | ||
|
||
export default addCart; | ||
export const fetchCart = async () => { | ||
const response = await httpClient.get<Cart[]>("/carts") | ||
|
||
return response.data | ||
}; | ||
|
||
export const deleteCart = async (cartId: number) => { | ||
const response = await httpClient.delete(`/carts/${cartId}`) | ||
|
||
return response.data; | ||
} |
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,66 @@ | ||
import { useMemo } from "react"; | ||
import { useAlert } from "src/hooks/useAlert"; | ||
import { Cart } from "src/models/cart.model"; | ||
import { formatNumber } from "src/utils/format"; | ||
import styled from "styled-components"; | ||
import Button from "../common/Button"; | ||
import Title from "../common/Title"; | ||
import CheckIconButton from "./CheckIconButton"; | ||
|
||
interface Props { | ||
cart: Cart; | ||
checkedItems: number[]; | ||
onCheck: (id: number) => void; | ||
onDelete: (id: number) => void; | ||
} | ||
|
||
const CartItem = ({cart, checkedItems, onCheck, onDelete}: Props) => { | ||
const { showConfirm } = useAlert() | ||
|
||
const isChecked = useMemo(() => { | ||
return checkedItems.includes(cart.id) | ||
}, [checkedItems, cart.id]) | ||
|
||
const handleCheck = () => { | ||
onCheck(cart.id) | ||
} | ||
|
||
const handleDelete = () => { | ||
showConfirm('정말 삭제하시겠습니까?', () => { | ||
onDelete(cart.id) | ||
}) | ||
} | ||
|
||
return ( | ||
<CartItemStyle> | ||
<div className="info"> | ||
<div><CheckIconButton isChecked={isChecked} onCheck={handleCheck}/></div> | ||
<div> | ||
<Title size="medium">{cart.title}</Title> | ||
<p className="summary">{cart.summary}</p> | ||
<p className="price">{formatNumber(cart.price)}원</p> | ||
<p className="price">{cart.quantity}권</p> | ||
</div> | ||
</div> | ||
<Button size="medium" scheme="normal" onClick={handleDelete}> | ||
장바구니 삭제 | ||
</Button> | ||
</CartItemStyle> | ||
); | ||
} | ||
|
||
const CartItemStyle = styled.div` | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: start; | ||
border: ${({ theme }) => theme.color.border}; | ||
border-radius: ${({ theme }) => theme.borderRadius.default}; | ||
padding: 12px; | ||
p { | ||
padding: 0 0 8px 0; | ||
margin: 0; | ||
} | ||
`; | ||
|
||
export default CartItem; |
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,30 @@ | ||
import { FaRegCheckCircle, FaRegCircle } from "react-icons/fa"; | ||
import styled from "styled-components"; | ||
|
||
interface Props { | ||
isChecked: boolean; | ||
onCheck: () => void; | ||
} | ||
|
||
const CheckIconButton = ({ isChecked, onCheck }: Props) => { | ||
return ( | ||
<CheckIconButtonStyle onClick={onCheck}> | ||
{ | ||
isChecked ? <FaRegCircle /> : <FaRegCheckCircle /> | ||
} | ||
</CheckIconButtonStyle> | ||
); | ||
} | ||
|
||
const CheckIconButtonStyle = styled.button` | ||
background-color: none; | ||
border: 0; | ||
cursor: pointer; | ||
svg { | ||
width: 24px; | ||
height: 24px; | ||
} | ||
`; | ||
|
||
export default CheckIconButton; |
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 |
---|---|---|
@@ -1,9 +1,15 @@ | ||
import { useCallback } from "react" | ||
import { useCallback } from "react"; | ||
|
||
export const useAlert = () => { | ||
const showAlert = useCallback((message: string) => { | ||
window.alert(message) | ||
window.alert(message) | ||
}, []) | ||
|
||
return showAlert; | ||
const showConfirm = useCallback((message: string, onConfirm: () => void) => { | ||
if (window.confirm(message)) { | ||
onConfirm(); | ||
} | ||
}, []); | ||
|
||
return { showAlert, showConfirm }; | ||
} |
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
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,23 @@ | ||
import { useEffect, useState } from "react"; | ||
import { deleteCart, fetchCart } from "src/api/cart.api"; | ||
import { Cart } from "src/models/cart.model"; | ||
|
||
export const useCart = () => { | ||
const [carts, setCarts] = useState<Cart[]>([]); | ||
const [isEmpty, setIsEmpty] = useState(true) | ||
|
||
const deleteCartItem = (id: number) => { | ||
deleteCart(id).then(() => { | ||
setCarts(carts.filter((cart) => cart.id !== id)) | ||
}) | ||
} | ||
|
||
useEffect(() => { | ||
fetchCart().then((carts) => { | ||
setCarts(carts) | ||
setIsEmpty(carts.length === 0) | ||
}) | ||
}, []) | ||
|
||
return { carts, isEmpty, deleteCartItem } | ||
} |
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,50 @@ | ||
import { useState } from "react"; | ||
import CartItem from "src/components/cart/CartItem"; | ||
import Title from "src/components/common/Title"; | ||
import { useCart } from "src/hooks/useCart"; | ||
import styled from "styled-components"; | ||
|
||
const Cart = () => { | ||
const { carts, deleteCartItem } = useCart() | ||
const [checkedItems, setCheckedItems] = useState<number[]>([30]) | ||
|
||
const handleCheckItem = (id: number) => { | ||
if (checkedItems.includes(id)) { | ||
setCheckedItems(checkedItems.filter((item) => item !== id)); | ||
} else { | ||
setCheckedItems([ | ||
...checkedItems, | ||
id | ||
]) | ||
} | ||
} | ||
|
||
const handleItemDelete = (id: number) => { | ||
deleteCartItem(id) | ||
} | ||
|
||
return ( | ||
<> | ||
<Title size="large">장바구니</Title> | ||
<CartStyle> | ||
<div className="content"> | ||
{ | ||
carts.map((item) => ( | ||
<CartItem | ||
key={item.id} | ||
cart={item} | ||
checkedItems={checkedItems} | ||
onCheck={handleCheckItem} | ||
onDelete={handleItemDelete} | ||
/> | ||
)) | ||
} | ||
</div> | ||
</CartStyle> | ||
</> | ||
); | ||
} | ||
|
||
const CartStyle = styled.div``; | ||
|
||
export default Cart; |
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
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
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