feat: QuizsetEditPage 기능 완성 및 스타일 일부 추가
This commit is contained in:
parent
0a9639e7fb
commit
0980d5d0fc
@ -1,17 +1,13 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import styles from './ClassCard.module.css';
|
||||
|
||||
export default function ClassCard({ img, path, children }) {
|
||||
export default function ClassCard({ path, children }) {
|
||||
return (
|
||||
<Link
|
||||
to={path}
|
||||
className={styles.card}
|
||||
>
|
||||
<img
|
||||
src={img}
|
||||
alt="강의 이미지"
|
||||
className={styles.thumbnail}
|
||||
/>
|
||||
<div className={styles.thumbnail} />
|
||||
<div>{children}</div>
|
||||
</Link>
|
||||
);
|
||||
|
@ -2,10 +2,12 @@ import { useState } from 'react';
|
||||
import styles from './QuizCard.module.css';
|
||||
|
||||
export default function QuizCard({ quiz, updateQuiz, deleteQuiz }) {
|
||||
// TODO: 카드 디자인 완성 및 이쁘게 바꾸기
|
||||
const [question, setQuestion] = useState(quiz.question || '');
|
||||
const [answer, setAnswer] = useState(quiz.answer || '');
|
||||
const [choices, setChoices] = useState(quiz.choices || []);
|
||||
const [image, setImage] = useState(quiz.image || null);
|
||||
const [imagePreview, setImagePreview] = useState(quiz.image || null);
|
||||
|
||||
const handleChoiceChange = (num, content) => {
|
||||
const updatedChoices = choices.map((choice) => (choice.num === num ? { ...choice, content } : choice));
|
||||
@ -34,15 +36,38 @@ export default function QuizCard({ quiz, updateQuiz, deleteQuiz }) {
|
||||
const file = e.target.files[0] ?? null;
|
||||
setImage(file);
|
||||
updateQuiz(quiz.id, { ...quiz, question, answer, choices, image: file });
|
||||
|
||||
if (file) {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onloadend = () => {
|
||||
setImagePreview(fileReader.result);
|
||||
};
|
||||
fileReader.readAsDataURL(file);
|
||||
} else {
|
||||
setImagePreview(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.card}>
|
||||
<div className={styles.header}>
|
||||
<span>퀴즈 생성 카드</span>
|
||||
<span onClick={() => deleteQuiz(quiz.id)}>X</span> {/* id를 기반으로 삭제 */}
|
||||
<button onClick={() => deleteQuiz(quiz.id)}>X</button>
|
||||
</div>
|
||||
<label>질문</label>
|
||||
<label>퀴즈 이미지</label>
|
||||
<input
|
||||
type="file"
|
||||
accept=".png, .jpg, .jpeg"
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
{imagePreview && (
|
||||
<img
|
||||
src={imagePreview}
|
||||
alt="Preview"
|
||||
className={styles.imagePreview}
|
||||
/>
|
||||
)}
|
||||
<label className={styles.label}>질문</label>
|
||||
<input
|
||||
type="text"
|
||||
value={question}
|
||||
@ -50,9 +75,10 @@ export default function QuizCard({ quiz, updateQuiz, deleteQuiz }) {
|
||||
setQuestion(e.target.value);
|
||||
updateQuiz(quiz.id, { ...quiz, question: e.target.value, answer, choices, image });
|
||||
}}
|
||||
className={styles.input}
|
||||
placeholder="질문 내용을 입력하세요"
|
||||
/>
|
||||
<label>정답</label>
|
||||
<label className={styles.label}>정답</label>
|
||||
<input
|
||||
type="text"
|
||||
value={answer}
|
||||
@ -60,6 +86,7 @@ export default function QuizCard({ quiz, updateQuiz, deleteQuiz }) {
|
||||
setAnswer(e.target.value);
|
||||
updateQuiz(quiz.id, { ...quiz, question, answer: e.target.value, choices, image });
|
||||
}}
|
||||
className={styles.input}
|
||||
placeholder="정답을 입력하세요"
|
||||
/>
|
||||
<div>
|
||||
@ -69,14 +96,14 @@ export default function QuizCard({ quiz, updateQuiz, deleteQuiz }) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAddChoice}
|
||||
className={styles.button}
|
||||
className={`${styles.button} ${styles.add}`}
|
||||
>
|
||||
선택지 추가하기
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handlePopChoice}
|
||||
className={styles.removeButton}
|
||||
className={`${styles.button} ${styles.remove}`}
|
||||
>
|
||||
선택지 줄이기
|
||||
</button>
|
||||
@ -92,12 +119,6 @@ export default function QuizCard({ quiz, updateQuiz, deleteQuiz }) {
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<label>퀴즈 이미지</label>
|
||||
<input
|
||||
type="file"
|
||||
accept=".png, .jpg, .jpeg"
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
.card {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 10px 20px;
|
||||
padding: 16px 12px;
|
||||
width: 400px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -14,20 +14,46 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--text-color);
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
font-weight: 400;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.imagePreview {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin: 10px 0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.input {
|
||||
padding: 14px;
|
||||
background: var(--background);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.input::placeholder {
|
||||
color: var(--text-color-tertiary);
|
||||
}
|
||||
|
||||
.buttonsWrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.removeButton {
|
||||
.button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--accent-color);
|
||||
background-color: var(--accent-color);
|
||||
color: var(--on-primary);
|
||||
stroke: var(--on-primary);
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
font-weight: 700;
|
||||
@ -36,18 +62,16 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
.add {
|
||||
border: 1px solid var(--primary-color);
|
||||
background-color: var(--primary-color);
|
||||
color: var(--on-primary);
|
||||
stroke: var(--on-primary);
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
font-weight: 700;
|
||||
align-self: end;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.remove {
|
||||
border: 1px solid var(--blue100);
|
||||
background-color: var(--blue100);
|
||||
color: var(--info-color);
|
||||
stroke: var(--info-color);
|
||||
}
|
||||
|
@ -52,33 +52,37 @@ export default function QuizsetForm({ headerTitle, topic, to, onSubmit, initialV
|
||||
className={styles.form}
|
||||
onSubmit={(e) => onSubmit(e, title, quizzes)}
|
||||
>
|
||||
<label className={styles.label}>퀴즈셋 제목</label>
|
||||
<input
|
||||
className={styles.input}
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
placeholder="퀴즈셋 제목을 입력해주세요"
|
||||
/>
|
||||
{quizzes.map((quiz) => (
|
||||
<QuizCard
|
||||
key={quiz.id}
|
||||
quiz={quiz}
|
||||
updateQuiz={updateQuiz}
|
||||
deleteQuiz={deleteQuiz}
|
||||
/>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAddQuiz}
|
||||
className={styles.button}
|
||||
>
|
||||
퀴즈 추가하기
|
||||
</button>
|
||||
<div className={styles.grid}>
|
||||
{quizzes.map((quiz) => (
|
||||
<QuizCard
|
||||
key={quiz.id}
|
||||
quiz={quiz}
|
||||
updateQuiz={updateQuiz}
|
||||
deleteQuiz={deleteQuiz}
|
||||
/>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAddQuiz}
|
||||
className={`${styles.button} ${styles.add}`}
|
||||
>
|
||||
퀴즈 추가하기
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className={styles.button}
|
||||
className={styles.createButton}
|
||||
>
|
||||
<EditIcon />
|
||||
<div>제출</div>
|
||||
<EditIcon className={styles.edit} />
|
||||
<div>퀴즈 생성하기</div>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -32,43 +32,82 @@
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-size: 24px;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
gap: 20px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.removeButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--accent-color);
|
||||
background-color: var(--accent-color);
|
||||
color: var(--on-primary);
|
||||
stroke: var(--on-primary);
|
||||
.label {
|
||||
color: var(--text-color);
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
font-weight: 700;
|
||||
align-self: end;
|
||||
font-weight: 400;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.input {
|
||||
padding: 20px;
|
||||
background: var(--background);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
line-height: 1.2;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
align-items: start;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
font-weight: 700;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.add {
|
||||
border: 1px solid var(--primary-color);
|
||||
background-color: var(--primary-color);
|
||||
color: var(--on-primary);
|
||||
stroke: var(--on-primary);
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
font-weight: 700;
|
||||
align-self: end;
|
||||
}
|
||||
|
||||
.createButton {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
border: 1px solid var(--accent-color);
|
||||
background-color: var(--accent-color);
|
||||
color: var(--on-primary);
|
||||
stroke: var(--on-primary);
|
||||
}
|
||||
|
||||
.edit {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { API_URL } from '../../constants';
|
||||
|
||||
export function useQuizsetDelete() {
|
||||
const quizsetDelete = (quizsetId) => {
|
||||
return instance.delete(`${API_URL}/quiz/teacher/${quizsetId}`);
|
||||
return instance.delete(`${API_URL}/quiz/${quizsetId}`);
|
||||
};
|
||||
|
||||
return { quizsetDelete };
|
||||
|
@ -9,17 +9,21 @@ export default function QuizsetEditPage() {
|
||||
const location = useLocation();
|
||||
const initialValue = location.state.initialValue;
|
||||
const { quizsetEdit } = useQuizsetEdit();
|
||||
|
||||
console.log(initialValue);
|
||||
const handleSubmit = async (e, title, quizzes) => {
|
||||
e.preventDefault();
|
||||
console.log(quizzes);
|
||||
|
||||
const images = [];
|
||||
const quizContents = [];
|
||||
quizzes.forEach((quiz) => {
|
||||
const { image, ...quizData } = quiz;
|
||||
images.push(image);
|
||||
quizContents.push(quizData);
|
||||
if (quizData.id > initialValue.quizzes[initialValue.quizzes.length - 1].id) {
|
||||
const { question, answer, choices } = quizData;
|
||||
quizContents.push({ question, answer, choices });
|
||||
} else {
|
||||
quizContents.push(quizData);
|
||||
}
|
||||
});
|
||||
|
||||
const quizsetObject = {
|
||||
@ -27,7 +31,6 @@ export default function QuizsetEditPage() {
|
||||
title,
|
||||
quizzes: quizContents,
|
||||
};
|
||||
console.log(quizsetObject);
|
||||
const formData = new FormData();
|
||||
formData.append('quizSetUpdateRequest', new Blob([JSON.stringify(quizsetObject)], { type: 'application/json' }));
|
||||
|
||||
|
@ -8,15 +8,15 @@ export default function QuizsetWritePage() {
|
||||
|
||||
const handleSubmit = async (e, title, quizzes) => {
|
||||
e.preventDefault();
|
||||
console.log(quizzes);
|
||||
|
||||
const images = [];
|
||||
const quizContents = [];
|
||||
|
||||
quizzes.forEach((quiz) => {
|
||||
const { image, ...quizData } = quiz;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { image, question, answer, choices } = quiz;
|
||||
images.push(image);
|
||||
quizContents.push(quizData);
|
||||
quizContents.push({ question, answer, choices });
|
||||
});
|
||||
|
||||
const quizsetObject = {
|
||||
|
Loading…
Reference in New Issue
Block a user