Commit e19694ac authored by Jiwon Yoon's avatar Jiwon Yoon
Browse files

보안 강화 및 edit가능

parent 65353848
import React from "react";
import { Page } from "./Page";
import { QuestionProvider } from "./question.context";
export const CreateSurveyForm = () => {
return (
<>
<QuestionProvider>
<Page />
</QuestionProvider>
</>
);
};
import React from "react";
import { useQuestion } from "./question.context";
type Props = {
id: string;
};
export const Edit = ({ id }: Props) => {
const { editClick } = useQuestion();
return (
<button id={id} className="w-1/12" onClick={editClick}>
수정
</button>
);
};
import React from "react";
import { Questions } from "./Questions";
import { useQuestion } from "./question.context";
export const Page = () => {
const { handleSurvey, handleSubmit } = useQuestion();
return (
<>
<form onSubmit={handleSubmit}>
<div className="flex flex-col place-items-center">
<div className="flex flex-col container place-items-center mt-4">
<input
type="text"
name="title"
className="font-bold text-4xl text-center m-2 border-b-2"
placeholder="설문지 제목"
onChange={handleSurvey}
></input>
<input
type="text"
name="comment"
className="font-bold text-1xl text-center m-2 resize-none"
placeholder="설문조사에 대한 설명을 입력해주세요"
size={50}
onChange={handleSurvey}
></input>
</div>
<Questions />
<div>
<button
type="submit"
className="border bg-themeColor my-5 py-2 px-3 font-bold text-white"
>
설문조사 생성
</button>
</div>
</div>
</form>
</>
);
};
import React from "react";
import { CheckboxType } from "../types";
import { useQuestion } from "./question.context";
import { Edit } from "./Edit";
import { TypeChange } from "./typeDD";
type Props = {
element: CheckboxType;
};
export const QCheckbox = ({ element }: Props) => {
const { questionListChange } = useQuestion();
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 flexgi-row h-16 w-full place-content-between items-center">
<input
type="text"
name="title"
id={element._id}
className="text-xl font-bold ml-6 border-b-2 w-1/2"
placeholder={element.title}
onChange={questionListChange}
></input>
<TypeChange tt="checkbox" />
</div>
<div className="flex w-full justify-center">
<input
type="text"
name="comment"
id={element._id}
className="border w-11/12"
placeholder="질문에 대한 설명을 입력해주세요"
onChange={questionListChange}
></input>
</div>
<div id="commentarea" className="flex mt-4">
{element.content.choices.map((e: any) => (
<div>
<input type="checkbox" checked={false}></input>
<input
type="text"
className="mx-2 border-b-2"
placeholder={e.text}
></input>
</div>
))}
</div>
<div className="flex w-full flex-row justify-end py-2">
<button className="w-1/12">필수</button>
<button className="w-1/12">옵션</button>
<button className="w-1/12">삭제</button>
<Edit id={element._id} />
</div>
</div>
);
};
import { useQuestion } from "./question.context";
import React from "react";
import { DropdownType } from "../types";
import { useQuestion } from "./question.context";
import { TypeChange } from "./typeDD";
type Props = {
element: DropdownType;
};
export const QDropdown = ({ element }: Props) => {
const { questionListChange } = useQuestion();
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 flexgi-row h-16 w-full place-content-between items-center">
<input
type="text"
name="title"
id={element._id}
className="text-xl font-bold ml-6 border-b-2 w-1/2"
placeholder={element.title}
onChange={questionListChange}
></input>
<TypeChange tt="dropdown" />
</div>
<div className="flex w-full justify-center">
<input
type="text"
name="comment"
id={element._id}
className="border w-11/12"
placeholder="질문에 대한 설명을 입력해주세요"
onChange={questionListChange}
></input>
</div>
<div id="commentarea" className="flex mt-4">
{element.content.choices.map((e: any) => (
<div>
<input type="checkbox" checked={false}></input>
<input
type="text"
className="mx-2 border-b-2"
placeholder={e.text}
></input>
</div>
))}
</div>
<div className="flex w-full flex-row justify-end py-2">
<button className="w-1/12">필수</button>
<button className="w-1/12">옵션</button>
<button className="w-1/12">삭제</button>
</div>
</div>
);
};
import React, { useState } from "react";
import { EssayType } from "../types";
import { useQuestion } from "./question.context";
import { Edit } from "./Edit";
import { TypeChange } from "./typeDD";
type Props = {
element: EssayType;
};
export const QEssay = ({ element }: Props) => {
const { questionListChange } = useQuestion();
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">
<input
type="text"
name="title"
id={element._id}
className="text-xl font-bold ml-6 border-b-2 w-1/2"
placeholder={element.title}
onChange={questionListChange}
></input>
{/* <TypeChange tt={"essay"} id={element._id} /> */}
<TypeChange tt={"essay"} />
</div>
<div className="flex w-full justify-center">
<input
type="text"
name="comment"
id={element._id}
className="border w-11/12"
placeholder="질문에 대한 설명을 입력해주세요"
onChange={questionListChange}
></input>
</div>
<div id="commentarea" className="flex mt-4 w-full justify-center">
<input className="border w-11/12 h-16" disabled></input>
</div>
<div className="flex w-full justify-end py-2">
<button className="w-1/12">필수</button>
<button className="w-1/12">옵션</button>
<button className="w-1/12">삭제</button>
<Edit id={element._id} />
</div>
</div>
);
};
import React, { useState } from "react";
import { FileType } from "../types";
import { useQuestion } from "./question.context";
import { TypeChange } from "./typeDD";
type Props = {
element: FileType;
};
export const QFile = ({ element }: Props) => {
const { questionListChange } = useQuestion();
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">
<input
type="text"
name="title"
id={element._id}
className="text-xl font-bold ml-6 border-b-2 w-1/2"
placeholder={element.title}
onChange={questionListChange}
></input>
<TypeChange tt="file" />
</div>
<div className="flex w-full justify-center">
<input
type="text"
name="comment"
id={element._id}
className="border w-11/12"
placeholder="질문에 대한 설명을 입력해주세요"
onChange={questionListChange}
></input>
</div>
<div id="commentarea" className="flex mt-4 w-full justify-center">
<input type="file" className=" w-11/12 h-16" disabled></input>
</div>
<div className="flex w-full justify-end py-2">
<button className="w-1/12">필수</button>
<button className="w-1/12">옵션</button>
<button className="w-1/12">삭제</button>
</div>
</div>
);
};
import { useQuestion } from "./question.context";
import React from "react";
import { RadioType } from "../types";
import { useQuestion } from "./question.context";
import { TypeChange } from "./typeDD";
type Props = {
element: RadioType;
};
export const QRadio = ({ element }: Props) => {
const { questionListChange } = useQuestion();
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">
<input
type="text"
name="title"
id={element._id}
className="text-xl font-bold ml-6 border-b-2 w-1/2"
placeholder={element.title}
onChange={questionListChange}
></input>
<TypeChange tt="radio" />
</div>
<div className="flex w-full justify-center">
<input
type="text"
name="comment"
id={element._id}
className="border w-11/12"
placeholder="질문에 대한 설명을 입력해주세요"
onChange={questionListChange}
></input>
</div>
<div className="flex mt-4">
{element.content.choices.map((e: any, index: number) => (
<div>
<input
type="radio"
id={element._id}
name="choice"
value={e.text}
disabled
/>
<input
type="text"
name={"choice"}
// key={`${index}`}
className="mx-2 border-b-2"
placeholder={e.text}
onChange={questionListChange}
></input>
<button></button>
</div>
))}
{/* <button className="border rounded-full border-green-500 border-4 text-green-500 font-bold px-2">
+
</button> */}
</div>
<div className="flex w-full flex-row justify-end py-2">
<button className="w-1/12">필수</button>
<button className="w-1/12">옵션</button>
<button className="w-1/12">삭제</button>
</div>
</div>
);
};
import React from "react";
import { RatingType } from "../types";
import { useQuestion } from "./question.context";
import { TypeChange } from "./typeDD";
type Props = {
element: RatingType;
// deleteValue: (e: React.MouseEvent<HTMLButtonElement>) => void;
};
export const QRating = ({ element }: Props) => {
const { questionListChange } = useQuestion();
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">
<input
type="text"
name="title"
id={element._id}
className="text-xl font-bold ml-6 border-b-2 w-1/2"
placeholder={element.title}
onChange={questionListChange}
></input>
<TypeChange tt="rating" />
</div>
<div className="flex w-full justify-center">
<input
type="text"
name="comment"
id={element._id}
className="border w-11/12"
placeholder="질문에 대한 설명을 입력해주세요"
onChange={questionListChange}
></input>
</div>
<div className="flex place-content-between items-center p-5">
<input
name="minRateDescription"
id={element._id}
className="border-b-2 text-center"
size={10}
placeholder={element.content.minRateDescription}
></input>
{element.content.choices.map((e) => (
<input
name="text"
id={element._id}
type="text"
className="border border-black rounded-full py-1 m-2 text-center"
size={1}
placeholder={e.text}
></input>
))}
<input
name="maxRateDescription"
id={element._id}
className="border-b-2 text-center"
size={10}
placeholder={element.content.maxRateDescription}
></input>
</div>
<div>
<button
// type="button"
name="rateValues"
id={element._id}
className="border border-red-500 rounded mx-2 px-2"
// onClick={deleteValue}
>
삭제
</button>
<button className="border border-blue-500 rounded mx-2 px-2">
추가
</button>
</div>
<div className="flex w-full justify-end py-2">
<button className="w-1/12">필수</button>
<button className="w-1/12">옵션</button>
<button className="w-1/12">삭제</button>
</div>
</div>
);
};
import React, { useState } from "react";
import { QEssay } from "./QEssay";
import { QCheckbox } from "./QCheckbox";
import { QRadio } from "./QRadio";
import { QDropdown } from "./QDropdown";
import { QFile } from "./QFile";
import { QRating } from "./QRating";
import { useQuestion } from "./question.context";
type Props = {};
export const Questions = ({}: Props) => {
const { addQuestion, questionList, currentId } = useQuestion();
return (
<>
{console.log(questionList, currentId)}
{questionList.map((element) => {
switch (element.type) {
case "essay":
return <QEssay element={element} />;
case "radio":
return <QRadio element={element} />;
case "checkbox":
return <QCheckbox element={element} />;
case "dropdown":
return <QDropdown element={element} />;
case "file":
return <QFile element={element} />;
case "rating":
return <QRating element={element} />;
default:
break;
}
})}
<div className="flex w-4/5 content-center justify-center border-2 border-black h-8 mt-3">
<button type="button" onClick={addQuestion}>
질문 추가
</button>
</div>
</>
);
};
export { CreateSurveyForm } from "./CreateSurveyFormPage";
export { Questions } from "./Questions";
import React, {
createContext,
FC,
ReactNode,
useContext,
useState,
} from "react";
import { BasicQuestionType, SurveyType } from "../types";
import { questionApi, surveyApi } from "../apis";
interface IQuestionContext {
handleSurvey: (e: React.ChangeEvent<HTMLInputElement>) => void;
handleSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
questionListChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
questionList: BasicQuestionType[];
editClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
editCompleteClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
currentId: string;
addQuestion: (e: React.MouseEvent<HTMLButtonElement>) => Promise<void>;
questionTypeChange: (e: React.ChangeEvent<HTMLSelectElement>) => void;
}
const QuestionContext = createContext<IQuestionContext>({
handleSurvey: () => {},
handleSubmit: () => {},
questionListChange: () => {},
questionList: [],
editClick: () => {},
editCompleteClick: () => {},
currentId: "",
addQuestion: async () => {},
questionTypeChange: () => {},
});
export const QuestionProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [error, setError] = useState("");
const [disabled, setDisabled] = useState(false);
const [success, setSuccess] = useState(false);
const [questionList, setQuestionList] = useState<Array<BasicQuestionType>>(
[]
);
const [currentId, setCurrentId] = useState<string>("");
const [survey, setSurvey] = useState<SurveyType>({
title: "",
comment: "",
//questions 는 _id들의 배열
questions: [],
});
function handleSurvey(event: React.ChangeEvent<HTMLInputElement>) {
setSurvey({
...survey,
[event.currentTarget.name]: event.currentTarget.value,
});
}
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
try {
const newSurvey: SurveyType = await surveyApi.createSurvey(survey);
console.log(newSurvey);
// setSuccess(true);
// setError("");
} catch (error) {
console.log("에러발생");
// catchErrors(error, setError)
} finally {
// setLoading(false);
}
}
function questionListChange(e: React.ChangeEvent<HTMLInputElement>): void {
const newList: BasicQuestionType[] = [...questionList];
const obj: any = newList.find((a) => a._id === e.target.id); //고유 _id로 질문찾기
const targetKey: any = e.target.name;
obj[targetKey] = e.target.value;
setQuestionList(newList);
}
function questionTypeChange(e: React.ChangeEvent<HTMLSelectElement>): void {
const newType: BasicQuestionType[] = [...questionList];
const objType: any = newType.find((t) => t._id === e.target.id);
const targetType: string = e.target.name;
objType[targetType] = e.target.value;
setQuestionList(newType);
}
async function addQuestion() {
try {
const newQ: BasicQuestionType = await questionApi.createQuestion();
setSurvey({ ...survey, questions: [...survey.questions, newQ] });
setQuestionList([...questionList, newQ]);
// setSuccess(true);
// setError("");
} catch (error) {
console.log("에러발생");
// catchErrors(error, setError)
} finally {
// setLoading(false);
}
}
function editClick(e: React.MouseEvent<HTMLButtonElement>) {
setCurrentId(e.currentTarget.id);
}
function editCompleteClick(e: React.MouseEvent<HTMLButtonElement>) {}
return (
<QuestionContext.Provider
value={{
handleSurvey,
handleSubmit,
questionListChange,
addQuestion,
questionList,
editClick,
editCompleteClick,
currentId,
questionTypeChange,
}}
>
{children}
</QuestionContext.Provider>
);
};
export const useQuestion = () => useContext(QuestionContext);
import React, { ChangeEvent, useState } from "react";
import { useQuestion } from "./question.context";
type typeChangeProps = {
tt: string;
};
export function TypeChange({ tt }: typeChangeProps) {
const { questionTypeChange } = useQuestion();
const typeDD = new Map([
["essay", "주관식"],
["radio", "객관식"],
["dropdown", "드롭다운(객관식)"],
["checkbox", "체크박스"],
["file", "파일"],
["rating", "선형"],
["grid", "그리드"],
["date", "날짜"],
]);
function changeDD(e: React.ChangeEvent<HTMLSelectElement>) {
const tt = e.target.value;
// questionTypeChange(e);
console.log(tt);
//if문으로 type별로 content 바뀌게 해보기
}
return (
<select
id="Questions"
name="type"
className="w-36 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-themeColor w-full mr-3 p-2.5"
defaultValue={tt}
onChange={changeDD}
>
{Array.from(typeDD.entries()).map(([k, v]) => (
<option value={k}>{v}</option>
))}
</select>
);
}
...@@ -4,10 +4,9 @@ import App from "./App"; ...@@ -4,10 +4,9 @@ import App from "./App";
import { Login, SignUp } from "./auth"; import { Login, SignUp } from "./auth";
import { RequireAuth } from "./auth/RequireAuth"; import { RequireAuth } from "./auth/RequireAuth";
import { SurveyForm } from "./commons"; import { SurveyForm } from "./commons";
import { CreateSurveyForm } from "./CreateSurveyForm";
import { Home } from "./home"; import { Home } from "./home";
import { Profile } from "./profile"; import { Profile } from "./profile";
import { CreateSurvey } from "./survey"; import { EditSurvey } from "./survey";
export const SurveyRouter = () => { export const SurveyRouter = () => {
return ( return (
...@@ -17,7 +16,7 @@ export const SurveyRouter = () => { ...@@ -17,7 +16,7 @@ export const SurveyRouter = () => {
<Route index element={<Home />} /> <Route index element={<Home />} />
<Route path="login" element={<Login />} /> <Route path="login" element={<Login />} />
<Route path="signup" element={<SignUp />} /> <Route path="signup" element={<SignUp />} />
<Route path="surveys/create" element={<CreateSurvey />} /> <Route path="surveys/edit/:surveyId" element={<EditSurvey />} />
<Route path="survey" element={<SurveyForm />} /> <Route path="survey" element={<SurveyForm />} />
<Route <Route
path="profile" path="profile"
......
...@@ -5,20 +5,23 @@ import baseUrl from "./baseUrl"; ...@@ -5,20 +5,23 @@ import baseUrl from "./baseUrl";
export const createQuestion = async () => { export const createQuestion = async () => {
const { data } = await axios.post(`${baseUrl}/questions/create`, { const { data } = await axios.post(`${baseUrl}/questions/create`, {
type: "essay", type: "essay",
title: "Question Title", title: "",
isRequired: false, isRequired: false,
comment: "질문에 대한 설명을 입력해주세요", comment: "",
content: { choices: [] }, content: { choices: [] },
}); });
return data; return data;
}; };
export const updateQuestion = async (question: BasicQuestionType) => { export const updateQuestion = async (question: BasicQuestionType) => {
const { data } = await axios.post(`${baseUrl}/questions/update`, question); const { data } = await axios.put(
`${baseUrl}/questions/update/${question._id}`,
question
);
return data; return data;
}; };
export const deleteQuestion = async (id: string) => { export const deleteQuestion = async (id: string) => {
const { data } = await axios.post(`${baseUrl}/questions/delete`, { id: id }); const { data } = await axios.delete(`${baseUrl}/questions/delete/${id}`);
return data; return data;
}; };
...@@ -2,8 +2,20 @@ import axios from "axios"; ...@@ -2,8 +2,20 @@ import axios from "axios";
import { SurveyType } from "../types"; import { SurveyType } from "../types";
import baseUrl from "./baseUrl"; import baseUrl from "./baseUrl";
export const createSurvey = async (survey:SurveyType) => { export const createSurvey = async (survey: SurveyType) => {
console.log(survey) const { data } = await axios.post(`${baseUrl}/surveys/create`, survey);
const {data} = await axios.post(`${baseUrl}/surveys/create`, {...survey})
return data; return data;
} };
\ No newline at end of file
export const getSurvey = async (surveyId: string) => {
const { data } = await axios.get(`${baseUrl}/surveys/edit/${surveyId}`);
return data;
};
export const editSurvey = async (survey: SurveyType) => {
const { data } = await axios.put(
`${baseUrl}/surveys/edit/${survey._id}`,
survey
);
return data;
};
import React from "react"; import React, { useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { surveyApi } from "../apis";
import { SurveyType } from "../types";
export const Profile = () => { export const Profile = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [survey, setSurvey] = useState<SurveyType>({
const createSurvey = () => { user: {},
title: "",
comment: "",
questions: [],
});
async function createSurvey() {
// 먼저 서버에 survey 테이블에 새로운 survey 항목 추가 로직 필요 // 먼저 서버에 survey 테이블에 새로운 survey 항목 추가 로직 필요
navigate("/surveys/create", { replace: true }); const newSurvey: SurveyType = await surveyApi.createSurvey(survey);
}; navigate(`/surveys/edit/${newSurvey._id}`, {
replace: true,
});
}
return ( return (
<div className="flex flex-col items-center"> <div className="flex flex-col items-center">
......
import React, { useState } from "react"; import React, { useState, Dispatch, SetStateAction } from "react";
import { BasicQuestionType, EssayType } from "../types"; import { BasicQuestionType, EssayType } from "../types";
import { questionApi } from "../apis"; import { questionApi } from "../apis";
import { EssayForm } from "./EssayForm"; import { EssayForm } from "./EssayForm";
...@@ -12,6 +12,8 @@ type Props = { ...@@ -12,6 +12,8 @@ type Props = {
element: BasicQuestionType; element: BasicQuestionType;
handleQuestion: (id: string) => void; handleQuestion: (id: string) => void;
deleteQuestion: (id: string) => void; deleteQuestion: (id: string) => void;
changeCurrentId: (id: string) => void;
currentId: string;
}; };
const typeDropDown = new Map([ const typeDropDown = new Map([
...@@ -29,9 +31,12 @@ export const Question = ({ ...@@ -29,9 +31,12 @@ export const Question = ({
element, element,
handleQuestion, handleQuestion,
deleteQuestion, deleteQuestion,
changeCurrentId,
currentId,
}: Props) => { }: Props) => {
const handleEdit = () => { const handleEdit = () => {
//setCurrentId해주고 currentId===element._id가 같은 input들만 disabled=false //setCurrentId해주고 currentId===element._id가 같은 input들만 disabled=false
changeCurrentId(element._id);
}; };
async function handleComplete() { async function handleComplete() {
//db에서 element._id인 애를 findOneAndUpdate() 해준다. //db에서 element._id인 애를 findOneAndUpdate() 해준다.
...@@ -167,12 +172,15 @@ export const Question = ({ ...@@ -167,12 +172,15 @@ export const Question = ({
<button type="button" className="px-1" onClick={handleDelete}> <button type="button" className="px-1" onClick={handleDelete}>
삭제 삭제
</button> </button>
<button type="button" className="px-1" onClick={handleEdit}> {currentId === element._id ? (
수정
</button>
<button type="button" className="px-1" onClick={handleComplete}> <button type="button" className="px-1" onClick={handleComplete}>
완료 수정완료
</button>
) : (
<button type="button" className="px-1" onClick={handleEdit}>
수정하기
</button> </button>
)}
</div> </div>
</div> </div>
); );
......
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