Skip to content

Commit

Permalink
[FE] 참여 중인 방에 대해 바뀐 명세서 반영하기(#612) (#622)
Browse files Browse the repository at this point in the history
* refactor: 수정된 API명세서에 맞게 includeClosed 쿼리 추가

* feat: 참여중 라벨 추가

* feat: 참여중, 모집중 라벨 같이 보여주기

* feat: 종료, 매칭실패 라벨 같이 보여주기

* feat: 참여중 라벨을 범용적으로 사용하기 위해 참여로 수정

* refactor: 참여 라벨, 매칭실패 라벨 고려하여 추가하기

* test: roomStatus, participationStatus, message 상태에 따라 다르게 보여지는 라벨의 테스트 구현

* feat: 서비스 어느 곳에서든지 참여 라벨이 있을 경우 상세페이지로 리다이렉팅

---------

Co-authored-by: 00kang <[email protected]>
Co-authored-by: Lee sang Yeop <[email protected]>
  • Loading branch information
3 people authored Oct 18, 2024
1 parent de7ba88 commit 92268d8
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 16 deletions.
6 changes: 4 additions & 2 deletions frontend/src/apis/rooms.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { ParticipantListInfo } from "@/@types/participantList";
import { Role, RoomInfo, RoomListInfo, SubmitRoomInfo } from "@/@types/roomInfo";
import MESSAGES from "@/constants/message";

export const getParticipatedRoomList = async (): Promise<RoomListInfo> => {
export const getParticipatedRoomList = async (
includeClosed: boolean = false,
): Promise<RoomListInfo> => {
const res = await apiClient.get({
endpoint: API_ENDPOINTS.PARTICIPATED_ROOMS,
endpoint: `${API_ENDPOINTS.PARTICIPATED_ROOMS}?includeClosed=${includeClosed}`,
errorMessage: MESSAGES.ERROR.GET_PARTICIPATED_ROOM_LIST,
});

Expand Down
14 changes: 10 additions & 4 deletions frontend/src/components/common/label/Label.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,29 @@ export const LabelWrapper = styled.div<LabelWrapperProps>`
background-color: ${$backgroundColor || theme.COLOR.white};
border: 1px solid ${theme.COLOR.grey0};
`;
case "PARTICIPATED":
return css`
color: ${theme.COLOR.white};
background-color: ${theme.COLOR.secondary};
border: 1px solid ${theme.COLOR.secondary};
`;
case "OPEN":
return css`
color: ${theme.COLOR.black};
background-color: ${theme.COLOR.primary1};
border: 1px solid ${theme.COLOR.primary1};
`;
case "CLOSE":
case "PROGRESS":
return css`
color: ${theme.COLOR.white};
background-color: ${theme.COLOR.primary2};
border: 1px solid ${theme.COLOR.primary2};
`;
case "PROGRESS":
case "CLOSE":
return css`
color: ${theme.COLOR.white};
background-color: ${theme.COLOR.secondary};
border: 1px solid ${theme.COLOR.secondary};
background-color: ${theme.COLOR.grey1};
border: 1px solid ${theme.COLOR.grey1};
`;
case "FAIL":
return css`
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/common/label/Label.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as S from "@/components/common/label/Label.style";
import { ThemeType } from "@/styles/theme";

export type LabelType = "KEYWORD" | "OPEN" | "CLOSE" | "PROGRESS" | "FAIL";
export type LabelType = "KEYWORD" | "PARTICIPATED" | "OPEN" | "PROGRESS" | "CLOSE" | "FAIL";
export type LabelSize = keyof ThemeType["TEXT"];

interface LabelProps {
Expand All @@ -15,9 +15,10 @@ const Label = ({ text, type, size = "semiSmall", backgroundColor }: LabelProps)
return (
<S.LabelWrapper type={type} $size={size} $backgroundColor={backgroundColor}>
{type === "KEYWORD" && `#${text}`}
{type === "PARTICIPATED" && "참여"}
{type === "OPEN" && "모집 중"}
{type === "CLOSE" && "종료"}
{type === "PROGRESS" && "진행 중"}
{type === "CLOSE" && "종료"}
{type === "FAIL" && "매칭 실패"}
</S.LabelWrapper>
);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/main/room/ParticipatedRoomList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import WithSuspense from "@/components/common/withSuspense/WithSuspense";
import RoomList from "@/components/shared/roomList/RoomList";

const ParticipatedRoomList = () => {
const { data: participatedRoomList } = useFetchParticipatedRoomList();
const { data: participatedRoomList } = useFetchParticipatedRoomList(false);

return (
<ContentSection title="">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ContentSection from "@/components/common/contentSection/ContentSection";
import RoomList from "@/components/shared/roomList/RoomList";

const UserParticipatedRoom = () => {
const { data: roomList } = useFetchParticipatedRoomList();
const { data: roomList } = useFetchParticipatedRoomList(true);

const participatingRoomList = roomList.rooms.filter((room) => room.roomStatus !== "CLOSE");
const participatedRoomList = roomList.rooms.filter((room) => room.roomStatus === "CLOSE");
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/components/shared/roomCard/RoomCard.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,8 @@ export const StyledDday = styled.span`
font: ${({ theme }) => theme.TEXT.semiSmall_bold};
color: ${({ theme }) => theme.COLOR.error};
`;

export const LabelWrapper = styled.div`
display: flex;
gap: 0.5rem;
`;
169 changes: 168 additions & 1 deletion frontend/src/components/shared/roomCard/RoomCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const mockBaseRoomInfo: RoomInfo = {
message: "테스트 메세지",
};

describe("RoomCard 컴포넌트 테스트", () => {
describe("RoomCard 컴포넌트 날짜 테스트", () => {
beforeAll(() => {
const mockDate = new Date("2024-10-02T10:30:00+09:00");
const OriginalDate = Date;
Expand Down Expand Up @@ -298,3 +298,170 @@ describe("RoomCard 컴포넌트 테스트", () => {
expect(leftDay).toBeInTheDocument();
});
});

describe("RoomCard 컴포넌트 라벨 테스트", () => {
it("참여중 상태의 방은 '참여' 라벨이 표시된다.", async () => {
const mockRoomInfo: RoomInfo = {
...mockBaseRoomInfo,
roomStatus: "OPEN",
participationStatus: "PARTICIPATED",
message: "",
};
render(
<ThemeProvider theme={theme}>
<RoomCard roomInfo={mockRoomInfo} />
</ThemeProvider>,
);

expect(await screen.findByText("참여")).toBeInTheDocument();
});

it("참여중 상태인데 모집 중인 상태의 방은 '참여'와 '모집 중' 라벨이 표시된다.", async () => {
const mockRoomInfo: RoomInfo = {
...mockBaseRoomInfo,
roomStatus: "OPEN",
participationStatus: "PARTICIPATED",
message: "",
};
render(
<ThemeProvider theme={theme}>
<RoomCard roomInfo={mockRoomInfo} />
</ThemeProvider>,
);

expect(await screen.findByText("참여")).toBeInTheDocument();
expect(await screen.findByText("모집 중")).toBeInTheDocument();
});

it("모집중 상태의 방은 '모집 중' 라벨이 표시된다.", async () => {
const mockRoomInfo: RoomInfo = {
...mockBaseRoomInfo,
roomStatus: "OPEN",
participationStatus: "NOT_PARTICIPATED",
message: "",
};
render(
<ThemeProvider theme={theme}>
<RoomCard roomInfo={mockRoomInfo} />
</ThemeProvider>,
);

expect(await screen.findByText("모집 중")).toBeInTheDocument();
});

it("모집중 상태인데 참여중인 상태의 방은 '모집 중'과 '참여' 라벨이 표시된다.", async () => {
const mockRoomInfo: RoomInfo = {
...mockBaseRoomInfo,
roomStatus: "OPEN",
participationStatus: "PARTICIPATED",
message: "",
};
render(
<ThemeProvider theme={theme}>
<RoomCard roomInfo={mockRoomInfo} />
</ThemeProvider>,
);

expect(await screen.findByText("모집 중")).toBeInTheDocument();
expect(await screen.findByText("참여")).toBeInTheDocument();
});

it("진행 중 상태의 방은 '진행 중' 라벨이 표시된다.", async () => {
const mockRoomInfo: RoomInfo = {
...mockBaseRoomInfo,
roomStatus: "PROGRESS",
participationStatus: "NOT_PARTICIPATED",
message: "",
};
render(
<ThemeProvider theme={theme}>
<RoomCard roomInfo={mockRoomInfo} />
</ThemeProvider>,
);

expect(await screen.findByText("진행 중")).toBeInTheDocument();
});

it("진행 중 상태인데 참여중인 상태의 방은 '진행 중'과 '참여' 라벨이 표시된다.", async () => {
const mockRoomInfo: RoomInfo = {
...mockBaseRoomInfo,
roomStatus: "PROGRESS",
participationStatus: "PARTICIPATED",
message: "",
};
render(
<ThemeProvider theme={theme}>
<RoomCard roomInfo={mockRoomInfo} />
</ThemeProvider>,
);

expect(await screen.findByText("진행 중")).toBeInTheDocument();
expect(await screen.findByText("참여")).toBeInTheDocument();
});

it("종료됨 상태의 방은 '종료' 라벨이 표시된다.", async () => {
const mockRoomInfo: RoomInfo = {
...mockBaseRoomInfo,
roomStatus: "CLOSE",
participationStatus: "NOT_PARTICIPATED",
message: "",
};
render(
<ThemeProvider theme={theme}>
<RoomCard roomInfo={mockRoomInfo} />
</ThemeProvider>,
);

expect(await screen.findByText("종료")).toBeInTheDocument();
});

it("종료됨 상태인데 참여중인 상태의 방은 '종료'와 '참여' 라벨이 표시된다.", async () => {
const mockRoomInfo: RoomInfo = {
...mockBaseRoomInfo,
roomStatus: "CLOSE",
participationStatus: "PARTICIPATED",
message: "",
};
render(
<ThemeProvider theme={theme}>
<RoomCard roomInfo={mockRoomInfo} />
</ThemeProvider>,
);

expect(await screen.findByText("종료")).toBeInTheDocument();
expect(await screen.findByText("참여")).toBeInTheDocument();
});

it("메인페이지에서 종료됨 상태인데 매칭실패 상태의 방은 '종료' 라벨만 표시된다.", async () => {
const mockRoomInfo: RoomInfo = {
...mockBaseRoomInfo,
roomStatus: "CLOSE",
participationStatus: "NOT_PARTICIPATED",
message: "",
};
render(
<ThemeProvider theme={theme}>
<RoomCard roomInfo={mockRoomInfo} />
</ThemeProvider>,
);

expect(await screen.findByText("종료")).toBeInTheDocument();
});

it("마이페이지에서 종료된 방인데 매칭실패 상태의 방은 '종료'와 '매칭실패' 라벨이 표시된다.", async () => {
const mockRoomInfo: RoomInfo = {
...mockBaseRoomInfo,
roomStatus: "CLOSE",
participationStatus: "PARTICIPATED",
message: "방의 최소 참여 인원보다 참가자가 부족하여 매칭이 진행되지 않았습니다.",
};
render(
<ThemeProvider theme={theme}>
<RoomCard roomInfo={mockRoomInfo} />
</ThemeProvider>,
);

expect(await screen.findByText("종료")).toBeInTheDocument();
expect(await screen.findByText("매칭 실패")).toBeInTheDocument();
});
});
13 changes: 12 additions & 1 deletion frontend/src/components/shared/roomCard/RoomCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ const DisplayLeftTime = (roomInfo: RoomInfo) => {
return <>종료됨</>;
};

const DisplayLabel = (roomInfo: RoomInfo) => {
return (
<>
<Label type={roomInfo.roomStatus} />
{(roomInfo.participationStatus === "MANAGER" ||
roomInfo.participationStatus === "PARTICIPATED") && <Label type="PARTICIPATED" />}
{roomInfo.roomStatus === "CLOSE" && roomInfo.message !== "" && <Label type="FAIL" />}
</>
);
};

interface RoomCardProps {
roomInfo: RoomInfo;
}
Expand Down Expand Up @@ -76,7 +87,7 @@ const RoomCard = React.memo(({ roomInfo }: RoomCardProps) => {
</S.KeywordsContainer>

<S.EtcContainer>
<Label type={roomInfo.roomStatus} />
<S.LabelWrapper>{DisplayLabel(roomInfo)}</S.LabelWrapper>
<S.JoinMember>
<Icon kind="person" size="1.6rem" color={theme.COLOR.grey4} />
{roomInfo.currentParticipants}/{roomInfo.limitedParticipants}
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/components/shared/roomList/RoomList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ const RoomList = ({
<S.RoomListSection>
<S.RoomListContainer>
{roomList.map((roomInfo) =>
roomType === "participated" ? (
roomType === "participated" ||
roomInfo.participationStatus === "PARTICIPATED" ||
roomInfo.participationStatus === "MANAGER" ? (
<Link to={`/rooms/${roomInfo.id}`} key={roomInfo.id}>
<RoomCard roomInfo={roomInfo} />
</Link>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/hooks/queries/useFetchRooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ export const useFetchDetailRoomInfo = (roomId: number) => {
});
};

export const useFetchParticipatedRoomList = () => {
export const useFetchParticipatedRoomList = (includeClosed: boolean = false) => {
return useSuspenseQuery({
queryKey: [QUERY_KEYS.PARTICIPATED_ROOM_LIST],
queryFn: getParticipatedRoomList,
queryKey: [QUERY_KEYS.PARTICIPATED_ROOM_LIST, includeClosed],
queryFn: () => getParticipatedRoomList(includeClosed),
});
};

Expand Down

0 comments on commit 92268d8

Please sign in to comment.