Skip to content

Commit

Permalink
♻️ Refactor: CartPage 컴포넌트 분리
Browse files Browse the repository at this point in the history
  • Loading branch information
hdlee0619 committed Jan 16, 2025
1 parent 20aea45 commit 46cf25f
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 170 deletions.
72 changes: 72 additions & 0 deletions src/refactoring/components/cart/CartList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { CartItem } from "../../../types.ts";

interface Props {
cart: CartItem[];
updateQuantity: (productId: string, quantity: number) => void;
removeFromCart: (productId: string) => void;
}

export const CartList = ({ cart, updateQuantity, removeFromCart }: Props) => {
const getAppliedDiscount = (item: CartItem) => {
const { discounts } = item.product;
const { quantity } = item;
let appliedDiscount = 0;
for (const discount of discounts) {
if (quantity >= discount.quantity) {
appliedDiscount = Math.max(appliedDiscount, discount.rate);
}
}
return appliedDiscount;
};

return (
<div className="space-y-2">
{cart.map((item) => {
const appliedDiscount = getAppliedDiscount(item);
return (
<div
key={item.product.id}
className="flex justify-between items-center bg-white p-3 rounded shadow"
>
<div>
<span className="font-semibold">{item.product.name}</span>
<br />
<span className="text-sm text-gray-600">
{item.product.price}원 x {item.quantity}
{appliedDiscount > 0 && (
<span className="text-green-600 ml-1">
({(appliedDiscount * 100).toFixed(0)}% 할인 적용)
</span>
)}
</span>
</div>
<div>
<button
onClick={() =>
updateQuantity(item.product.id, item.quantity - 1)
}
className="bg-gray-300 text-gray-800 px-2 py-1 rounded mr-1 hover:bg-gray-400"
>
-
</button>
<button
onClick={() =>
updateQuantity(item.product.id, item.quantity + 1)
}
className="bg-gray-300 text-gray-800 px-2 py-1 rounded mr-1 hover:bg-gray-400"
>
+
</button>
<button
onClick={() => removeFromCart(item.product.id)}
className="bg-red-500 text-white px-2 py-1 rounded hover:bg-red-600"
>
삭제
</button>
</div>
</div>
);
})}
</div>
);
};
27 changes: 27 additions & 0 deletions src/refactoring/components/cart/OrderSummary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
interface Props {
calculateTotal: () => {
totalBeforeDiscount: number;
totalAfterDiscount: number;
totalDiscount: number;
};
}

export const OrderSummary = ({ calculateTotal }: Props) => {
const { totalBeforeDiscount, totalAfterDiscount, totalDiscount } =
calculateTotal();

return (
<div className="mt-6 bg-white p-4 rounded shadow">
<h2 className="text-2xl font-semibold mb-2">주문 요약</h2>
<div className="space-y-1">
<p>상품 금액: {totalBeforeDiscount.toLocaleString()}</p>
<p className="text-green-600">
할인 금액: {totalDiscount.toLocaleString()}
</p>
<p className="text-xl font-bold">
최종 결제 금액: {totalAfterDiscount.toLocaleString()}
</p>
</div>
</div>
);
};
77 changes: 77 additions & 0 deletions src/refactoring/components/cart/ProductList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { CartItem, Product } from "../../../types.ts";

interface Props {
products: Product[];
cart: CartItem[];
addToCart: (product: Product) => void;
}

export const ProductList = ({ products, cart, addToCart }: Props) => {
const getMaxDiscount = (discounts: { quantity: number; rate: number }[]) => {
return discounts.reduce((max, discount) => Math.max(max, discount.rate), 0);
};

const getRemainingStock = (product: Product) => {
const cartItem = cart.find((item) => item.product.id === product.id);
return product.stock - (cartItem?.quantity || 0);
};

return (
<div>
<h2 className="text-2xl font-semibold mb-4">상품 목록</h2>
<div className="space-y-2">
{products.map((product) => {
const remainingStock = getRemainingStock(product);
return (
<div
key={product.id}
data-testid={`product-${product.id}`}
className="bg-white p-3 rounded shadow"
>
<div className="flex justify-between items-center mb-2">
<span className="font-semibold">{product.name}</span>
<span className="text-gray-600">
{product.price.toLocaleString()}
</span>
</div>
<div className="text-sm text-gray-500 mb-2">
<span
className={`font-medium ${remainingStock > 0 ? "text-green-600" : "text-red-600"}`}
>
재고: {remainingStock}
</span>
{product.discounts.length > 0 && (
<span className="ml-2 font-medium text-blue-600">
최대 {(getMaxDiscount(product.discounts) * 100).toFixed(0)}%
할인
</span>
)}
</div>
{product.discounts.length > 0 && (
<ul className="list-disc list-inside text-sm text-gray-500 mb-2">
{product.discounts.map((discount, index) => (
<li key={index}>
{discount.quantity}개 이상:{" "}
{(discount.rate * 100).toFixed(0)}% 할인
</li>
))}
</ul>
)}
<button
onClick={() => addToCart(product)}
className={`w-full px-3 py-1 rounded ${
remainingStock > 0
? "bg-blue-500 text-white hover:bg-blue-600"
: "bg-gray-300 text-gray-500 cursor-not-allowed"
}`}
disabled={remainingStock <= 0}
>
{remainingStock > 0 ? "장바구니에 추가" : "품절"}
</button>
</div>
);
})}
</div>
</div>
);
};
42 changes: 42 additions & 0 deletions src/refactoring/components/cart/SelectCoupon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Coupon } from "../../../types.ts";

interface Props {
coupons: Coupon[];
applyCoupon: (coupon: Coupon) => void;
selectedCoupon: Coupon | null;
}

export const SelectCoupon = ({
coupons,
applyCoupon,
selectedCoupon,
}: Props) => {
return (
<div className="mt-6 bg-white p-4 rounded shadow">
<h2 className="text-2xl font-semibold mb-2">쿠폰 적용</h2>
<select
onChange={(e) => applyCoupon(coupons[parseInt(e.target.value)])}
className="w-full p-2 border rounded mb-2"
>
<option value="">쿠폰 선택</option>
{coupons.map((coupon, index) => (
<option key={coupon.code} value={index}>
{coupon.name} -{" "}
{coupon.discountType === "amount"
? `${coupon.discountValue}원`
: `${coupon.discountValue}%`}
</option>
))}
</select>
{selectedCoupon && (
<p className="text-green-600">
적용된 쿠폰: {selectedCoupon.name}(
{selectedCoupon.discountType === "amount"
? `${selectedCoupon.discountValue}원`
: `${selectedCoupon.discountValue}%`}{" "}
할인)
</p>
)}
</div>
);
};
4 changes: 4 additions & 0 deletions src/refactoring/components/cart/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./CartList.tsx";
export * from "./ProductList.tsx";
export * from "./SelectCoupon.tsx";
export * from "./OrderSummary.tsx";
Loading

0 comments on commit 46cf25f

Please sign in to comment.