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

Merge branch 'edit-survey'

parents 1cec063b b0b76f7f
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="301px" height="211px" viewBox="-0.5 -0.5 301 211" content="&lt;mxfile&gt;&lt;diagram id=&quot;YBBmzcxktw8_dhlKI17t&quot; name=&quot;페이지-1&quot;&gt;zVVNc4MgEP013FUSY45Naj8OPeXQM5WtMoPiIH6kv74YMGow03Y6k/ai7OMh+x7sivA+7x4lKbMXQYGjwKMdwvcoCHCw0s8eOBpghSMDpJJRA/kjcGAfYEHPojWjUM2ISgiuWDkHE1EUkKgZRqQU7Zz2Lvh815Kk4ACHhHAXfWVUZQaNgs2IPwFLs2FnP9yamZwMZKukyggV7QTCMcJ7KYQyo7zbA++9G3wx6x6uzJ4Tk1Co7yywB9EQXlttNi91HMQC1dptWIhCv3aZyrmOfD009J5zNQMLVaKWiWXZXRSRKVgWPqvXtwZEDkoeNUUCJ4o1868Te37pmTdK1AOrcllx+G8Ur2+kOHAUPxeVIj3nQrgUdUGhX+ZpoW3GFBxKcpLQ6kJe8mFBdgNSQTeBXI1DI1jZMrB9wMc2bseq8odSySYVFXq/twU7tgidecOgdS8E57q1wNeezA28YseCad92KNjc0KG1WyrxHdpFKPJQvEfbGG09xyuZifytrv7Aq3V4cZsC16towaro51bpcOzRp7nJjw7Hnw==&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<path d="M 60 70 L 60 143.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 60 148.88 L 56.5 141.88 L 60 143.63 L 63.5 141.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 120 40 L 213.63 40" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 218.88 40 L 211.88 43.5 L 213.63 40 L 211.88 36.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<rect x="0" y="10" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 40px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Install
</div>
</div>
</div>
</foreignObject>
<text x="60" y="44" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Install
</text>
</switch>
</g>
<ellipse cx="60" cy="180" rx="60" ry="30" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 180px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
overview
</div>
</div>
</div>
</foreignObject>
<text x="60" y="184" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
overview
</text>
</switch>
</g>
<path d="M 260 0 L 300 40 L 260 80 L 220 40 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 40px; margin-left: 221px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
글자
</div>
</div>
</div>
</foreignObject>
<text x="260" y="44" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
글자
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Viewer does not support full SVG 1.1
</text>
</a>
</switch>
</svg>
\ No newline at end of file
# 개관
![설치](./install.drawio.svg)
# 할일
## 리덕스
리덕스는 전역 상태를 관리하는 프레임워크입니다.
### 리덕스 데이터 비동기
1. 리덕스와 프론트 api와 연결을 해서 api에서 서버에서 데이터를 가져오면 리덕스 상태를 변경시켜야 하고 변경된 상태가 컴포넌트에 즉시 적용되도록 해야하는 문제를 연구해야 합니다.
- [리덕스 홈페이지 비동기 로직](https://redux.js.org/tutorials/fundamentals/part-6-async-logic)
import axios from "axios";
import { ISurvey } from "../types";
import { CreateQuestionData, IQuestionData, ISurvey } from "../types";
import baseUrl from "./baseUrl";
export const addQuestion = async (
surveyId: string,
question: IQuestionData
) => {
const { data } = await axios.post(
`${baseUrl}/surveys/${surveyId}/questions`,
question
);
return data;
};
export const createSurvey = async (survey: ISurvey) => {
const { data } = await axios.post(`${baseUrl}/surveys`, survey);
return data;
};
export const deleteQuestion = async (surveyId: string, questionId: string) => {
const { data } = await axios.delete(
`${baseUrl}/surveys/${surveyId}/questions/${questionId}`
);
return data;
};
export const deleteSurvey = async (surveyId: string) => {
const { data } = await axios.delete(`${baseUrl}/surveys/${surveyId}`);
return data;
};
export const getSurvey = async (surveyId: string) => {
const { data } = await axios.get(`${baseUrl}/surveys/${surveyId}/edit`);
return data;
......@@ -22,11 +45,6 @@ export const getSurveys = async () => {
return data;
};
export const updateSurvey = async (survey: ISurvey) => {
const { data } = await axios.put(`${baseUrl}/surveys/${survey._id}`, survey);
return data;
};
export const resultSurvey = async (survey: ISurvey) => {
const { data } = await axios.put(
`${baseUrl}/surveys/${survey._id}/result`,
......@@ -35,7 +53,18 @@ export const resultSurvey = async (survey: ISurvey) => {
return data;
};
export const deleteSurvey = async (surveyId: string) => {
const { data } = await axios.delete(`${baseUrl}/surveys/${surveyId}`);
export const updateQuestion = async (
surveyId: string,
question: CreateQuestionData
) => {
const { data } = await axios.put(
`${baseUrl}/surveys/${surveyId}/questions/${question._id}`,
question
);
return data;
};
export const updateSurvey = async (survey: ISurvey) => {
const { data } = await axios.put(`${baseUrl}/surveys/${survey._id}`, survey);
return data;
};
......@@ -2,12 +2,16 @@ import React from "react";
import { NavLink, useOutletContext } from "react-router-dom";
import { Outlet, useNavigate, useParams } from "react-router-dom";
import { useSurveys } from "./SurveysLayout";
import type { ICreateSurvey, ISurvey } from "../types";
import type { CreateQuestionData, ICreateSurvey, ISurvey } from "../types";
import { SpinnerIcon } from "../icons";
import { surveyApi } from "../apis";
type SurveyContextType = {
survey: ICreateSurvey;
update: (survey: ISurvey) => Promise<any>;
createQuestion: (question: CreateQuestionData) => Promise<any>;
removeQuestion: (questionId: string) => Promise<any>;
updateQuestion: (question: CreateQuestionData) => Promise<any>;
};
const activeStyle =
......@@ -16,11 +20,11 @@ const inActiveStyle =
"w-36 h-12 flex justify-center items-center bg-white border border-themeColor p-1 text-center font-bold text-xl";
export const SurveyLayout = () => {
const { surveys, update } = useSurveys();
const { surveys, update, updateLocalSurveysList } = useSurveys();
let { surveyId } = useParams<{ surveyId: string }>();
const survey = surveys.find((survey) => survey._id === surveyId);
console.log("surveys in survey layout", surveys);
// console.log("surveys in survey layout", surveys);
if (!survey) {
return (
......@@ -30,6 +34,40 @@ export const SurveyLayout = () => {
);
}
const createQuestion = async (question: CreateQuestionData) => {
const newQuestion = await surveyApi.addQuestion(survey._id!, question);
console.log("new question:", newQuestion);
survey.questions.push(newQuestion);
updateLocalSurveysList(survey);
};
const removeQuestion = async (questionId: string) => {
await surveyApi.deleteQuestion(survey._id!, questionId);
const questions = survey.questions;
const updatedQuestions = questions.filter((q) => q._id !== questionId);
console.log("questions after deleted question:", updatedQuestions);
survey.questions = updatedQuestions;
updateLocalSurveysList(survey);
};
const updateQuestion = async (question: CreateQuestionData) => {
await surveyApi.updateQuestion(survey._id!, question);
const questions = survey.questions;
const index = questions.findIndex((q) => q._id === question._id);
if (index < 0) {
return;
}
questions[index] = question;
console.log("questions in update question:", questions);
// setQuestions([...questions]);
survey.questions = questions;
updateLocalSurveysList(survey);
};
return (
<div>
<div className="flex justify-center items-center mt-6">
......@@ -61,7 +99,15 @@ export const SurveyLayout = () => {
응답결과
</NavLink>
</div>
<Outlet context={{ survey, update }} />
<Outlet
context={{
survey,
createQuestion,
removeQuestion,
update,
updateQuestion,
}}
/>
</div>
);
};
......
......@@ -11,6 +11,7 @@ type SurveysContextType = {
create: () => Promise<any>;
remove: (id: string) => Promise<any>;
update: (survey: ICreateSurvey) => Promise<any>;
updateLocalSurveysList: (survey: ICreateSurvey) => void;
};
export const SurveysLayout = () => {
......@@ -38,14 +39,25 @@ export const SurveysLayout = () => {
* @param surveyData 바꾸려는 설문 객체
*/
const update = async (surveyData: ICreateSurvey) => {
// const result = await surveyApi.updateSurvey(surveyData);
// const index = surveys.findIndex((survey) => survey._id === result._id);
// surveys[index] = result;
const result = await surveyApi.updateSurvey(surveyData);
const index = surveys.findIndex((survey) => survey._id === result._id);
surveys[index] = result;
// const index = surveys.findIndex((survey) => survey._id === surveyData._id);
// surveys[index] = surveyData;
// console.log("update in surveys layout layout:", surveyData);
console.log("updated survey data:", result);
setSurveys([...surveys]);
// return result;
};
const updateLocalSurveysList = (surveyData: ICreateSurvey) => {
const index = surveys.findIndex((survey) => survey._id === surveyData._id);
surveys[index] = surveyData;
console.log("update in surveys layout layout:", surveyData);
// const index = surveys.findIndex((survey) => survey._id === surveyData._id);
// surveys[index] = surveyData;
// console.log("update in surveys layout layout:", surveyData);
console.log("updated local survey data:", surveyData);
setSurveys([...surveys]);
// return result;
};
/**
......@@ -74,7 +86,17 @@ export const SurveysLayout = () => {
return (
<>
<Outlet context={{ error, loading, surveys, create, remove, update }} />
<Outlet
context={{
error,
loading,
surveys,
create,
remove,
update,
updateLocalSurveysList,
}}
/>
</>
);
};
......
......@@ -8,7 +8,8 @@ import { SpinnerIcon } from "../icons";
import { ModifySurveyView } from "./ModifySurveyView";
export const EditSurvey = () => {
const { survey, update } = useSurvey();
const { survey, createQuestion, removeQuestion, update, updateQuestion } =
useSurvey();
// const [survey, setSurvey] = useState<ISurvey>(surveyData);
// const [questions, setQuestions] = useState<CreateQuestionData[]>(() => {
// const questions = survey.questions;
......@@ -36,21 +37,20 @@ export const EditSurvey = () => {
* @param question 수정할 질문
* @returns 없음
*/
const updateQuestion = (question: CreateQuestionData) => {
const index = questions.findIndex((q) => q._id === question._id);
if (index < 0) {
return;
}
questions[index] = question;
console.log("questions in update question:", questions);
// setQuestions([...questions]);
survey.questions = questions;
update(survey);
};
// const updateQuestion = (question: CreateQuestionData) => {
// const index = questions.findIndex((q) => q._id === question._id);
// if (index < 0) {
// return;
// }
// questions[index] = question;
// console.log("questions in update question:", questions);
// // setQuestions([...questions]);
// survey.questions = questions;
// update(survey);
// };
const addQuestion = () => {
const addQuestion = async () => {
const question: CreateQuestionData = {
_id: Math.random().toString(),
order: questions.length,
type: "singletext",
title: "",
......@@ -59,11 +59,16 @@ export const EditSurvey = () => {
content: { choices: [] },
isEditing: true,
};
// const updatedSurvey = await surveyApi.addQuestion(survey._id!, question);
await createQuestion(question);
// console.log("new question:", updatedSurvey);
// await update(updatedSurvey);
// setQuestions([...questions, question]);
};
async function deleteQuestion(id: string) {
const delQuestions = questions.filter((question) => question._id !== id);
await removeQuestion(id);
// const delQuestions = questions.filter((question) => question._id !== id);
// setQuestions(delQuestions);
}
......@@ -83,7 +88,7 @@ export const EditSurvey = () => {
deleteQuestion={deleteQuestion}
handleQuestion={updateQuestion}
handleTitle={handleTitle}
callApi={update}
// callApi={update}
/>
);
};
......@@ -13,7 +13,7 @@ type Props = {
deleteQuestion: (id: string) => void;
handleQuestion: (question: CreateQuestionData) => void;
handleTitle: Function;
callApi: (surveyData: ISurvey) => Promise<any>;
// callApi: (surveyData: ISurvey) => Promise<any>;
};
export const ModifySurveyView = ({
......@@ -23,8 +23,8 @@ export const ModifySurveyView = ({
deleteQuestion,
handleQuestion,
handleTitle,
callApi,
}: Props) => {
}: // callApi,
Props) => {
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
// const [survey, setSurvey] = useState<ISurvey>(surveyData);
......
......@@ -28,9 +28,7 @@ export const createQuestion = asyncWrap(
return res.json(newQuestion);
}
} catch (error: any) {
return res
.status(500)
.send(error.message || "질문을 생성하는 중 오류 발생");
return res.status(500).send(error.message || "질문 생성 오류");
}
}
);
......
import { NextFunction, Request, Response } from "express";
import { Types } from "mongoose";
import { surveyDb } from "../db";
import { questionDb, surveyDb } from "../db";
import { asyncWrap } from "../helpers/asyncWrap";
import { ISurvey } from "../models";
......@@ -9,6 +9,23 @@ export interface TypedRequestAuth<T> extends Request {
user: any;
}
/**
* 설문에 새로운 질문을 추가
*/
export const addQuestion = asyncWrap(async (reqExp: Request, res: Response) => {
const req = reqExp as TypedRequestAuth<{ userId: string }>;
// Question controller 이용 질문 생성
const { userId } = req.auth;
const { _id, ...questionInput } = req.body;
questionInput.user = userId;
const newQuestion = await questionDb.createQuestion(questionInput);
// 생성된 질문을 survey에 추가
const { surveyId } = req.params;
await surveyDb.addQuestion(surveyId, newQuestion);
res.json(newQuestion);
});
export const createSurvey = asyncWrap(
async (reqExp: Request, res: Response) => {
const req = reqExp as TypedRequestAuth<{ userId: string }>;
......@@ -21,6 +38,19 @@ export const createSurvey = asyncWrap(
}
);
export const deleteQuestion = asyncWrap(async (req, res) => {
const { surveyId, questionId } = req.params;
const deletedQuestion = await questionDb.deleteQuestionById(questionId);
const survey = await surveyDb.removeQuestion(surveyId, questionId);
return res.json(deletedQuestion);
});
export const deleteSurvey = asyncWrap(async (req, res) => {
const { surveyId } = req.params;
const survey = await surveyDb.deleteSurvey(surveyId);
return res.json(survey);
});
export const getSurveyById = asyncWrap(async (req, res) => {
const { surveyId } = req.params;
const survey: any = await surveyDb.getSurveyById(surveyId);
......@@ -41,12 +71,6 @@ 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,
......
import { HydratedDocument } from "mongoose";
import { Survey, ISurvey, Question, IQuestion } from "../models";
export const findUserBySurveyId = async (surveyId: string) => {
const survey = await Survey.findById(surveyId).populate("user");
console.log(survey);
if (survey !== null) {
console.log(survey.user);
return survey.user;
export const addQuestion = async (surveyId: string, question: any) => {
if (question !== null) {
const updatedSurvey = await Survey.findOneAndUpdate(
{ _id: surveyId },
{ $push: { questions: question } },
{ new: true }
).populate("questions");
return updatedSurvey;
}
return null;
};
......@@ -32,6 +34,32 @@ export const createSurvey = async (surveyData: ISurvey) => {
return newSurvey;
};
export const removeQuestion = async (surveyId: string, questionId: string) => {
const updatedSurvey = await Survey.findOneAndUpdate(
{ _id: surveyId },
{ $pull: { questions: questionId } },
{ new: true }
);
return updatedSurvey;
};
export const deleteSurvey = async (surveyId: string) => {
console.log("survey id", surveyId);
const survey = await Survey.findOneAndDelete({ _id: surveyId });
return survey;
};
export const findUserBySurveyId = async (surveyId: string) => {
const survey = await Survey.findById(surveyId).populate("user");
console.log(survey);
if (survey !== null) {
console.log(survey.user);
return survey.user;
}
return null;
};
export const getSurveyById = async (surveyId: string) => {
console.log("survey id", surveyId);
const survey = await Survey.findById(surveyId).populate("questions");
......@@ -61,12 +89,6 @@ export const updateSurvey = async (survey: HydratedDocument<ISurvey>) => {
return newSurvey;
};
export const deleteSurvey = async (surveyId: string) => {
console.log("survey id", surveyId);
const survey = await Survey.findOneAndDelete({ _id: surveyId });
return survey;
};
export const putNewQuestion = async (newQuestion: any, surveyId: string) => {
console.log(newQuestion, surveyId);
if (newQuestion !== null) {
......
......@@ -20,10 +20,14 @@ router
router
.route("/:surveyId/questions")
.post(
.post(authCtrl.requireLogin, authCtrl.authenticate, surveyCtrl.addQuestion);
router
.route("/:surveyId/questions/:questionId")
.delete(
authCtrl.requireLogin,
authCtrl.authenticate,
questionCtrl.createQuestion
surveyCtrl.deleteQuestion
);
router.param("surveyId", surveyCtrl.userBySurveyId);
......
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