Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NDD-270] 문제집 세부 정보페이지 개발하기 (4h/5h) #129

Merged
merged 12 commits into from
Nov 30, 2023
Merged
7 changes: 7 additions & 0 deletions FE/src/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import invalidPathLoader from '@routes/invalidPathLoader';
import InterviewVideoPublicPage from '@page/interviewVideoPublicPage';
import InterviewVideoPublicLoader from '@routes/interviewVideoPublicLoader';
import rootLoader from '@routes/rootLoader';
import InterviewWorkbookDetailPage from '@page/interviewWorkbookDetailPage';
import interviewWorkbookDetailLoader from '@routes/interviewWorkbookDetailLoader';

const AppRouter = ({ queryClient }: { queryClient: QueryClient }) => {
const routes = createBrowserRouter([
Expand Down Expand Up @@ -50,6 +52,11 @@ const AppRouter = ({ queryClient }: { queryClient: QueryClient }) => {
queryClient: queryClient,
}),
},
{
path: PATH.INTERVIEW_WORKBOOK_DETAIL(),
element: <InterviewWorkbookDetailPage />,
loader: ({ params }) => interviewWorkbookDetailLoader(params),
},
{
path: '*',
loader: () => invalidPathLoader(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type QuestionAccordionProps = {
question: Question;
workbookId: number;
isSelected: boolean;
isEditable?: boolean;
toggleSelected?: () => void;
};

Expand All @@ -31,6 +32,7 @@ const QuestionAccordion: React.FC<QuestionAccordionProps> = ({
question,
workbookId,
isSelected,
isEditable,
toggleSelected,
}) => {
const queryClient = useQueryClient();
Expand Down Expand Up @@ -88,15 +90,17 @@ const QuestionAccordion: React.FC<QuestionAccordionProps> = ({
{question.answerContent}
</Typography>
</LeadingDot>
<Icon
id="edit"
css={css`
flex-shrink: 0;
`}
width="2rem"
height="2rem"
onClick={userInfo ? handleEditModal : handleEditGuestUser}
/>
{isEditable && (
<Icon
id="edit"
css={css`
flex-shrink: 0;
`}
width="2rem"
height="2rem"
onClick={userInfo ? handleEditModal : handleEditGuestUser}
/>
)}
</AccordionDetails>
</Accordion>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,51 @@
import { WorkbookEntity } from '@/types/workbook';
import { css } from '@emotion/react';
import Modal from '@foundation/Modal';
import { Button, CheckBox, Icon, Typography } from '@foundation/index';
import { Button, CheckBox, Typography } from '@foundation/index';
import useQuestionCopyMutation from '@hooks/apis/mutations/useQuestionCopyMutation';
import useWorkbookTitleListQuery from '@hooks/apis/queries/useWorkbookTitleListQuery';
import { useState } from 'react';
import NewWorkbookListButton from './NewWorkbookListButton';
import { useNavigate } from 'react-router-dom';

const AddWorkbookListModal = ({
isOpen,
closeModal,
selectedQuestionIds,
workbookData,
}: {
isOpen: boolean;
closeModal: () => void;
workbookId: number;
selectedQuestionIds: number[];
workbookData: WorkbookEntity;
}) => {
const navigate = useNavigate();
const { data: workbookTitleData } = useWorkbookTitleListQuery();
const { mutateAsync } = useQuestionCopyMutation();

const [selectedWorkbook, setSelectedWorkbook] = useState<string[]>([]);

const handleCheckBox = (e: React.ChangeEvent<HTMLInputElement>) => {
const { checked, id } = e.target;
if (checked) {
setSelectedWorkbook((pre) => [...pre, id]);
} else {
setSelectedWorkbook((pre) => pre.filter((item) => item !== id));
}
checked ? selectWorkBook(id) : unSelectWorkBook(id);
};

const selectWorkBook = (id: string) =>
setSelectedWorkbook((pre) => [...pre, id]);

const unSelectWorkBook = (id: string) =>
setSelectedWorkbook((pre) => pre.filter((item) => item !== id));

const mutateAllQuestionCopy = async () => {
try {
await Promise.all(
selectedWorkbook.map((item) => {
const workbookId = parseInt(item);
return mutateAsync({
workbookId: workbookId,
questionIds: selectedQuestionIds,
});
})
);
} catch (error) {
console.error('문제집 복사 중 오류 발생', error);
throw error;
}
await Promise.all(
selectedWorkbook.map((item) => {
const workbookId = parseInt(item);
return mutateAsync({
workbookId: workbookId,
questionIds: selectedQuestionIds,
});
})
);
milk717 marked this conversation as resolved.
Show resolved Hide resolved
};

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
Expand All @@ -52,7 +54,15 @@ const AddWorkbookListModal = ({
alert('문제집을 선택해주세요.');
return;
}
void mutateAllQuestionCopy();

try {
void mutateAllQuestionCopy();
closeModal();
// TODO: 문제집 리스트가 있는 페이지로 이동해야함
} catch (error) {
console.error('문제집 복사 중 오류 발생', error);
throw error;
}
};

return (
milk717 marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -63,9 +73,7 @@ const AddWorkbookListModal = ({
<div
css={css`
width: 100%;
> * {
margin-bottom: 1rem;
}
gap: 1rem;
`}
>
{workbookTitleData?.map((item) => (
Expand All @@ -83,7 +91,10 @@ const AddWorkbookListModal = ({
</Typography>
</CheckBox>
))}
<NewWorkbookList />
<NewWorkbookListButton
selectedQuestionIds={selectedQuestionIds}
workbookData={workbookData}
/>
</div>
</Modal.content>
<Modal.footer>
Expand Down Expand Up @@ -114,30 +125,3 @@ const ModalHeader = () => {
</div>
);
};

const NewWorkbookList = () => {
return (
<button
css={css`
display: flex;
align-items: center;
width: 100%;

outline: none;
border: none;
background-color: transparent;
cursor: pointer;
`}
type="button"
>
<Icon id="plus" width="1.5rem" height="1.5rem" />
<Typography
css={css`
margin-left: 1rem;
`}
>
새로운 재생 목록 만들기
</Typography>
</button>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Header, Layout } from '@components/layout';
import { css } from '@emotion/react';
import React, { PropsWithChildren } from 'react';

const InterviewWorkbookDetailPageLayout: React.FC<PropsWithChildren> = ({
children,
}) => {
return (
<div>
<Header />
<Layout
direction="column"
css={css`
padding: 8rem 1rem 1rem 1rem;
`}
>
{children}
</Layout>
</div>
);
};

export default InterviewWorkbookDetailPageLayout;
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { WorkbookEntity } from '@/types/workbook';
import { css } from '@emotion/react';
import { Icon, Typography } from '@foundation/index';
import useQuestionCopyMutation from '@hooks/apis/mutations/useQuestionCopyMutation';
import useWorkbookPostMutation from '@hooks/apis/mutations/useWorkbookPostMutation';

const NewWorkbookListButton = ({
selectedQuestionIds,
workbookData,
}: {
selectedQuestionIds: number[];
workbookData: WorkbookEntity;
}) => {
const { mutateAsync: newWorkbookMutate } = useWorkbookPostMutation();
const { mutateAsync: newQuestionCopyMutate } = useQuestionCopyMutation();

const handleNewWorkbook = () => {
try {
void createNewWorkbook();
//TODO: 이 다음에는 어떻게 해줄까...?
} catch (err) {
console.log(err);
throw err;
}
};

const createNewWorkbook = async () => {
const result = await newWorkbookMutate({
title: `${workbookData.title} 복사본`,
content: workbookData.content,
categoryId: workbookData.categoryId,
});

await newQuestionCopyMutate({
workbookId: result.workbookId,
questionIds: selectedQuestionIds,
});
};

return (
<button
onClick={handleNewWorkbook}
css={css`
display: flex;
align-items: center;
width: 100%;

outline: none;
border: none;
background-color: transparent;
cursor: pointer;
`}
type="button"
>
<Icon id="plus" width="1.5rem" height="1.5rem" />
<Typography
css={css`
margin-left: 1rem;
`}
>
새로운 재생 목록 만들기
</Typography>
</button>
);
};

export default NewWorkbookListButton;
2 changes: 2 additions & 0 deletions FE/src/components/interviewWorkbookDetailPage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as AddWorkbookListModal } from './AddWorkbookListModal';
export { default as InterviewWorkbookDetailPageLayout } from './InterviewWorkbookDetailPageLayout';
3 changes: 3 additions & 0 deletions FE/src/constants/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const CONNECTION = 'connection';
const RECORD = 'record';
const MYPAGE = 'mypage';
const QUESTION = 'question';
const WORKBOOK = 'workbook';

export const PATH = {
ROOT: '/',
Expand All @@ -16,6 +17,8 @@ export const PATH = {
`/${INTERVIEW}/${videoId ?? ':videoId'}`,
INTERVIEW_VIDEO_PUBLIC: (videoHash?: string) =>
`/${INTERVIEW}/public/${videoHash ?? ':videoHash'}`,
INTERVIEW_WORKBOOK_DETAIL: (workbookId?: number) =>
`/${INTERVIEW}/${WORKBOOK}/${workbookId ?? ':workbookId'}`,
NOT_FOUND: `/404`,
};

Expand Down
5 changes: 5 additions & 0 deletions FE/src/constants/queryKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ export const QUERY_KEY = {
VIDEO_HASH: (videoHash: string) => ['video', videoHash],
WORKBOOK: ['workbook'],
WORKBOOK_ID: (workbookId: number) => ['workbook', workbookId],
QUESTION_WORKBOOK_ID: (workbookId: number) => [
'question',
'workbook',
workbookId,
],
};
2 changes: 1 addition & 1 deletion FE/src/hooks/apis/queries/useQuestionWorkbookQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const useQuestionWorkbookQuery = ({
enabled,
}: {
workbookId: number;
enabled: boolean;
enabled?: boolean;
}) => {
return useQuery({
queryKey: QUERY_KEY.QUESTION_CATEGORY(workbookId),
Expand Down
Loading
Loading