Skip to content

Commit

Permalink
feat: 방 생성할 때 요청, 응답 type 변경
Browse files Browse the repository at this point in the history
  • Loading branch information
chlwlstlf committed Dec 7, 2024
1 parent 5e93f7f commit 2f74b26
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 34 deletions.
18 changes: 12 additions & 6 deletions frontend/src/@types/roomInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,15 @@ export type MemberRole = "BOTH" | "REVIEWER" | "REVIEWEE" | "NONE";
export interface BaseRoomInfo {
title: string;
content: string;
repositoryLink: string;
thumbnailLink: string;
matchingSize: number;
keywords: string[];
limitedParticipants: number;
classification: Classification;
isPublic: boolean;
recruitmentDeadline: string;
reviewDeadline: string;
memberRole: MemberRole;
repositoryLink: string;
classification: Classification;
isPublic: boolean;
}

export interface RoomInfo extends BaseRoomInfo {
Expand All @@ -36,6 +35,13 @@ export interface RoomInfo extends BaseRoomInfo {
roomStatus: RoomStatus;
participationStatus: ParticipationStatus;
message: string;
memberRole: MemberRole;
}

export interface CreateRoomInfo extends BaseRoomInfo {
roomId?: number;
managerMemberRole?: MemberRole;
managerMatchingSize?: number;
}

// 요청(Request) 구조
Expand All @@ -60,8 +66,8 @@ export interface RepositoryRequest {
}

export interface ManagerParticipationRequest {
memberRole: MemberRole;
matchingSize: number;
memberRole?: MemberRole;
matchingSize?: number;
}

export interface RoomCreateRequest {
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/apis/rooms.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import apiClient from "./apiClient";
import { API_ENDPOINTS } from "./endpoints";
import { ParticipantListInfo } from "@/@types/participantList";
import {
BaseRoomInfo,
Classification,
CreateRoomInfo,
MemberRole,
RoomCreateRequest,
RoomDetailResponse,
RoomInfo,
RoomListInfo,
RoomStatus,
Expand Down Expand Up @@ -80,15 +82,15 @@ export const getRoomDetailInfo = async (id: number): Promise<RoomInfo> => {
return res;
};

export const postCreateRoom = async (roomData: BaseRoomInfo): Promise<void> => {
export const postCreateRoom = async (roomData: RoomCreateRequest): Promise<RoomDetailResponse> => {
return apiClient.post({
endpoint: API_ENDPOINTS.ROOMS,
body: roomData,
errorMessage: MESSAGES.ERROR.POST_CREATE_ROOM,
});
};

export const putEditRoom = async (roomData: BaseRoomInfo): Promise<void> => {
export const putEditRoom = async (roomData: CreateRoomInfo): Promise<void> => {
return apiClient.put({
endpoint: API_ENDPOINTS.ROOMS,
body: roomData,
Expand Down
28 changes: 27 additions & 1 deletion frontend/src/components/roomForm/RoomFormLayout.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ export const SectionTitle = styled.h1`
color: ${({ theme }) => theme.COLOR.grey4};
`;

export const SubSectionTitle = styled.h1`
display: flex;
gap: 1rem;
align-items: flex-end;
width: 100%;
font: ${({ theme }) => theme.TEXT.medium_bold};
color: ${({ theme }) => theme.COLOR.grey4};
`;

export const HelpText = styled.p`
font: ${({ theme }) => theme.TEXT.xSmall};
color: ${({ theme }) => theme.COLOR.grey3};
Expand Down Expand Up @@ -53,7 +64,7 @@ export const RowContainer = styled.div`
`;

export const ContentLabel = styled.span`
font: ${({ theme }) => theme.TEXT.medium_bold};
font: ${({ theme }) => theme.TEXT.small_bold};
`;

export const RequiredLabel = styled.span`
Expand All @@ -77,6 +88,21 @@ export const ContentWrapper = styled.div`
padding: 1rem 0;
`;

export const ContentRadioWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 2rem;
align-items: flex-start;
padding: 1rem 0;
div {
display: flex;
flex-direction: column;
gap: 1rem;
}
`;

export const ContentRadioInput = styled.input`
display: none;
`;
Expand Down
104 changes: 90 additions & 14 deletions frontend/src/components/roomForm/RoomFormLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import ConfirmModal from "@/components/common/modal/confirmModal/ConfirmModal";
import { Textarea } from "@/components/common/textarea/Textarea";
import DateTimePicker from "@/components/dateTimePicker/DateTimePicker";
import * as S from "@/components/roomForm/RoomFormLayout.style";
import { BaseRoomInfo, Classification, RoomInfo } from "@/@types/roomInfo";
import { Classification, CreateRoomInfo, RoomInfo } from "@/@types/roomInfo";
import MESSAGES from "@/constants/message";
import { ErrorText } from "@/styles/common";
import { formatCombinedDateTime } from "@/utils/dateFormatter";
import { validateForm, validators } from "@/utils/roomInputValidator";
import { mapRoomInfoToRoomCreateRequest } from "@/utils/roomMapper";

const dropdownItems: DropdownItem[] = [
{ text: "안드로이드", value: "ANDROID" },
Expand All @@ -28,29 +29,34 @@ interface RoomFormLayoutProps {
data?: RoomInfo;
}

const getInitialFormState = (data?: RoomInfo): BaseRoomInfo => ({
const getInitialFormState = (formType: "create" | "edit", data?: RoomInfo): CreateRoomInfo => ({
title: data?.title ?? "",
classification: data?.classification ?? ("" as Classification),
content: data?.content ?? "",
repositoryLink: data?.repositoryLink ?? "",
thumbnailLink: data?.thumbnailLink ?? "",
keywords: data?.keywords?.filter((keyword) => keyword !== "") ?? [],
matchingSize: data?.matchingSize ?? 1,
keywords: data?.keywords?.filter((keyword) => keyword !== "") ?? [],
limitedParticipants: data?.limitedParticipants ?? 1,
recruitmentDeadline: data ? data.recruitmentDeadline : formatCombinedDateTime(new Date()),
reviewDeadline: data ? data.reviewDeadline : formatCombinedDateTime(new Date()),
memberRole: data?.memberRole ?? "BOTH",
repositoryLink: data?.repositoryLink ?? "",
classification: data?.classification ?? ("" as Classification),
isPublic: data?.isPublic ?? true,
...(formType === "create" && {
managerMemberRole: "BOTH",
managerMatchingSize: 1,
}),
});

const RoomFormLayout = ({ formType, roomId, data }: RoomFormLayoutProps) => {
const navigate = useNavigate();
const [isClickedButton, setIsClickedButton] = useState(false);
const [formState, setFormState] = useState<BaseRoomInfo>(() => getInitialFormState(data));
const [formState, setFormState] = useState<CreateRoomInfo>(() =>
getInitialFormState(formType, data),
);
const { postCreateRoomMutation, putEditRoomMutation } = useMutateRoom();
const { isModalOpen, handleOpenModal, handleCloseModal } = useModal();

const handleInputChange = <K extends keyof BaseRoomInfo>(name: K, value: BaseRoomInfo[K]) => {
const handleInputChange = <K extends keyof CreateRoomInfo>(name: K, value: CreateRoomInfo[K]) => {
setFormState((prevState) => ({
...prevState,
[name]: value,
Expand All @@ -59,12 +65,14 @@ const RoomFormLayout = ({ formType, roomId, data }: RoomFormLayoutProps) => {

const handleConfirm = () => {
if (formType === "edit" && roomId) {
const updatedFormState = { ...formState, roomId };
putEditRoomMutation.mutate(updatedFormState, {
onSuccess: () => navigate(`/rooms/${roomId}`),
});
putEditRoomMutation.mutate(
{ ...formState, roomId },
{
onSuccess: () => navigate(`/rooms/${roomId}`),
},
);
} else {
postCreateRoomMutation.mutate(formState, {
postCreateRoomMutation.mutate(mapRoomInfoToRoomCreateRequest(formState), {
onSuccess: () => navigate("/"),
});
}
Expand Down Expand Up @@ -92,6 +100,7 @@ const RoomFormLayout = ({ formType, roomId, data }: RoomFormLayoutProps) => {
</S.SectionTitle>

<S.SubSection>
<S.SubSectionTitle>방 기본 정보</S.SubSectionTitle>
<S.RowContainer>
<S.ContentLabel>
제목 <S.RequiredLabel>*</S.RequiredLabel>
Expand Down Expand Up @@ -230,9 +239,10 @@ const RoomFormLayout = ({ formType, roomId, data }: RoomFormLayoutProps) => {
</S.SubSection>

<S.SubSection>
<S.SubSectionTitle>방 상세 정보</S.SubSectionTitle>
<S.RowContainer>
<S.ContentLabel>
상호 리뷰 인원 <S.RequiredLabel>*</S.RequiredLabel>
상호 리뷰 인원 <S.RequiredLabel>*</S.RequiredLabel>
</S.ContentLabel>
<S.HelpText>최소 1명, 최대 5명 가능해요.</S.HelpText>
<S.ContentInput>
Expand Down Expand Up @@ -345,6 +355,72 @@ const RoomFormLayout = ({ formType, roomId, data }: RoomFormLayoutProps) => {
</S.RowContainer>
</S.SubSection>

{formType === "create" && (
<S.SubSection>
<S.SubSectionTitle>나의 정보</S.SubSectionTitle>
<S.RowContainer>
<S.ContentLabel>
참여 역할 <S.RequiredLabel>*</S.RequiredLabel>
</S.ContentLabel>
<S.ContentRadioWrapper>
<div>
<S.ContentRadioInput
type="radio"
id="both"
name="managerMemberRole"
checked={formState.managerMemberRole === "BOTH"}
onChange={() => handleInputChange("managerMemberRole", "BOTH")}
/>
<S.RadioLabel htmlFor="both">리뷰어, 리뷰이로 둘 다 참여</S.RadioLabel>
<S.HelpText>
상대방의 코드를 리뷰하면서 자신의 코드도 리뷰받고 싶은 경우 선택하세요.
</S.HelpText>
</div>
<div>
<S.ContentRadioInput
type="radio"
id="reviewer"
name="managerMemberRole"
checked={formState.managerMemberRole === "REVIEWER"}
onChange={() => handleInputChange("managerMemberRole", "REVIEWER")}
/>

<S.RadioLabel htmlFor="reviewer">리뷰어로만 참여</S.RadioLabel>
<S.HelpText>다른 사람의 코드만 리뷰하고 싶은 경우 선택하세요.</S.HelpText>
</div>
</S.ContentRadioWrapper>
</S.RowContainer>

<S.RowContainer>
<S.ContentLabel>
원하는 상호 리뷰 인원 <S.RequiredLabel>*</S.RequiredLabel>
</S.ContentLabel>
<S.HelpText>
리뷰할 사람 수는 자신의 학습 목표나 시간 여유에 맞춰 선택하세요.
</S.HelpText>
<S.ContentInput>
<Input
type="number"
min="1"
max="5"
name="matchingSize"
value={formState.managerMatchingSize}
onChange={(e) =>
handleInputChange("managerMatchingSize", parseInt(e.target.value, 10))
}
error={
isClickedButton && validators.matchingSize(formState.managerMatchingSize) !== ""
}
required
/>
<ErrorText>
{isClickedButton && validators.matchingSize(formState.managerMatchingSize)}
</ErrorText>
</S.ContentInput>
</S.RowContainer>
</S.SubSection>
)}

<Button
onClick={() => {
if (validateForm(formState)) handleOpenModal();
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/constants/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const GUIDANCE_MESSAGES: Record<string, string> = {
SUB_DESCRIPTION: "조금만 기다려주세요🤗",
DELETE_ROOM: "정말 방을 삭제하시겠습니까?\n모집 마감 후엔 방을 삭제할 수 없습니다.",
EXIT_ROOM: "정말 방을 나가시겠습니까?\n모집 마감 전까진 언제든지 다시 참여할 수 있습니다.",
CREATE_ROOM: "방을 생성합니다.\n모집 마감 전까지 방 정보를 수정할 수 있습니다.",
CREATE_ROOM:
"방을 생성합니다.\n모집 마감 전까지는 방 정보를 수정할 수 있습니다.\n단, 나의 정보는 방 생성 후 변경할 수 없습니다",
EDIT_ROOM: "방 정보를 수정합니다.\n모집 마감 전까지 방 정보를 수정할 수 있습니다.",
EMPTY_PARTICIPANTS: "참여자 목록은 매칭이 시작된 이후 공개됩니다.",
ZERO_PARTICIPANTS: "이 방의 참여자가 없습니다.",
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/hooks/mutations/useMutateRoom.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import useToast from "../common/useToast";
import useMutateHandlers from "./useMutateHandlers";
import { useMutation } from "@tanstack/react-query";
import { BaseRoomInfo, MemberRole } from "@/@types/roomInfo";
import { CreateRoomInfo, MemberRole, RoomCreateRequest } from "@/@types/roomInfo";
import {
deleteParticipateIn,
deleteParticipatedRoom,
Expand All @@ -16,15 +16,15 @@ const useMutateRoom = () => {
const { handleMutateError } = useMutateHandlers();

const postCreateRoomMutation = useMutation({
mutationFn: (roomData: BaseRoomInfo) => postCreateRoom(roomData),
mutationFn: (roomData: RoomCreateRequest) => postCreateRoom(roomData),
onSuccess: () => {
openToast(MESSAGES.SUCCESS.POST_CREATE_ROOM);
},
onError: (error) => handleMutateError(error),
});

const putEditRoomMutation = useMutation({
mutationFn: (roomData: BaseRoomInfo) => putEditRoom(roomData),
mutationFn: (roomData: CreateRoomInfo) => putEditRoom(roomData),
onSuccess: () => {
openToast(MESSAGES.SUCCESS.PUT_EDIT_ROOM);
},
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/utils/dateFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const formatTime = (date: Date): string => {

// 날짜+시간
export const formatCombinedDateTime = (date: Date): string => {
return `${formatDate(date)} ${formatTime(date)}`; // TODO: T로 바꿔야함
return `${formatDate(date)} ${formatTime(date)}`;
};

export const formatLeftTime = (time: string) => {
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/utils/roomInputValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ export const validators = {
return "";
},

matchingSize(value: number): string {
matchingSize(value: number | undefined): string {
if (value === undefined) return "";
if (!isNumberNotEmpty(value)) {
return "상호 리뷰 인원을 입력해주세요.";
}
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/utils/roomMapper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BaseRoomInfo, RoomCreateRequest, RoomDetailResponse, RoomInfo } from "@/@types/roomInfo";
import { CreateRoomInfo, RoomCreateRequest, RoomDetailResponse, RoomInfo } from "@/@types/roomInfo";

// RoomDetailResponse를 RoomInfo로 변환
export const mapRoomDetailResponseToRoomInfo = (response: RoomDetailResponse): RoomInfo => ({
Expand All @@ -24,7 +24,7 @@ export const mapRoomDetailResponseToRoomInfo = (response: RoomDetailResponse): R
});

// RoomInfo를 RoomCreateRequest로 변환
export const mapRoomInfoToRoomCreateRequest = (info: BaseRoomInfo): RoomCreateRequest => ({
export const mapRoomInfoToRoomCreateRequest = (info: CreateRoomInfo): RoomCreateRequest => ({
roomInfoRequest: {
title: info.title,
content: info.content,
Expand All @@ -43,7 +43,7 @@ export const mapRoomInfoToRoomCreateRequest = (info: BaseRoomInfo): RoomCreateRe
isPublic: info.isPublic,
},
managerParticipationRequest: {
memberRole: info.memberRole,
matchingSize: info.matchingSize,
memberRole: info.managerMemberRole,
matchingSize: info.managerMatchingSize,
},
});

0 comments on commit 2f74b26

Please sign in to comment.