Skip to content
Merged
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
25 changes: 20 additions & 5 deletions apps/web/src/components/tournament-card/FriendListDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ const toUser = (friend: FriendListItemT): UserT => ({

function FriendListDialog({ open, onOpenChange, tournamentId }: FriendListDialogProps) {
// 모달이 열렸을 때만 group-result fetch.
const { groupResultData, isGroupResultPending } = useGetGroupResult(tournamentId, {
enabled: open,
});
const { groupResultData, isGroupResultPending, isGroupResultError } = useGetGroupResult(
tournamentId,
{ enabled: open }
);
const { userData } = useGetMe();
const myUserId = userData.id;

Expand Down Expand Up @@ -78,18 +79,23 @@ function FriendListDialog({ open, onOpenChange, tournamentId }: FriendListDialog
이 토너먼트에 참여한 친구 목록입니다.
</DialogDescription>

<FriendListBody isPending={isGroupResultPending} friends={friends} />
<FriendListBody
isPending={isGroupResultPending}
isError={isGroupResultError}
friends={friends}
/>
</DialogContent>
</Dialog>
);
}

type FriendListBodyProps = {
isPending: boolean;
isError: boolean;
friends: FriendListItemT[];
};

function FriendListBody({ isPending, friends }: FriendListBodyProps) {
function FriendListBody({ isPending, isError, friends }: FriendListBodyProps) {
if (isPending) {
return (
<div className="flex h-50 items-center justify-center">
Expand All @@ -98,6 +104,15 @@ function FriendListBody({ isPending, friends }: FriendListBodyProps) {
);
}

/** 조회 실패를 빈 목록으로 뭉뚱그리면 "친구 없음" 으로 잘못 안내된다 */
if (isError) {
return (
<p className="py-10 text-center body-1-medium text-text-neutral-tertiary">
친구 목록을 불러오지 못했어요.
</p>
);
}

if (friends.length === 0) {
return (
<p className="py-10 text-center body-1-medium text-text-neutral-tertiary">
Expand Down
137 changes: 71 additions & 66 deletions apps/web/src/components/tournament-card/MorePopover.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { useRouter } from 'next/navigation';
import { type ComponentType, type SVGProps, useState } from 'react';
import { type ComponentType, type MouseEvent, type SVGProps, useState } from 'react';

import {
GroupIconFill,
Expand Down Expand Up @@ -57,76 +57,81 @@ function MorePopover({ status, tournamentId, participantCount = 0 }: MorePopover
router.push(ROUTES.TOURNAMENT_RESULT(tournamentId));
};

/** 카드 전체가 Link 라 더보기 조작이 페이지 이동으로 이어지지 않게 막는다 */
const handleStopCardNavigation = (event: MouseEvent<HTMLDivElement>) => {
event.preventDefault();
event.stopPropagation();
};

return (
<div
className="contents"
onClick={event => {
event.preventDefault();
event.stopPropagation();
}}
>
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
<PopoverTrigger asChild>
<button type="button" aria-label="더보기" className="cursor-pointer">
<ThreeDotVerticalIconFill className="size-6 text-icon-neutral-secondary" />
</button>
</PopoverTrigger>
<PopoverContent className="p-2" align="end" alignOffset={-24}>
<PopoverTitle className="sr-only">더보기</PopoverTitle>

{status === TOURNAMENT_STATUS.PENDING && (
<>
<OptionButton
Icon={HeartIconFill}
label="위시 추가하기"
onClick={handleAddTournamentItem}
/>
<OptionButton
Icon={TrashIconFill}
label="삭제하기"
onClick={handleDeleteTournament}
/>
</>
)}

{status === TOURNAMENT_STATUS.IN_PROGRESS && (
<>
{/*
<>
{/*
Dialog 는 이 래퍼 밖에 둔다 — 안에 두면 딤 클릭이 stopPropagation 에 막혀 닫히지 않는다.
*/}
<div className="contents" onClick={handleStopCardNavigation}>
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
<PopoverTrigger asChild>
<button type="button" aria-label="더보기" className="cursor-pointer">
<ThreeDotVerticalIconFill className="size-6 text-icon-neutral-secondary" />
</button>
</PopoverTrigger>
<PopoverContent className="p-2" align="end" alignOffset={-24}>
<PopoverTitle className="sr-only">더보기</PopoverTitle>

{status === TOURNAMENT_STATUS.PENDING && (
<>
<OptionButton
Icon={HeartIconFill}
label="위시 추가하기"
onClick={handleAddTournamentItem}
/>
<OptionButton
Icon={TrashIconFill}
label="삭제하기"
onClick={handleDeleteTournament}
/>
</>
)}

{status === TOURNAMENT_STATUS.IN_PROGRESS && (
<>
{/*
TODO: IN_PROGRESS 참여자 API 가 추가되면 친구 목록 보기 노출.
현재는 group-result(COMPLETED 전용) 만 사용 가능해 데이터를 못 받으므로 미노출.
*/}
<OptionButton
disabled
Icon={TrashIconFill}
label="삭제하기"
onClick={handleDeleteTournament}
/>
</>
)}

{status === TOURNAMENT_STATUS.COMPLETED && (
<>
{hasInvitedFriends && (
<OptionButton
Icon={GroupIconFill}
label="친구 목록 보기"
onClick={handleViewFriendList}
disabled
Icon={TrashIconFill}
label="삭제하기"
onClick={handleDeleteTournament}
/>
</>
)}

{status === TOURNAMENT_STATUS.COMPLETED && (
<>
{hasInvitedFriends && (
<OptionButton
Icon={GroupIconFill}
label="친구 목록 보기"
onClick={handleViewFriendList}
/>
)}
<OptionButton
Icon={ReceiptIconFill}
label="결과 확인하기"
onClick={handleViewTournamentResult}
/>
<OptionButton
Icon={TrashIconFill}
label="삭제하기"
onClick={handleDeleteTournament}
/>
)}
<OptionButton
Icon={ReceiptIconFill}
label="결과 확인하기"
onClick={handleViewTournamentResult}
/>
<OptionButton
Icon={TrashIconFill}
label="삭제하기"
onClick={handleDeleteTournament}
/>
</>
)}
</PopoverContent>
</Popover>
</>
)}
</PopoverContent>
</Popover>
</div>

<TournamentDeleteDialog
open={isDeleteDialogOpen}
Expand All @@ -139,7 +144,7 @@ function MorePopover({ status, tournamentId, participantCount = 0 }: MorePopover
onOpenChange={setIsFriendListOpen}
tournamentId={tournamentId}
/>
</div>
</>
);
}

Expand Down
Loading