feat: QuizSet Create / Read

This commit is contained in:
정기영 2024-08-01 17:52:22 +09:00
parent 45f63e4819
commit 561f91481c
16 changed files with 276 additions and 0 deletions

View File

@ -26,6 +26,8 @@ const PasswordChangePage = lazy(async () => await import('./pages/PasswordChange
const LearningLecturesPage = lazy(async () => await import('./pages/LearningLecturesPage')); const LearningLecturesPage = lazy(async () => await import('./pages/LearningLecturesPage'));
const LectureCreatePage = lazy(async () => await import('./pages/LectureCreatePage')); const LectureCreatePage = lazy(async () => await import('./pages/LectureCreatePage'));
const LectureEditPage = lazy(async () => await import('./pages/LectureEditPage')); const LectureEditPage = lazy(async () => await import('./pages/LectureEditPage'));
const QuizsetListPage = lazy(async () => await import('./pages/QuizsetListPage'));
const QuizsetWritePage = lazy(async () => await import('./pages/QuizsetWritePage'));
const router = createBrowserRouter([ const router = createBrowserRouter([
{ {
@ -114,6 +116,19 @@ const router = createBrowserRouter([
}, },
], ],
}, },
{
path: 'quiz',
children: [
{
index: true,
element: <QuizsetListPage />,
},
{
path: 'write',
element: <QuizsetWritePage />,
},
],
},
], ],
}, },
{ {

View File

@ -0,0 +1,79 @@
import { useState } from 'react';
import styles from './QuizCard.module.css';
export default function QuizCard({ quiz, index, updateQuiz }) {
const [question, setQuestion] = useState(quiz.question || '');
const [answer, setAnswer] = useState(quiz.answer || '');
const [choices, setChoices] = useState(quiz.choices || []);
const handleChoiceChange = (num, content) => {
const updatedChoices = choices.map((choice) => (choice.num === num ? { ...choice, content } : choice));
setChoices(updatedChoices);
updateQuiz(index, { question, answer, choices: updatedChoices });
};
const handleAddChoice = () => {
if (choices.length < 4) {
const newChoice = { num: choices.length + 1, content: '' };
const updatedChoices = [...choices, newChoice];
setChoices(updatedChoices);
updateQuiz(index, { question, answer, choices: updatedChoices });
}
};
const handlePopChoice = () => {
if (choices.length > 0) {
const updatedChoices = choices.slice(0, -1);
setChoices(updatedChoices);
updateQuiz(index, { question, answer, choices: updatedChoices });
}
};
return (
<div className={styles.card}>
<input
type="text"
value={question}
onChange={(e) => {
setQuestion(e.target.value);
updateQuiz(index, { question: e.target.value, answer, choices });
}}
placeholder="질문 내용을 입력하세요"
/>
<input
type="text"
value={answer}
onChange={(e) => {
setAnswer(e.target.value);
updateQuiz(index, { question, answer: e.target.value, choices });
}}
placeholder="정답을 입력하세요"
/>
<div>
<span>Tip: 선택지를 넣지 않는다면 단답형 문제가 됩니다</span>
</div>
<button
type="button"
onClick={handleAddChoice}
>
선택지 추가하기
</button>
<button
type="button"
onClick={handlePopChoice}
>
선택지 줄이기
</button>
{choices.map?.((choice, idx) => (
<div key={idx}>
<input
type="text"
value={choice.content}
onChange={(e) => handleChoiceChange(choice.num, e.target.value)}
placeholder={`Choice ${choice.num}`}
/>
</div>
))}
</div>
);
}

View File

@ -0,0 +1,8 @@
.card {
border: 1px solid black;
padding: 8px;
width: 400px;
display: flex;
flex-direction: column;
gap: 4px;
}

View File

@ -0,0 +1,52 @@
import { useState } from 'react';
import QuizCard from './QuizCard';
import styles from './QuizsetForm.module.css';
export default function QuizsetForm({ onSubmit }) {
// TODO:
const [title, setTitle] = useState('');
const [quizzes, setQuizzes] = useState([]);
const handleAddQuiz = () => {
setQuizzes([...quizzes, { question: '', answer: '', choices: [] }]);
};
const updateQuiz = (index, updatedQuiz) => {
const updatedQuizzes = quizzes.map((quiz, i) => (i === index ? updatedQuiz : quiz));
setQuizzes(updatedQuizzes);
};
return (
<form
className={styles.form}
onSubmit={(e) => onSubmit(e, title, quizzes)}
>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="퀴즈셋 제목을 입력해주세요"
/>
{quizzes.map((quiz, index) => (
<QuizCard
key={index}
quiz={quiz}
index={index}
updateQuiz={updateQuiz}
/>
))}
<button
type="button"
onClick={handleAddQuiz}
>
퀴즈 추가하기
</button>
<button
type="button"
onClick={(e) => onSubmit(e, title, quizzes)}
>
퀴즈셋 저장하기
</button>
</form>
);
}

View File

@ -0,0 +1,6 @@
.form {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: 8px;
}

View File

@ -0,0 +1,2 @@
export { default as QuizCard } from './QuizCard';
export { default as QuizsetForm } from './QuizsetForm';

View File

@ -0,0 +1,10 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import instance from '../../utils/axios/instance';
import { API_URL } from '../../constants';
export function useQuizsetDetail(id) {
return useSuspenseQuery({
queryKey: ['quizset', id],
queryFn: () => instance.get(`${API_URL}/quiz/${id}`),
});
}

View File

@ -0,0 +1,14 @@
import instance from '../../utils/axios/instance';
import { API_URL } from '../../constants';
export function useQuizsetWrite() {
const quizsetWrite = (formData) => {
return instance.post(`${API_URL}/quiz`, formData, {
headers: {
'Content-type': 'multipart/form-data',
},
});
};
return { quizsetWrite };
}

View File

@ -0,0 +1,10 @@
import { useQuery } from '@tanstack/react-query';
import instance from '../../utils/axios/instance';
import { API_URL } from '../../constants';
export function useQuizsets() {
return useQuery({
queryKey: ['quizsetList'],
queryFn: () => instance.get(`${API_URL}/quiz`),
});
}

View File

@ -0,0 +1,15 @@
import { useQuizsetDetail } from '../../hooks/api/useQuizsetDetail';
import { useParams } from 'react-router-dom';
// import useBoundStore from '../../store';
export default function QuizsetListPage() {
const { lectureId } = useParams();
const { data } = useQuizsetDetail(lectureId);
const quizset = data?.data ?? [];
console.log(quizset);
return (
<div>
<div>디테일일{lectureId}</div>
</div>
);
}

View File

@ -0,0 +1 @@
export { default } from './QuizsetDetailPage';

View File

@ -0,0 +1,27 @@
import { ArticleLink } from '../../components/ArticleLink';
import ArticleBoard from '../../components/ArticleBoard/ArticleBoard';
import { useQuizsets } from '../../hooks/api/useQuizsets';
import { useParams } from 'react-router-dom';
// import useBoundStore from '../../store';
export default function QuizsetListPage() {
const { lectureId } = useParams();
const { data } = useQuizsets(lectureId);
const quizsets = data?.data ?? [];
// const userType = useBoundStore((state) => state.userType);
console.log(quizsets);
return (
<ArticleBoard
title="퀴즈 목록"
canCreate={true}
>
{quizsets.map?.((quizset) => (
<ArticleLink
key={`${quizset.quizSetId}`}
title={quizset.title}
to={`${quizset.quizSetId}`}
/>
))}
</ArticleBoard>
);
}

View File

@ -0,0 +1 @@
export { default } from './QuizsetListPage';

View File

@ -0,0 +1,34 @@
import { QuizsetForm } from '../../components/QuizForm';
import { useQuizsetWrite } from '../../hooks/api/useQuizsetWrite';
export default function QuizsetWritePage() {
// TODO: lecture
const { quizsetWrite } = useQuizsetWrite();
const handleSubmit = async (e, title, quizzes) => {
e.preventDefault();
console.log(title, quizzes);
const quizsetObject = {
title,
quizzes,
};
const formData = new FormData();
formData.append('quizSetCreateRequest', new Blob([JSON.stringify(quizsetObject)], { type: 'application/json' }));
const response = await quizsetWrite(formData);
console.log(response);
};
return (
<div>
<div>퀴즈 쓰기</div>
<QuizsetForm onSubmit={handleSubmit} />
<div>
<label>퀴즈 이미지</label>
<input
type="file"
accept=".png, .jpg, .jpeg"
/>
</div>
</div>
);
}

View File

@ -0,0 +1 @@
export { default } from './QuizsetWritePage';

View File

@ -15,6 +15,7 @@ instance.interceptors.request.use((config) => {
const accessToken = useBoundStore.getState().token; const accessToken = useBoundStore.getState().token;
if (accessToken) { if (accessToken) {
console.log(accessToken);
config.headers.Authorization = `${accessToken}`; config.headers.Authorization = `${accessToken}`;
} }