Skip to content

Commit 28a1a5d

Browse files
authored
Merge pull request #60 from SanjiJikFarm/feat/#59-mypurchase
[Feat/#59 mypurchase api] 마이페이지 구매내역 API 연결
2 parents c4f505f + 26905bd commit 28a1a5d

File tree

2 files changed

+108
-40
lines changed

2 files changed

+108
-40
lines changed

sanjijikfarm/src/components/feature/Receipt/ItemCard.jsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,18 @@ export default function ItemCard({ name, code, price, qty, total, rating, onClic
77
<div className="text-body-1 mb-1 font-semibold">{name}</div>
88

99
{/* 코드 + 가격 정보 */}
10-
<div className="text-body-2-med mb-1 flex justify-between text-gray-700">
11-
<span>{code}</span>
12-
<span>{price.toLocaleString()}</span>
13-
<span>{qty}</span>
14-
<span className="text-body-2-med text-gray-700">{total.toLocaleString()}</span>
10+
<div className="text-body-2-med mb-2 flex justify-between text-gray-700">
11+
<span className="w-1/3">{price.toLocaleString()}</span>
12+
<span className="w-1/3 text-center">{qty}</span>
13+
<span className="w-1/3 text-right">{total.toLocaleString()}</span>
1514
</div>
1615

1716
{/* 별점 + 리뷰 버튼 */}
1817
<div className="mt-2 flex items-center justify-between">
1918
{hasRating ? <span className="text-main-brown text-body-2-med">{rating}</span> : <span />}
2019

2120
<button
22-
onClick={() =>
23-
onClickReview({
24-
name,
25-
code,
26-
rating,
27-
reviewText: '맛있고 신선했어요!',
28-
images: ['/example.jpg'],
29-
})
30-
}
21+
onClick={onClickReview}
3122
className={`text-body-2 rounded px-3 py-1 font-semibold ${
3223
hasRating ? 'bg-pale-green text-black' : 'bg-main-green text-white hover:bg-green-600'
3324
}`}

sanjijikfarm/src/pages/MyPurchases.jsx

Lines changed: 103 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,121 @@
1+
import { useState } from 'react';
2+
import { useQuery } from '@tanstack/react-query';
3+
4+
import { useAuthStore } from '@/api/axios/store';
5+
import { getReceiptList } from '@/api/receipt/receipt';
6+
import { fetchReviewList } from '@/api/review/review';
17
import ItemCard from '@/components/feature/Receipt/ItemCard';
2-
const dummyGroupedData = [
3-
{
4-
date: '2025.07.29',
5-
items: [
6-
{ name: '김포로컬푸드 공동판매장', code: '2100032732783', price: 4000, quantity: 1, total: 4000, rating: 3.8 },
7-
{ name: '김포로컬푸드 공동판매장', code: '2100032732783', price: 4000, quantity: 1, total: 4000, rating: 0 },
8-
],
9-
},
10-
{
11-
date: '2025.07.31',
12-
items: [
13-
{ name: '김포로컬푸드 공동판매장', code: '2100032732783', price: 4000, quantity: 1, total: 4000, rating: 4.2 },
14-
{ name: '김포로컬푸드 공동판매장', code: '2100032732783', price: 4000, quantity: 1, total: 4000 },
15-
],
16-
},
17-
{
18-
date: '2025.08.01',
19-
items: [
20-
{ name: '김포로컬푸드 공동판매장', code: '2100032732783', price: 4000, quantity: 1, total: 4000, rating: 4.2 },
21-
{ name: '김포로컬푸드 공동판매장', code: '2100032732783', price: 4000, quantity: 1, total: 4000 },
22-
],
23-
},
24-
];
8+
import ReviewModal from '@/components/feature/Receipt/ReviewModal';
9+
10+
function groupReceiptsByDate(receipts) {
11+
const grouped = {};
12+
13+
receipts.forEach((receipt) => {
14+
const { date, storeName, itemList } = receipt;
15+
16+
if (!grouped[date]) {
17+
grouped[date] = [];
18+
}
19+
20+
const items = itemList.map((item) => ({
21+
name: storeName,
22+
price: parseInt(item.price),
23+
quantity: parseInt(item.qty),
24+
total: parseInt(item.total),
25+
rating: item.rating,
26+
reviewId: item.reviewId ?? null,
27+
reviewText: item.reviewText ?? '',
28+
imageUrl: item.imageUrl ?? null,
29+
productId: item.productId,
30+
}));
31+
32+
grouped[date].push(...items);
33+
});
34+
35+
return Object.entries(grouped)
36+
.map(([date, items]) => ({ date, items }))
37+
.sort((a, b) => new Date(b.date) - new Date(a.date));
38+
}
2539

2640
export default function MyPurchases() {
41+
const { username } = useAuthStore.getState();
42+
const [selectedItem, setSelectedItem] = useState(null);
43+
const [isModalOpen, setIsModalOpen] = useState(false);
44+
const [isEditMode, setIsEditMode] = useState(false);
45+
46+
const {
47+
data: receiptList,
48+
isLoading,
49+
isError,
50+
refetch,
51+
} = useQuery({
52+
queryKey: ['receiptListWithReviews', username],
53+
queryFn: async () => {
54+
const [receipts, reviews] = await Promise.all([getReceiptList(username), fetchReviewList()]);
55+
56+
return receipts.map((receipt) => {
57+
const newItemList = receipt.itemList.map((item) => {
58+
const matched = reviews.find((r) => r.productId === item.productId);
59+
return {
60+
...item,
61+
rating: matched?.rating ?? 0,
62+
reviewId: matched?.reviewId ?? null,
63+
reviewText: matched?.text ?? '',
64+
imageUrl: matched?.imageUrl ?? null,
65+
};
66+
});
67+
68+
return {
69+
...receipt,
70+
itemList: newItemList,
71+
};
72+
});
73+
},
74+
});
75+
76+
const handleOpenModal = (item) => {
77+
setSelectedItem(item);
78+
setIsEditMode(!!item.reviewId);
79+
setIsModalOpen(true);
80+
};
81+
82+
const handleCloseModal = () => {
83+
setSelectedItem(null);
84+
setIsModalOpen(false);
85+
};
86+
87+
if (isLoading) return <div className="py-10 text-center">로딩 중...</div>;
88+
if (isError || !receiptList) return <div className="py-10 text-center">오류 발생</div>;
89+
90+
const groupedData = groupReceiptsByDate(receiptList);
91+
2792
return (
2893
<div className="relative h-screen bg-white">
94+
{isModalOpen && selectedItem && (
95+
<ReviewModal
96+
item={selectedItem}
97+
isEditMode={isEditMode}
98+
onClose={async () => {
99+
await refetch();
100+
handleCloseModal();
101+
}}
102+
/>
103+
)}
104+
29105
<div className="scrollbar-hide overflow-y-auto px-5 pt-6 pb-6" style={{ height: 'calc(100vh - 120px)' }}>
30-
{dummyGroupedData.map((group, groupIdx) => (
106+
{groupedData.map((group, groupIdx) => (
31107
<div key={groupIdx} className={`pt-6 ${groupIdx > 0 ? 'mt-4 border-t border-gray-200' : ''}`}>
32108
<h2 className="text-title-3 mb-4 ml-2 font-bold">{group.date}</h2>
33109
<div className="space-y-4">
34-
{group.items.map((item) => (
110+
{group.items.map((item, idx) => (
35111
<ItemCard
112+
key={idx}
36113
name={item.name}
37-
code={item.code}
38114
price={item.price}
39115
qty={item.quantity}
40116
total={item.total}
41117
rating={item.rating}
118+
onClickReview={() => handleOpenModal(item)}
42119
/>
43120
))}
44121
</div>

0 commit comments

Comments
 (0)