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
54 changes: 54 additions & 0 deletions frontend/src/api/question-list/getMyQuestionList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import axios from "axios";

interface QuestionListProps {
page: number;
limit: number;
}

interface Question {
id: number;
content: string;
index: number;
questionListId: number;
}

interface QuestionList {
id: number;
title: string;
contents: Question[];
categoryNames: string[];
isPublic: boolean;
usage: number;
}

interface ApiResponse {
success: boolean;
message: string;
data: {
myQuestionLists: QuestionList[];
meta: {
itemsPerPage: number;
totalItems: number;
currentPage: string;
totalPages: number;
sortBy: [string[]];
};
};
}

const getMyQuestionList = async ({ page, limit }: QuestionListProps) => {
const response = await axios.get<ApiResponse>("/api/question-list/my", {
params: {
page,
limit,
},
});

if (!response.data.success) {
throw new Error(response.data.message);
}

return response.data.data;
};

export default getMyQuestionList;
15 changes: 15 additions & 0 deletions frontend/src/api/question-list/getQuestionContent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import axios from "axios";

const getQuestionContent = async (questionListId: number) => {
const response = await axios.post("/api/question-list/contents", {
questionListId,
});

if (!response.data.success) {
throw new Error(response.data.message);
}

return response.data.data.questionListContents;
};

export default getQuestionContent;
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export const getQuestionList = async ({ page, limit }: QuestionListProps) => {
limit,
},
});
return response.data;

return response.data.data.allQuestionLists;
};
54 changes: 54 additions & 0 deletions frontend/src/api/question-list/getScrapQuestionList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import axios from "axios";

interface QuestionListProps {
page: number;
limit: number;
}

interface Question {
id: number;
content: string;
index: number;
questionListId: number;
}

interface QuestionList {
id: number;
title: string;
contents: Question[];
categoryNames: string[];
isPublic: boolean;
usage: number;
}

interface ApiResponse {
success: boolean;
message: string;
data: {
questionLists: QuestionList[];
meta: {
itemsPerPage: number;
totalItems: number;
currentPage: string;
totalPages: number;
sortBy: [string[]];
};
};
}

const getScrapQuestionList = async ({ page, limit }: QuestionListProps) => {
const response = await axios.get<ApiResponse>("/api/question-list/scrap", {
params: {
page,
limit,
},
});

if (!response.data.success) {
throw new Error(response.data.message);
}

return response.data.data;
};

export default getScrapQuestionList;
15 changes: 0 additions & 15 deletions frontend/src/api/questions/getQuestionContent.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Category from "./Category";
import { useDeleteQuesitonList } from "@hooks/api/useDeleteQuestionList";
import Modal from "@components/common/Modal";
import useModal from "@/hooks/useModal";
import { useGetQuestionContent } from "@/hooks/api/useGetQuestionContent";

interface ItemProps {
questionListId: number;
Expand All @@ -19,6 +20,7 @@ const QuestionItem = ({ questionListId, type, page }: ItemProps) => {
const navigate = useNavigate();
const modal = useModal();
const deleteQuestions = useDeleteQuesitonList({ page, limit: ITEM_PER_PAGE });
const { data } = useGetQuestionContent(questionListId);

const openDeleteModal = (e: React.MouseEvent) => {
e.stopPropagation();
Expand All @@ -38,7 +40,7 @@ const QuestionItem = ({ questionListId, type, page }: ItemProps) => {
leftButton="취소하기"
rightButton="삭제하기"
type="red"
onLeftClick={() => {}}
onLeftClick={() => { }}
onRightClick={deleteHandler}
/>
<div
Expand All @@ -50,12 +52,11 @@ const QuestionItem = ({ questionListId, type, page }: ItemProps) => {
<div className="relative flex flex-col w-full gap-1">
<div className="relative">
<div className="flex flex-row gap-1 mb-2">
<Category text="네트워크" />
<Category text="운영체제" />
{data?.categoryNames.map(category => (<Category key={category} text={category} />))}
</div>
<div className="px-1">
<p className="text-gray-black text-semibold-m">
프론트엔드프론트엔드프론트엔드프론트엔드
{data?.title}
</p>
<div className="absolute top-0 right-0 text-gray-400 flex flex-row gap-1">
{type === "my" ? (
Expand All @@ -77,7 +78,7 @@ const QuestionItem = ({ questionListId, type, page }: ItemProps) => {
)}
</div>
<span className="text-gray-600 text-medium-m">
작성자 눈드뮴눈드뮴눈드뮴눈드뮴눈드뮴눈드뮴눈드
작성자 {data?.username}
</span>
</div>
</div>
Expand Down
48 changes: 39 additions & 9 deletions frontend/src/components/mypage/QuestionList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,52 @@
import { useGetMyQuestionList } from "@/hooks/api/useGetMyQuestionList";
import QuestionItem from "./QuestionItem";
import { useGetScrapQuestionList } from "@/hooks/api/useGetScrapQuestionList";

interface ListProps {
tab: "myList" | "savedList";
page: number;
}

const QuestionList = ({ tab, page }: ListProps) => {
const {
data: myData,
isLoading: isMyListLoading,
error: myListError,
} = useGetMyQuestionList({ page, limit: 8 });
const {
data: scrapData,
isLoading: isScrapListLoading,
error: scrapListError,
} = useGetScrapQuestionList({ page, limit: 8 });

const currentQuestionList =
tab === "myList" ? myData?.myQuestionLists : scrapData?.questionLists;
const isLoading = tab === "myList" ? isMyListLoading : isScrapListLoading;
const error = tab === "myList" ? myListError : scrapListError;

return (
<div className="my-4 grid grid-cols-2 gap-3">
{tab === "myList" ? (
<>
<QuestionItem questionListId={1} type="my" page={page} />
<QuestionItem questionListId={2} type="my" page={page} />
</>
<div className="my-4 w-full grid grid-cols-2 gap-3">
{isLoading ? (
<div className="col-span-2 flex items-center justify-center min-h-20">
질문 목록을 불러오는 중입니다
</div>
) : error ? (
<div className="col-span-2 flex items-center justify-center min-h-20">
질문 목록을 불러오는데 실패했습니다.
</div>
) : currentQuestionList && currentQuestionList.length > 0 ? (
currentQuestionList.map((question) => (
<QuestionItem
key={question.id}
questionListId={question.id}
type={tab === "myList" ? "my" : "saved"}
page={page}
/>
))
) : (
<>
<QuestionItem questionListId={3} type="saved" page={page} />
</>
<div className="col-span-2 flex items-center justify-center min-h-20">
질문 목록이 비어있습니다
</div>
)}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { useRef, useState } from "react";
import { useState } from "react";
import { IoMdClose } from "react-icons/io";
import CategoryTap from "@components/sessions/create/SessionForm/ListSelectModal/CategoryTab";
import SearchBar from "@components/common/SearchBar";
import useModalStore from "@stores/useModalStore";
import useModal from "@hooks/useModal";
import QuestionList from "./QuestionList";
import useSessionFormStore from "@stores/useSessionFormStore";
import Pagination from "@components/common/Pagination";

const ListSelectModal = () => {
const dialogRef = useRef<HTMLDialogElement>(null);
const { isModalOpen, closeModal } = useModalStore();
interface UseModalReturn {
dialogRef: React.RefObject<HTMLDialogElement>;
isOpen: boolean;
openModal: () => void;
closeModal: () => void;
}

interface ModalProps {
modal: UseModalReturn;
}

const ListSelectModal = ({ modal: { dialogRef, closeModal } }: ModalProps) => {
const { tab, setTab, setSelectedOpenId } = useSessionFormStore();
const [myListPage, setMyListPage] = useState(1);
const [savedListPage, setSavedListPage] = useState(1);
Expand All @@ -32,16 +39,24 @@ const ListSelectModal = () => {
},
});

useModal({ isModalOpen, dialogRef });

const closeHandler = () => {
closeModal();
setTab("myList");
setSelectedOpenId(-1);
};

const handleMouseDown = (e: React.MouseEvent<HTMLDialogElement>) => {
if (e.target === dialogRef.current) {
closeModal();
}
};

return (
<dialog ref={dialogRef} className="w-42.5 rounded-custom-l shadow-8 pb-5">
<dialog
ref={dialogRef}
className="w-42.5 rounded-custom-l shadow-8 pb-5"
onMouseDown={handleMouseDown}
>
<div className="flex p-8">
<h3 className="text-bold-m text-gray-black mr-6">질문 리스트</h3>
<CategoryTap />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import useModalStore from "@/stores/useModalStore";
import useSessionFormStore from "@/stores/useSessionFormStore";
import SelectTitle from "@/components/common/SelectTitle";
import { MdOutlineArrowForwardIos } from "react-icons/md";

const QuestionListSection = () => {
const { openModal } = useModalStore();
interface ListSectionProps {
openModal: () => void;
}

const QuestionListSection = ({ openModal }: ListSectionProps) => {
const questionTitle = useSessionFormStore((state) => state.questionTitle);

return (
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/sessions/create/SessionForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
SESSION_LISTEN_EVENT,
} from "@/constants/WebSocket/SessionEvent.ts";
import useAuth from "@hooks/useAuth.ts";
import useModal from "@/hooks/useModal";

interface RoomCreatedResponse {
id?: string;
Expand All @@ -30,6 +31,7 @@ const SessionForm = () => {
const isValid = useSessionFormStore((state) => state.isFormValid());
const navigate = useNavigate();
const toast = useToast();
const modal = useModal();

const submitHandler = () => {
if (!isValid || !socket) {
Expand Down Expand Up @@ -77,10 +79,10 @@ const SessionForm = () => {

return (
<div className="flex flex-col gap-7 p-8 bg-gray-white shadow-8 w-47.5 rounded-custom-l">
<ListSelectModal />
<ListSelectModal modal={modal} />
<CategorySection />
<TitleSection />
<QuestionListSection />
<QuestionListSection openModal={modal.openModal} />
<ParticipantSection />
Comment on lines +82 to 86
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useModal로 생성한 modal은 모달로 만들고 싶은 컴포넌트에 전달하고, 모달을 여는 버튼에는 modal.openModal을 전달하는 방식이군요 활용해보겠슴니다

<AccessSection />
<button
Expand Down
16 changes: 11 additions & 5 deletions frontend/src/hooks/__test__/useSession.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {
mockSocketStore,
mockToast,
} from "@hooks/__test__/mocks/useSession.mock";
import { SESSION_EMIT_EVENT, SESSION_LISTEN_EVENT } from "@/constants/WebSocket/SessionEvent";
import {
SESSION_EMIT_EVENT,
SESSION_LISTEN_EVENT,
} from "@/constants/WebSocket/SessionEvent";
import { SIGNAL_LISTEN_EVENT } from "@/constants/WebSocket/SignalingEvent";

const REACTION_DURATION = 3000;
Expand Down Expand Up @@ -197,10 +200,13 @@ describe("useSession Hook 테스트", () => {
result.current.emitReaction("👍");
});

expect(mockSocket.emit).toHaveBeenCalledWith(SESSION_EMIT_EVENT.REACTION, {
roomId: "test-session",
reactionType: "👍",
});
expect(mockSocket.emit).toHaveBeenCalledWith(
SESSION_EMIT_EVENT.REACTION,
{
roomId: "test-session",
reactionType: "👍",
}
);

act(() => {
jest.advanceTimersByTime(REACTION_DURATION);
Expand Down
Loading