Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 25 additions & 26 deletions src/components/common/modal-delete.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
"use client";

import { useState, useEffect } from "react";
import Button from "@/components/common/Button";
import { deleteWine, deleteReview } from "@/api/wine.api";
Expand All @@ -9,15 +7,18 @@ type DeleteModalProps = {
onCancel: () => void;
id: number;
type: "wine" | "review";
onDeleted?: () => void; // 삭제 후 콜백
};

export default function DeleteModal({
isOpen,
onCancel,
id,
type,
onDeleted,
}: DeleteModalProps) {
const [message, setMessage] = useState<string | null>(null);
const [isDeleting, setIsDeleting] = useState(false);

useEffect(() => {
if (isOpen) {
Expand All @@ -32,6 +33,10 @@ export default function DeleteModal({
}, [isOpen]);

const handleDelete = async () => {
// 삭제 중복 방지
if (isDeleting) return;

setIsDeleting(true);
try {
if (type === "wine") {
await deleteWine({ id });
Expand All @@ -40,30 +45,36 @@ export default function DeleteModal({
await deleteReview({ id });
setMessage("리뷰가 삭제되었습니다.");
}

// 1초 후에 모달을 닫고 콜백 호출
setTimeout(() => {
handleCloseMessage();
if (onDeleted) {
onDeleted(); // 리뷰 목록 새로고침 등의 작업 수행
}
}, 1000);

} catch (error) {
console.error(`${type} 삭제 오류:`, error);
setMessage(`${type === "wine" ? "와인" : "리뷰"} 삭제에 실패했습니다.`);
setIsDeleting(false);
}
};

const handleCloseMessage = () => {
setMessage(null);
onCancel(); // 삭제 결과 모달을 닫으면서 삭제 모달도 닫음
setIsDeleting(false);
onCancel();
};

if (!isOpen && !message) return null;

return (
<div className="fixed inset-0 z-10 flex items-center justify-center">
{/* 배경 레이어 */}
<div className="fixed inset-0 bg-black opacity-30" onClick={onCancel} />

{/* 삭제 확인 모달 */}
{isOpen && !message && (
<div
className="rounded-[1.6rem] border-[0.1rem] border-solid border-gray-300 dark:border-none fixed z-20 dark:bg-dark-black bg-white pt-[3.2rem] pr-[1.6rem] pb-[2.4rem] pl-[1.6rem]
w-[35.3rem] h-[17.2rem] tablet:h-[18.2rem]"
>
<div className="rounded-[1.6rem] border-[0.1rem] border-solid border-gray-300 dark:border-none fixed z-20 dark:bg-dark-black bg-white pt-[3.2rem] pr-[1.6rem] pb-[2.4rem] pl-[1.6rem] w-[35.3rem] h-[17.2rem] tablet:h-[18.2rem]">
<h1 className="text-[1.8rem] tablet:text-[2rem] m-0 mb-[4rem] text-center font-bold">
정말 삭제하시겠습니까?
</h1>
Expand All @@ -75,6 +86,7 @@ export default function DeleteModal({
addClassName="!text-gray-500 text-[1.6rem] rounded-[1rem] font-bold flex items-center justify-center min-h-[5rem] tablet:min-h-[5.4rem]"
onClick={onCancel}
style={{ flexGrow: "1" }}
disabled={isDeleting}
>
취소
</Button>
Expand All @@ -85,19 +97,16 @@ export default function DeleteModal({
addClassName="text-white text-[1.6rem] rounded-[1rem] font-bold flex items-center justify-center min-h-[5rem] tablet:min-h-[5.4rem]"
style={{ flexGrow: "1" }}
onClick={handleDelete}
disabled={isDeleting}
>
삭제하기
{isDeleting ? '삭제 중...' : '삭제하기'}
</Button>
</div>
</div>
)}

{/* 삭제 결과 메시지 모달 */}
{message && (
<div
className="rounded-[1.6rem] border-[0.1rem] border-solid border-gray-300 fixed z-20 dark:bg-dark-black bg-white p-[2rem]
w-[30rem] h-[15rem] flex flex-col justify-center items-center"
>
<div className="rounded-[1.6rem] border-[0.1rem] border-solid border-gray-300 fixed z-20 dark:bg-dark-black bg-white p-[2rem] w-[30rem] h-[15rem] flex flex-col justify-center items-center">
<p className="text-[1.6rem] text-center mb-[2rem]">{message}</p>
<Button
type="button"
Expand All @@ -112,14 +121,4 @@ export default function DeleteModal({
)}
</div>
);
}

/*
페이지에서 사용예시
<DeleteModal
isOpen={isDeleteModalOpen}
onCancel={() => setIsDeleteModalOpen(false)}
id={id}
type="wine" // 또는 "review"
/>
*/
}
5 changes: 3 additions & 2 deletions src/components/myprofile/my-review-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export interface Review {
aroma: Aroma[];
}

export default function MyReviewCard({ review }: { review: Review }) {
export default function MyReviewCard({ review, onDelete }: { review: Review; onDelete?: () => void; }) {
const [wineName, setWineName] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
Expand Down Expand Up @@ -189,7 +189,8 @@ export default function MyReviewCard({ review }: { review: Review }) {
onCancel={closeDeleteModal}
id={reviewIdForModal!}
type="review"
/>
onDeleted={onDelete} // onDeleted 대신 onSuccessDelete 사용
/>
</div>
);
}
4 changes: 3 additions & 1 deletion src/components/myprofile/my-wine-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ export interface Wine {
interface MyWineCardProps {
wine: Wine;
onUpdate?: (updatedWine: Wine) => void;
onDelete?: () => void; // 삭제 후 동작을 위한 콜백 추가
}

export default function MyWineCard({ wine, onUpdate }: MyWineCardProps) {
export default function MyWineCard({ wine, onUpdate, onDelete }: MyWineCardProps) {
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);

Expand Down Expand Up @@ -114,6 +115,7 @@ export default function MyWineCard({ wine, onUpdate }: MyWineCardProps) {
onCancel={closeDeleteModal}
id={wine.id}
type="wine"
onDeleted={onDelete} // 삭제 완료 후 동작 설정
/>
</div>
);
Expand Down
116 changes: 51 additions & 65 deletions src/components/myprofile/profile-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,100 +21,68 @@ export default function ProfileTab() {

const initialLimit = 5; // 데이터 로딩 개수

// 리뷰 데이터 가져오기 (useCallback으로 메모이제이션)
// 리뷰 데이터 가져오기
const fetchReviews = useCallback(
async (cursor: number | null) => {
if (isLoading || !hasMoreReviews) return; // 로딩 중이거나 더 이상 불러올 데이터가 없으면 return
console.log("Fetching reviews with cursor:", cursor);
if (isLoading || !hasMoreReviews) return;
try {
setIsLoading(true);
const response = await instance.get("/users/me/reviews", {
params: { limit: initialLimit, cursor },
});

const { list, totalCount, nextCursor } = response.data;
console.log("Reviews data:", list);
console.log("Total reviews count:", totalCount);
console.log("Next cursor for reviews:", nextCursor);

// 중복되지 않는 데이터만 추가
setReviews((prevReviews) => {
const uniqueReviews = [
...prevReviews,
...list.filter(
(newReview: Review) =>
!prevReviews.some((review) => review.id === newReview.id),
),
];
return uniqueReviews;
});

setReviews((prevReviews) => [
...prevReviews,
...list.filter(
(newReview: Review) =>
!prevReviews.some((review) => review.id === newReview.id),
),
]);
setReveiwTotal(totalCount);
setReviewCursor(nextCursor); // 커서 저장

if (!nextCursor) {
setHasMoreReviews(false); // 더 이상 로드할 데이터 없으면 false
}
setReviewCursor(nextCursor);
setHasMoreReviews(!!nextCursor);
} catch (error) {
console.error("리뷰 데이터 불러오기 실패", error);
} finally {
setIsLoading(false);
}
},
[isLoading, hasMoreReviews],
[isLoading, hasMoreReviews, initialLimit],
);

// 와인 데이터 가져오기 (useCallback으로 메모이제이션)
// 와인 데이터 가져오기
const fetchWines = useCallback(
async (cursor: number | null) => {
if (isLoading || !hasMoreWines) return; // 로딩 중이거나 더 이상 불러올 데이터가 없으면 return
console.log("Fetching wines with cursor:", cursor);
if (isLoading || !hasMoreWines) return;
try {
setIsLoading(true);
const response = await instance.get("/users/me/wines", {
params: { limit: initialLimit, cursor },
});

const { list, totalCount, nextCursor } = response.data;
console.log("Wines data:", list);
console.log("Total wines count:", totalCount);
console.log("Next cursor for wines:", nextCursor);

// 중복되지 않는 데이터만 추가
setWines((prevWines) => {
const uniqueWines = [
...prevWines,
...list.filter(
(newWine: Wine) =>
!prevWines.some((wine) => wine.id === newWine.id),
),
];
return uniqueWines;
});

setWines((prevWines) => [
...prevWines,
...list.filter(
(newWine: Wine) =>
!prevWines.some((wine) => wine.id === newWine.id),
),
]);
setWineTotal(totalCount);
setWineCursor(nextCursor); // 커서 저장
if (!nextCursor) {
setHasMoreWines(false); // 더 이상 로드할 데이터 없으면 false
}
setWineCursor(nextCursor);
setHasMoreWines(!!nextCursor);
} catch (error) {
console.error("와인 데이터 불러오기 실패", error);
} finally {
setIsLoading(false);
}
},
[isLoading, hasMoreWines],
[isLoading, hasMoreWines, initialLimit],
);

// 탭 변경 시 데이터 가져오기
// useEffect(() => {
// if (activeTab === "reviews") {
// fetchReviews(reviewCursor);
// } else {
// fetchWines(wineCursor);
// }
// }, [activeTab, reviewCursor, wineCursor, fetchReviews, fetchWines]);

return (
<div className="desktop:w-[80rem] desktop:h-[3.2rem] tablet:w-full mobile:w-full">
<div className="flex justify-between items-center ">
Expand All @@ -123,9 +91,10 @@ export default function ProfileTab() {
className={`
desktop:w-[9.6rem] desktop:h-[3.2rem] desktop:text-[2rem] desktop:leading-[3.2rem]
tablet:w-[9.6rem] tablet:h-[3.2rem] tablet:text-[2rem] tablet:leading-[3.2rem]
mobile:w-auto mobile:h-[2.6rem] mobile:text-[1.8rem] mobile:leading-[2.6rem]

font-semibold ${activeTab === "reviews" ? "text-gray-800" : "text-gray-500"}`}
mobile:w-auto mobile:h-[2.6rem] mobile:text-[1.8rem] mobile:leading-[2.6rem]
font-semibold ${
activeTab === "reviews" ? "text-gray-800" : "text-gray-500"
}`}
onClick={() => setActiveTab("reviews")}
>
내가 쓴 후기
Expand All @@ -136,8 +105,9 @@ export default function ProfileTab() {
tablet:w-[13.1rem] tablet:h-[3.2rem] tablet:text-[2rem] tablet:leading-[3.2rem]
mobile:w-auto mobile:h-[2.6rem] mobile:text-[1.8rem] mobile:leading-[2.6rem]
desktop:ml-[3.2rem] tablet:ml-[3.2rem] mobile:ml-[1.6rem]

font-semibold ${activeTab === "wines" ? "text-gray-800" : "text-gray-500"}`}
font-semibold ${
activeTab === "wines" ? "text-gray-800" : "text-gray-500"
}`}
onClick={() => setActiveTab("wines")}
>
내가 등록한 와인
Expand All @@ -160,10 +130,18 @@ export default function ProfileTab() {
>
<div className="mt-[2.2rem] space-y-[2rem]">
{reviews.map((review) => (
<MyReviewCard key={review.id} review={review} />
<MyReviewCard
key={review.id}
review={review}
onDelete={() => {
setReviews((prevReviews) =>
prevReviews.filter((r) => r.id !== review.id),
);
setReveiwTotal((prev) => prev - 1);
}}
/>
))}
</div>
{/* 더 이상 데이터가 없을 때 메시지 표시 */}
{!hasMoreReviews && !isLoading && (
<div className="mt-[3rem] text-center text-[1.8rem] text-[#CCCCCC]">
더 이상 데이터가 없습니다.
Expand All @@ -179,10 +157,18 @@ export default function ProfileTab() {
>
<div className="mt-[2.2rem] space-y-[2rem]">
{wines.map((wine) => (
<MyWineCard key={wine.id} wine={wine} />
<MyWineCard
key={wine.id}
wine={wine}
onDelete={() => {
setWines((prevWines) =>
prevWines.filter((w) => w.id !== wine.id),
);
setWineTotal((prev) => prev - 1);
}}
/>
))}
</div>
{/* 더 이상 데이터가 없을 때 메시지 표시 */}
{!hasMoreWines && !isLoading && (
<div className="mt-[3rem] text-center text-[1.8rem] text-[#CCCCCC]">
더 이상 데이터가 없습니다.
Expand Down
Loading