Commit 2c9d5fe2 authored by Jiwon Yoon's avatar Jiwon Yoon
Browse files

isRequired, survey-question cascade delete

parent 4f7388bd
......@@ -21,7 +21,9 @@ export const updateQuestion = async (question: BasicQuestionType) => {
return data;
};
export const deleteQuestion = async (id: string) => {
const { data } = await axios.delete(`${baseUrl}/questions/delete/${id}`);
export const deleteQuestion = async (questionId: string) => {
const { data } = await axios.delete(
`${baseUrl}/questions/delete/${questionId}`
);
return data;
};
......@@ -24,3 +24,8 @@ export const editSurvey = async (survey: SurveyType) => {
);
return data;
};
export const deleteSurvey = async (surveyId: string) => {
const { data } = await axios.delete(`${baseUrl}/surveys/delete/${surveyId}`);
return data;
};
......@@ -14,37 +14,30 @@ export const Header = () => {
</Link>
<div className="md:flex items-center justify-end md:flex-1 lg:w-0">
{user.isLoggedIn ? (
<div className="">
<div>
<button
onClick={() => logout()}
className="whitespace-nowrap font-bold text-gray-600 hover:text-themeColor mx-1 py-2 px-3 rounded-md"
className="font-bold text-gray-600 hover:text-themeColor mx-1 py-2 px-3 rounded-md"
>
로그아웃
</button>
{location.pathname === "/profile" ? (
""
) : (
<Link
to="/profile"
className="whitespace-nowrap font-bold text-gray-600 hover:text-themeColor mx-1 py-2 px-3 rounded-md"
>
<Link to="/profile">
<button className="font-bold text-gray-600 hover:text-themeColor mx-1 py-2 px-3 rounded-md">
프로필
</Link>
)}
</button>
</Link>
</div>
) : (
<div>
<Link
to="/login"
className="whitespace-nowrap font-bold text-gray-600 hover:text-themeColor mx-1 py-2 px-3 rounded-md"
>
로그인
<Link to="/login">
<button className="font-bold text-gray-600 hover:text-themeColor mx-1 py-2 px-3 rounded-md">
로그인
</button>
</Link>
<Link
to="/signup"
className="whitespace-nowrap font-bold text-white hover:bg-blue-500 mx-1 py-2 px-3 bg-themeColor rounded-md "
>
회원가입
<Link to="/signup">
<button className="font-bold text-white hover:bg-blue-500 mx-1 py-2 px-3 bg-themeColor rounded-md ">
회원가입
</button>
</Link>
</div>
)}
......
import React from "react";
import { BasicQuestionType, SurveyType } from "../types";
type Props = {
data: SurveyType;
};
export const MySurveyCard = ({ data }: Props) => {
return (
<div className="w-48 h-60 rounded overflow-hidden border-2">
<div className="px-6 py-4">
<p className="text-gray-700 text-base">{data.comment}</p>
</div>
<div className="flex flex-col py-6">
<div className="px-2 py-2">
<label>{data.title}</label>
</div>
<div className="flex justify-end">
<select
className="py-2 w-14 bg-themeColor rounded text-white"
//value={}
//onChange={}
>
<option value="option">옵션</option>
<option value="option">삭제</option>
<option value="option">이름 바꾸기</option>
</select>
</div>
</div>
</div>
);
};
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { surveyApi } from "../apis";
import { SurveyType } from "../types";
type Props = {
data: SurveyType;
};
export const MySurveyCard = ({ data }: Props) => {
const navigate = useNavigate();
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const editSurvey = () => {
navigate(`/surveys/edit/${data._id}`, {
replace: true,
});
};
async function deleteSurvey() {
try {
if (data._id) {
const survey = await surveyApi.deleteSurvey(data._id);
setSuccess(true);
setError("");
location.reload();
} else {
setLoading(true);
}
} catch (error) {
console.log("에러발생");
// catchErrors(error, setError)
} finally {
setLoading(false);
}
}
return (
<div className="w-52 h-60 rounded border-2">
<div className="h-32 p-5">
<p className="text-gray-700">
{data.comment ? data.comment : "설명없는 설문조사"}
</p>
</div>
<div className="flex flex-col px-5 py-3">
<div className="h-12">
<p className="font-bold">
{data.title ? data.title : "제목없는 설문조사"}
</p>
<p className="text-gray-500 text-sm">
{data.updatedAt?.substring(0, 10)}
</p>
</div>
<div className="flex justify-end pt-1">
<button
type="button"
className="bg-themeColor rounded text-white py-1 px-1.5 mr-1"
onClick={editSurvey}
>
수정
</button>
<button
type="button"
className="bg-themeColor rounded text-white py-1 px-1.5 ml-1"
onClick={deleteSurvey}
>
삭제
</button>
</div>
</div>
</div>
);
};
......@@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { surveyApi } from "../apis";
import { SurveyType } from "../types";
import { MySurveyCard } from "./MySurvey";
import { MySurveyCard } from "./MySurveyCard";
const testData = [
{ id: 0, name: "이름1", description: "설명1" },
......@@ -21,7 +21,8 @@ export const Profile = () => {
useEffect(() => {
getSurveys();
}, [cardDatas]);
}, []);
async function createSurvey() {
const newSurvey: SurveyType = await surveyApi.createSurvey(survey);
navigate(`/surveys/edit/${newSurvey._id}`, {
......@@ -31,24 +32,25 @@ export const Profile = () => {
async function getSurveys() {
const surveys: SurveyType[] = await surveyApi.getSurveys();
console.log(surveys);
setCardDatas(surveys);
}
// let surveys = getSurvey(_id);
return (
<div className="flex flex-col items-center">
<div className="m-5">나의 설문조사</div>
<div className="m-5 text-bold">나의 설문조사</div>
<div className="flex space-x-4 mt-5">
<button
onClick={createSurvey}
className="flex h-60 w-48 items-center border-2 border-themeColor font-bold bg-gray-200 hover:bg-themeColor rounded-lg "
className="flex h-60 w-52 items-center border-2 border-themeColor font-bold bg-gray-200 hover:bg-themeColor rounded-lg "
>
<div className="text-center px-6 py-6 font-bold text-gray-500 place-items-center hover:text-white">
CREATE NEW SURVEY!
</div>
</button>
{cardDatas.map((data, i) => {
return <MySurveyCard data={data} key={i} />;
{cardDatas.map((data, index) => {
return <MySurveyCard data={data} key={index} />;
})}
</div>
</div>
......
......@@ -34,9 +34,6 @@ export const Question = ({
changeCurrentId,
currentId,
}: Props) => {
const handleEditClick = () => {
changeCurrentId(element._id);
};
async function handleEditComplete() {
try {
const newQuestion: BasicQuestionType = await questionApi.updateQuestion(
......@@ -92,10 +89,6 @@ export const Question = ({
handleQuestion(element._id);
}
function handleDelete() {
deleteQuestion(element._id);
}
function getContent(element: BasicQuestionType) {
switch (element.type) {
case "essay":
......@@ -138,7 +131,17 @@ export const Question = ({
return <></>;
}
}
const handleRequired = (event: React.ChangeEvent<HTMLInputElement>) => {
const { checked, value } = event.currentTarget;
element[value] = checked;
handleQuestion(element._id);
};
const handleDelete = () => {
deleteQuestion(element._id);
};
const handleEditClick = () => {
changeCurrentId(element._id);
};
return (
<div className="flex flex-col container w-4/5 h-auto border-2 border-themeColor items-center m-3 py-2">
<div className="flex h-16 w-full place-content-between items-center">
......@@ -184,12 +187,17 @@ export const Question = ({
{getContent(element)}
<div className="place-self-end py-2">
<button type="button" className="px-1">
<input
type="checkbox"
id="isRequired"
value="isRequired"
onChange={handleRequired}
disabled={currentId !== element._id}
checked={element.isRequired}
/>
<label htmlFor="isRequired" className="px-1">
필수
</button>
<button type="button" className="px-1">
옵션
</button>
</label>
<button type="button" className="px-1" onClick={handleDelete}>
삭제
</button>
......
......@@ -31,7 +31,7 @@ export const RatingForm = ({ element, handleQuestion, currentId }: Props) => {
handleQuestion(element._id);
}
function addValue() {
choices.push({ text: "0", value: choices.length });
choices.push({ text: "", value: choices.length });
element.content.choices = choices;
handleQuestion(element._id);
}
......
import React, { FormEvent, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { useParams, useNavigate } from "react-router-dom";
import { questionApi, surveyApi } from "../apis";
import { SpinnerIcon } from "../icons";
import { Question } from "../questions";
import { BasicQuestionType, SurveyType } from "../types";
import { catchErrors } from "../helpers";
export const EditSurvey = () => {
let { surveyId } = useParams<{ surveyId: string }>();
const navigate = useNavigate();
useEffect(() => {
getSurvey();
}, [surveyId]);
......@@ -35,8 +37,10 @@ export const EditSurvey = () => {
setLoading(true);
}
} catch (error) {
console.log("에러발생");
// catchErrors(error, setError)
catchErrors(error, setError);
// navigate(`/`, {
// replace: false,
// });
} finally {
setLoading(false);
}
......@@ -57,13 +61,12 @@ export const EditSurvey = () => {
try {
const newSurvey: SurveyType = await surveyApi.editSurvey(survey);
console.log(newSurvey);
// setSuccess(true);
// setError("");
setSuccess(true);
setError("");
} catch (error) {
console.log("에러발생");
// catchErrors(error, setError)
catchErrors(error, setError);
} finally {
// setLoading(false);
setLoading(false);
}
}
......@@ -71,13 +74,12 @@ export const EditSurvey = () => {
try {
const newQuestion: BasicQuestionType = await questionApi.createQuestion();
setSurvey({ ...survey, questions: [...survey.questions, newQuestion] });
// setSuccess(true);
// setError("");
setSuccess(true);
setError("");
} catch (error) {
console.log("에러발생");
// catchErrors(error, setError)
catchErrors(error, setError);
} finally {
// setLoading(false);
setLoading(false);
}
}
......@@ -88,13 +90,12 @@ export const EditSurvey = () => {
id
);
setSurvey({ ...survey, questions: newList.filter((a) => a._id !== id) });
// setSuccess(true);
// setError("");
setSuccess(true);
setError("");
} catch (error) {
console.log("에러발생");
// catchErrors(error, setError)
catchErrors(error, setError);
} finally {
// setLoading(false);
setLoading(false);
}
}
......@@ -102,6 +103,7 @@ export const EditSurvey = () => {
console.log(questions);
return (
<>
{error ? alert(error) : <></>}
{loading && (
<SpinnerIcon className="animate-spin h-5 w-5 mr-1 text-slate" />
)}
......@@ -113,6 +115,7 @@ export const EditSurvey = () => {
name="title"
className="font-bold text-4xl text-center m-2 border-b-2"
placeholder="설문지 제목"
value={survey.title}
onChange={handleSurvey}
></input>
<input
......@@ -121,6 +124,7 @@ export const EditSurvey = () => {
className="font-bold text-1xl text-center m-2 resize-none"
placeholder="설문조사에 대한 설명을 입력해주세요"
size={50}
value={survey.comment}
onChange={handleSurvey}
></input>
</div>
......@@ -143,7 +147,7 @@ export const EditSurvey = () => {
type="submit"
className="border bg-themeColor my-5 py-2 px-3 font-bold text-white"
>
설문조사 생성
저장하기
</button>
</div>
</div>
......
......@@ -16,6 +16,8 @@ export interface SurveyType {
title: string;
comment: string;
questions: BasicQuestionType[];
createdAt?: string;
updatedAt?: string;
}
export interface BasicQuestionType {
......
......@@ -41,10 +41,11 @@ export const userByQuestionId = async (
const req = reqExp as TypedRequestAuth<{ userId: string }>;
let user = await questionDb.findUserByQuestionId(questionId);
if (!user) {
return res.status(404).send("사용자를 찾을 수 없습니다");
return res.status(404).send("올바른 접근이 아닙니다");
} else {
req.user = user;
next();
}
req.user = user;
next();
} catch (error: any) {
return res
.status(500)
......
......@@ -8,7 +8,7 @@ export interface TypedRequestAuth<T> extends Request {
}
export const createSurvey = asyncWrap(
async (reqExp: Request, res: Response, next: NextFunction) => {
async (reqExp: Request, res: Response) => {
const req = reqExp as TypedRequestAuth<{ userId: string }>;
const { userId } = req.auth;
let survey = req.body;
......@@ -21,14 +21,16 @@ export const createSurvey = asyncWrap(
export const getSurveyById = asyncWrap(async (req, res) => {
const { surveyId } = req.params;
const survey = await surveyDb.getSurveyById(surveyId);
const survey: any = await surveyDb.getSurveyById(surveyId);
console.log("Get완료", survey);
return res.json(survey);
});
//동혁
export const getSurveys = asyncWrap(async(req,res)=>{
const surveys = await surveyDb.getSurveys();
export const getSurveys = asyncWrap(async (reqExp: Request, res: Response) => {
const req = reqExp as TypedRequestAuth<{ userId: string }>;
const { userId } = req.auth;
const surveys = await surveyDb.getSurveys(userId);
return res.json(surveys);
});
......@@ -38,6 +40,12 @@ export const updateSurvey = asyncWrap(async (req, res) => {
return res.json(newSurvey);
});
export const deleteSurvey = asyncWrap(async (req, res) => {
const { surveyId } = req.params;
const survey = await surveyDb.deleteSurvey(surveyId);
return res.json(survey);
});
export const userBySurveyId = async (
reqExp: Request,
res: Response,
......@@ -48,10 +56,11 @@ export const userBySurveyId = async (
const req = reqExp as TypedRequestAuth<{ userId: string }>;
let user = await surveyDb.findUserBySurveyId(surveyId);
if (!user) {
return res.status(404).send("사용자를 찾을 수 없습니다");
return res.status(404).send("올바른 접근이 아닙니다.");
} else {
req.user = user;
next();
}
req.user = user;
next();
} catch (error: any) {
return res
.status(500)
......
......@@ -20,8 +20,9 @@ export const getSurveyById = async (surveyId: string) => {
const survey = await Survey.findById(surveyId).populate("questions");
return survey;
};
export const getSurveys = async () => {
const surveys = await Survey.find();
export const getSurveys = async (userId: string) => {
const surveys = await Survey.find({ user: userId });
return surveys;
};
......@@ -29,3 +30,9 @@ export const updateSurvey = async (survey: ISurvey) => {
const newSurvey = await Survey.findOneAndUpdate({ _id: survey._id }, survey);
return newSurvey;
};
export const deleteSurvey = async (surveyId: string) => {
console.log("survey id", surveyId);
const survey = await Survey.findOneAndDelete({ _id: surveyId });
return survey;
};
import { model, Schema, Types } from "mongoose";
import { Question } from ".";
export interface ISurvey {
_id?: Types.ObjectId;
......@@ -8,11 +9,25 @@ export interface ISurvey {
questions: Types.ObjectId[];
}
const schema = new Schema<ISurvey>({
title: { type: String },
comment: { type: String },
user: { type: Schema.Types.ObjectId, ref: "User" },
questions: [{ type: Schema.Types.ObjectId, ref: "Question" }],
});
const schema = new Schema<ISurvey>(
{
title: { type: String },
comment: { type: String },
user: { type: Schema.Types.ObjectId, ref: "User" },
questions: [{ type: Schema.Types.ObjectId, ref: "Question" }],
},
{ timestamps: true }
);
schema.pre(
"findOneAndDelete",
{ document: false, query: true },
async function (next) {
const doc = await this.model.findOne(this.getFilter());
const questions = doc.questions;
await Question.deleteMany({ _id: { $in: questions } });
next();
}
);
export default model<ISurvey>("Survey", schema);
......@@ -3,13 +3,20 @@ import { authCtrl, surveyCtrl } from "../controllers";
const router = express.Router();
router.route("/").get(authCtrl.requireLogin, surveyCtrl.getSurveys);
router.route("/create").post(authCtrl.requireLogin, surveyCtrl.createSurvey);
router
.route("/edit/:surveyId")
.get(authCtrl.requireLogin, authCtrl.authenticate, surveyCtrl.getSurveyById)
.put(authCtrl.requireLogin, authCtrl.authenticate, surveyCtrl.updateSurvey);
router.route("/")
.get(authCtrl.requireLogin,surveyCtrl.getSurveys);
router
.route("/delete/:surveyId")
.delete(
authCtrl.requireLogin,
authCtrl.authenticate,
surveyCtrl.deleteSurvey
);
router.param("surveyId", surveyCtrl.userBySurveyId);
export default router;
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