Commit abd187d8 authored by Yoon, Daeki's avatar Yoon, Daeki 😅
Browse files

edit problem중

parent 08ce2115
{
"watch": ["src/server"],
"env": {
"NODE_ENV": "development"
}
}
\ No newline at end of file
...@@ -3,6 +3,8 @@ import { Route, Switch } from "react-router-dom"; ...@@ -3,6 +3,8 @@ import { Route, Switch } from "react-router-dom";
import Signin from "./auth/Signin"; import Signin from "./auth/Signin";
import Home from "./core/Home"; import Home from "./core/Home";
import Menu from "./core/Menu"; import Menu from "./core/Menu";
import NewQuiz from './quiz/NewQuiz'
import Quiz from './quiz/Quiz'
function MainRouter() { function MainRouter() {
return ( return (
...@@ -16,6 +18,12 @@ function MainRouter() { ...@@ -16,6 +18,12 @@ function MainRouter() {
<Route path="/signin"> <Route path="/signin">
<Signin /> <Signin />
</Route> </Route>
<Route path='/quiz/new'>
<NewQuiz />
</Route>
<Route path="/quiz/:quizId">
<Quiz />
</Route>
</Switch> </Switch>
</div> </div>
// <BrowserRouter> // <BrowserRouter>
......
...@@ -14,13 +14,13 @@ function Menu() { ...@@ -14,13 +14,13 @@ function Menu() {
return ( return (
<Navbar sticky="top" bg="dark" variant="dark" expand="sm"> <Navbar sticky="top" bg="dark" variant="dark" expand="sm">
<Navbar.Brand href="/"> <Navbar.Brand href="/">
<i className="fas fa-child fa-2x"></i> <i className="fas fa-diagnoses fa-2x"></i>
</Navbar.Brand> </Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" /> <Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav"> <Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto"> <Nav className="mr-auto">
<Nav.Link href="#home">Home</Nav.Link> <Nav.Link href="/">Home</Nav.Link>
<Nav.Link href="#link">Link</Nav.Link> <Nav.Link href="/quiz/new">New Quiz</Nav.Link>
<NavDropdown title="Dropdown" id="basic-nav-dropdown"> <NavDropdown title="Dropdown" id="basic-nav-dropdown">
<NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item> <NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item>
<NavDropdown.Item href="#action/3.2"> <NavDropdown.Item href="#action/3.2">
......
import React from 'react'
function EditProblem() {
return (
<div>
</div>
)
}
export default EditProblem
import React, { useState } from "react";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
import Col from "react-bootstrap/Col";
function NewProblem({ addProblem }) {
const [answers, setAnswers] = useState([""]);
const [question, setQuestion] = useState("");
const addAnswer = () => {
setAnswers([...answers, ""]);
};
const removeAnswer = (index) => {
const list = [...answers];
list.splice(index, 1);
setAnswers(list);
};
const handleAnswer = (event, index) => {
const { value } = event.target;
const list = [...answers];
list[index] = value;
setAnswers(list);
};
const handleQuestion = (event) => {
setQuestion(event.target.value);
};
const clickAdd = (event) => {
event.preventDefault();
addProblem({ question, answers });
};
return (
<div>
<Form>
<Form.Group controlId="question">
<Form.Label>Question</Form.Label>
<Form.Control
name="question"
as="textarea"
rows={5}
onChange={handleQuestion}
/>
</Form.Group>
<Form.Label>Answers</Form.Label>
{answers.map((answer, index) => {
return (
<Form.Row key={index}>
<Col>
<Form.Control
type="text"
value={answer}
onChange={(event) => handleAnswer(event, index)}
/>
</Col>
<Col>
{answers.length !== 1 && (
<Button onClick={removeAnswer}>Remove</Button>
)}
{answers.length - 1 === index && (
<Button onClick={addAnswer}>Add</Button>
)}
</Col>
</Form.Row>
);
})}
<Button onClick={clickAdd}>Add</Button>
</Form>
</div>
);
}
export default NewProblem;
import React, { useState } from "react";
import Button from "react-bootstrap/Button";
import authHelpers from "../auth/auth-helpers";
import { create } from "./api-quiz";
import NewProblem from "./NewProblem";
import Problem from "./Problem";
function NewQuiz() {
const [problems, setProblems] = useState([])
const jwt = authHelpers.isAuthenticated();
const addProblem = (problem) => {
console.log(problem)
setProblems([...problems, problem])
}
const clickSubmit = (event) => {
event.preventDefault();
const quizData = {
problems
}
create({ userId: jwt.user._id }, { t: jwt.token }, quizData).then(
(data) => {
if (data.error) {
console.log(data.error);
} else {
console.log(data);
}
}
);
};
return (
<div>
<h1 className="text-center">
Quiz List
</h1>
{
problems.map((problem, index) => {
return <Problem key={index} problem={problem} />
})
}
<NewProblem addProblem={addProblem} />
<Button onClick={clickSubmit}>퀴즈 저장</Button>
</div>
);
}
export default NewQuiz;
import React from "react";
import Card from "react-bootstrap/Card";
import Button from "react-bootstrap/Button";
function Problem({ problem, number }) {
return (
<Card>
<Card.Body>
<Card.Title>
{number}번. {problem.question}
</Card.Title>
Answers
{problem.answers.map((answer, index) => {
return <Card.Text key={index}>{answer}</Card.Text>;
})}
<Button>수정</Button>
<Button>삭제</Button>
</Card.Body>
</Card>
);
}
export default Problem;
import React from 'react'
function Problems() {
return (
<div>
</div>
)
}
export default Problems
import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { read } from "./api-quiz";
import auth from "../auth/auth-helpers";
import Problem from './Problem'
function Quiz() {
const { quizId } = useParams();
const [quiz, setQuiz] = useState({});
const jwt = auth.isAuthenticated();
useEffect(() => {
const abortController = new AbortController();
const signal = abortController.signal;
read({ quizId: quizId }, { t: jwt.token }, signal).then((data) => {
if (data.error) {
console.log(data.error);
} else {
setQuiz(data);
}
});
return () => {
abortController.abort();
};
}, [quizId]);
return (
<div>
{quiz.problems?.map((problem, i) => {
return <Problem key={i} problem={problem} number={i+1} />;
})}
</div>
);
}
export default Quiz;
const create = async (params, credentials, quiz) => {
try {
let response = await fetch('/api/quiz/' + params.userId, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + credentials.t,
},
body: JSON.stringify(quiz),
})
return await response.json()
} catch (error) {
console.log(error)
}
}
const read = async (params, credentials, signal) => {
try {
let response = await fetch('/api/quiz/' + params.quizId, {
method: 'GET',
signal: signal,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + credentials.t,
},
})
return await response.json()
} catch (error) {
console.log(error)
}
}
export {
create,
read,
}
\ No newline at end of file
import mongoose from 'mongoose' import mongoose from 'mongoose'
const AnswerSchema = new mongoose.Schema({ const AnswerSchema = new mongoose.Schema({
questionId: { problemId: {
type: mongoose.Schema.Types.ObjectId, type: mongoose.Schema.Types.ObjectId,
ref: 'Question', ref: 'Problem',
}, },
type: String, // single/multiple choice? type: String, // single/multiple choice?
score: Number, // 맞힌 점수 score: Number, // 맞힌 점수
points: Number, // Question.score와 자동 연결 필요 points: Number, // Problem.score와 자동 연결 필요
content: String, // 기록한 답 content: String, // 기록한 답
}) })
......
import mongoose from 'mongoose' import mongoose from 'mongoose'
const QuestionSchema = new mongoose.Schema({ const ProblemSchema = new mongoose.Schema({
type: String, // 객관식, 주관식 single/multiple choice type: String, // 객관식, 주관식 single/multiple choice
created: { created: {
type: Date, type: Date,
...@@ -10,12 +10,12 @@ const QuestionSchema = new mongoose.Schema({ ...@@ -10,12 +10,12 @@ const QuestionSchema = new mongoose.Schema({
level: String, level: String,
category: [String], category: [String],
score: Number, //문제당 할당 점수 score: Number, //문제당 할당 점수
content: String, //질문 question: String, //질문
choices: [String], // 선택형 항목 answers: [String], // 선택형 항목
correct: { correct: {
type: mongoose.Schema.Types.ObjectId, type: mongoose.Schema.Types.ObjectId,
ref: 'Answer' ref: 'Answer'
}, // 정답; Answer Model 객체 }, // 정답; Answer Model 객체
}) })
export default mongoose.model('Question', QuestionSchema) export default mongoose.model('Problem', ProblemSchema)
\ No newline at end of file \ No newline at end of file
import formidable from 'formidable' import formidable from 'formidable'
import fs from 'fs' import fs from 'fs'
import quizModel from './quiz.model.js' import Problem from './problem.model.js'
import Quiz from './quiz.model.js'
const create = async (req, res) => { const create = async (req, res) => {
const form = new formidable.IncomingForm() try {
form.keepExtensions = true const { problems } = req.body
form.parse(req, async (err, fields, files) => {
if (err) { const quiz = new Quiz()
// console.log('quiz in quiz.controller:', quiz);
for await (let problem of problems) {
// console.log('problem in quiz.controller:', problem);
const p = new Problem(problem)
// console.log('problem in quiz.controller:', p);
await p.save()
quiz.problems.push(p._id)
}
quiz.author = req.profile
// console.log('quiz in quiz.controller:', quiz);
await quiz.save()
quiz.author.hashedPassword = undefined
quiz.author.salt = undefined
res.json(quiz)
} catch (error) {
return res.status(400).json({ return res.status(400).json({
error: 'Image could not be uploaded' error: 'Quiz save DB error' + error
}) })
} }
}
const quiz = new quizModel(fields) const isAuthor = (req, res, next) => {
quiz.author = req.profile const isAuthor = req.auth && req.quiz && req.auth._id == req.quiz.author._id
if (files.image) { if (!isAuthor) {
quiz.image.data = fs.readFileSync(files.image.path) return res.status(403).json({
quiz.image.contentType = files.image.type error: 'User is not an author'
})
} }
next()
}
const read = async (req, res) => {
let quiz = req.quiz
res.json(quiz)
}
const quizById = async (req, res, next, id) => {
try { try {
const result = await quiz.save() const quiz = await Quiz.findById(id)
res.json(result) .populate('author', '_id name')
} catch (error) { .populate('problems')
.exec()
if (!quiz) {
return res.status(400).json({ return res.status(400).json({
error: 'Quiz save db error' error: 'Quiz not found'
}) })
} }
req.quiz = quiz
next()
} catch (error) {
return res.status(400).json({
error: 'Quiz by id query db error: ' + error
}) })
}
} }
export default { export default {
create, create,
read,
isAuthor,
quizById,
} }
\ No newline at end of file
...@@ -16,7 +16,10 @@ const QuizSchema = new mongoose.Schema({ ...@@ -16,7 +16,10 @@ const QuizSchema = new mongoose.Schema({
publishedAt: Date, publishedAt: Date,
startAt: Date, startAt: Date,
endAt: Date, endAt: Date,
questions: [], // Question Schemas problems: [{
type: mongoose.SchemaTypes.ObjectId,
ref: 'Problem'
}], // Problem Schemas
image: { image: {
type: Buffer, type: Buffer,
contentType: String, contentType: String,
......
...@@ -8,6 +8,10 @@ const router = express.Router() ...@@ -8,6 +8,10 @@ const router = express.Router()
router.route('/api/quiz/:userId') router.route('/api/quiz/:userId')
.post(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.isInstructor, quizCtrl.create) .post(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.isInstructor, quizCtrl.create)
router.route('/api/quiz/:quizId')
.get(authCtrl.requireSignin, quizCtrl.isAuthor, quizCtrl.read)
router.param('userId', userCtrl.userById) router.param('userId', userCtrl.userById)
router.param('quizId', quizCtrl.quizById)
export default router export default router
\ No newline at end of file
...@@ -89,6 +89,7 @@ const isInstructor = (req, res, next) => { ...@@ -89,6 +89,7 @@ const isInstructor = (req, res, next) => {
} }
const userById = async (req, res, next, id) => { const userById = async (req, res, next, id) => {
// console.log('req.body in userById', req.body);
try { try {
let user = await User.findById(id) let user = await User.findById(id)
.exec() .exec()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment