Skip to content
Merged
7 changes: 7 additions & 0 deletions frontend/src/api/questions/deleteQuestionList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import axios from "axios";

export const deleteQuestionList = async (questionListId: number) => {
const response = await axios.delete(`api/question-list/${questionListId}`);

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

interface QuestionMetadataRequest {
title?: string;
categoryNames?: string[];
isPublic?: boolean;
}

export const editQuestionMetadata = async (
questionListId: string,
data: QuestionMetadataRequest
) => {
const response = await axios.patch(
`/api/question-list/${questionListId}`,
data
);

return response.data;
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from "axios";

const fetchQuestion = async (questionListId: number) => {
const getQuestionContent = async (questionListId: number) => {
const { data } = await axios.post("/api/question-list/contents", {
questionListId,
});
Expand All @@ -12,4 +12,4 @@ const fetchQuestion = async (questionListId: number) => {
return data.data.questionListContents;
};

export default fetchQuestion;
export default getQuestionContent;
16 changes: 16 additions & 0 deletions frontend/src/api/questions/getQuestionList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import axios from "axios";

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

export const getQuestionList = async ({ page, limit }: QuestionListProps) => {
const response = await axios.get("/api/question-list", {
params: {
page,
limit,
},
});
return response.data;
};
4 changes: 3 additions & 1 deletion frontend/src/components/common/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ interface ButtonProps {
text: string;
type: "gray" | "green";
icon?: IconType;
onClick: () => void;
}

const Button = ({ text, type, icon: Icon }: ButtonProps) => {
const Button = ({ text, type, icon: Icon, onClick }: ButtonProps) => {
const buttonColor =
type === "gray"
? "bg-gray-200 text-gray-black"
Expand All @@ -15,6 +16,7 @@ const Button = ({ text, type, icon: Icon }: ButtonProps) => {
return (
<button
className={`w-full h-12 flex flex-row items-center justify-center gap-2 rounded-custom-m text-semibold-m ${buttonColor}`}
onClick={onClick}
>
{Icon ? <Icon /> : null}
{text}
Expand Down
25 changes: 13 additions & 12 deletions frontend/src/components/common/Modal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
import { useRef } from "react";
import useModalStore from "@/stores/useModalStore";
import useModal from "@/hooks/useModal";
import ModalTitle from "./Title";

interface UseModalReturn {
dialogRef: React.RefObject<HTMLDialogElement>;
isOpen: boolean;
openModal: () => void;
closeModal: () => void;
}

export interface ModalProps {
modal: UseModalReturn;
title: string;
subtitle: string;
subtitle?: string;
leftButton: string;
rightButton: string;
type: "red" | "green";
onLeftClick: () => void;
onLeftClick?: () => void;
onRightClick: () => void;
}

const Modal = ({
modal: { dialogRef, isOpen, closeModal },
title,
subtitle,
leftButton,
rightButton,
type,
onLeftClick,
onLeftClick = () => { },
onRightClick,
}: ModalProps) => {
const dialogRef = useRef<HTMLDialogElement>(null);
const { isModalOpen, closeModal } = useModalStore();

useModal({ dialogRef, isModalOpen });

const handleButtonClick = (callback: () => void) => () => {
callback();
closeModal();
Expand All @@ -38,7 +39,7 @@ const Modal = ({
}
};

if (!isModalOpen) return null;
if (!isOpen) return null;

return (
<dialog
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/components/common/PageTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
interface TitleProps {
title: string;
}

const PageTitle = ({ title }: TitleProps) => {
return (
<h1 className={"text-bold-l mb-6 text-gray-black dark:text-white"}>
{title}
</h1>
);
};

export default PageTitle;
8 changes: 6 additions & 2 deletions frontend/src/components/layout/SidebarPageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import Sidebar from "@components/common/Sidebar.tsx";

interface SidebarPageLayoutProps {
children: React.ReactNode;
childrenClassName?: string;
}

const SidebarPageLayout = ({ children }: SidebarPageLayoutProps) => {
const SidebarPageLayout = ({
children,
childrenClassName = "",
}: SidebarPageLayoutProps) => {
return (
<section className={sectionWithSidebar}>
<Sidebar />
{children}
<div className={childrenClassName}>{children}</div>
</section>
);
};
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/components/mypage/ButtonSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import useModalStore from "@/stores/useModalStore";
import Button from "@components/common/Button";

const ButtonSection = () => {
const { closeModal } = useModalStore();

return (
<div className="flex w-full gap-4 my-4 px-8">
<Button text="취소하기" type="gray" onClick={closeModal} />
<Button text="수정하기" type="green" onClick={() => {}} />
</div>
);
};

export default ButtonSection;
26 changes: 0 additions & 26 deletions frontend/src/components/mypage/Profile/index.tsx

This file was deleted.

12 changes: 0 additions & 12 deletions frontend/src/components/mypage/ProfileEditModal/ButtonSection.tsx

This file was deleted.

10 changes: 10 additions & 0 deletions frontend/src/components/mypage/ProfileIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const ProfileIcon = () => {
return (
<div className="relative bg-gray-50 rounded-full w-32 h-32 border-2 border-gray-100 overflow-hidden">
<div className="absolute rounded-full w-12 h-12 bg-gray-500 top-5 left-1/2 -translate-x-1/2"></div>
<div className="absolute rounded-custom-3xl w-32 h-32 bg-gray-500 top-[4.75rem]"></div>
</div>
);
};

export default ProfileIcon;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
interface CategoryProps {
text: string;
}

const Category = ({ text }: CategoryProps) => {
return (
<span className="px-3 py-1 bg-green-50 text-green-600 dark:bg-green-600/20 dark:text-green-400 text-medium-s rounded-full">
{text}
</span>
);
};

export default Category;
90 changes: 90 additions & 0 deletions frontend/src/components/mypage/QuestionList/QuestionItem/index.tsx
Copy link
Member

Choose a reason for hiding this comment

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

모달 개선과 마이페이지 바뀐 UI도 좋은 것 같습니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { useNavigate } from "react-router-dom";
import { MdEdit } from "react-icons/md";
import { RiDeleteBin6Fill } from "react-icons/ri";
import { FaBookmark } from "react-icons/fa";
import Category from "./Category";
import { useDeleteQuesitonList } from "@hooks/api/useDeleteQuestionList";
import Modal from "@components/common/Modal";
import useModal from "@/hooks/useModal";

interface ItemProps {
questionListId: number;
type: "my" | "saved";
page: number;
}

const ITEM_PER_PAGE = 8;

const QuestionItem = ({ questionListId, type, page }: ItemProps) => {
const navigate = useNavigate();
const modal = useModal();
const deleteQuestions = useDeleteQuesitonList({ page, limit: ITEM_PER_PAGE });

const openDeleteModal = (e: React.MouseEvent) => {
e.stopPropagation();
modal.openModal();
}

const deleteHandler = () => {
deleteQuestions(questionListId);
};

return (
<>
<Modal
modal={modal}
title="해당 질문지를 삭제하실건까요?"
subtitle="삭제하면 복구할 수 없어요!"
leftButton="취소하기"
rightButton="삭제하기"
type="red"
onLeftClick={() => { }}
onRightClick={deleteHandler}
/>
<div
className="flex items-center w-full h-32 p-4 border-custom-s border-gray-200 rounded-custom-m cursor-pointer"
onClick={() => {
navigate(`/questions/${questionListId}`);
}}
>
<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="운영체제" />
</div>
<div className="px-1">
<p className="text-gray-black text-semibold-m">
프론트엔드프론트엔드프론트엔드프론트엔드
</p>
<div className="absolute top-0 right-0 text-gray-400 flex flex-row gap-1">
{type === "my" ? (
<>
<button
className="w-5 h-5"
onClick={() => navigate("/")} // TODO: 질문지 수정 페이지로 이동
>
<MdEdit className="w-5 h-5 mt-1" />
</button>
<button className="w-5 h-5" onClick={openDeleteModal}>
<RiDeleteBin6Fill className="w-5 h-5 mt-1" />
</button>
</>
) : (
<button className="w-5 h-5">
<FaBookmark className="w-5 h-5 mt-1" />
</button>
)}
</div>
<span className="text-gray-600 text-medium-m">
작성자 눈드뮴눈드뮴눈드뮴눈드뮴눈드뮴눈드뮴눈드
</span>
</div>
</div>
</div>
</div>
</>
);
};

export default QuestionItem;
25 changes: 25 additions & 0 deletions frontend/src/components/mypage/QuestionList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import QuestionItem from "./QuestionItem";

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

const QuestionList = ({ tab, page }: ListProps) => {
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} />
</>
) : (
<>
<QuestionItem questionListId={3} type="saved" page={page} />
</>
)}
</div>
);
};

export default QuestionList;

This file was deleted.

Loading