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

result, answer 로직 및 불필요한 폴더 파일 삭제

parent e7ae6d3f
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import { Login, SignUp } from "./auth";
import { Login, SignUp, RequireAuth } from "./auth";
import { NotFound } from "./commons";
import {
Profile,
......@@ -8,10 +8,15 @@ import {
Preview,
EditSurvey,
AnswerSurvey,
ResultSurvey,
} from "./surveys";
import { AnswerLayout, BaseLayout, ModifyLayout } from "./layouts";
import {
AnswerLayout,
BaseLayout,
ModifyLayout,
ResultLayout,
} from "./layouts";
import { Home } from "./home";
import { RequireAuth } from "./auth/RequireAuth";
export const MainRouter = () => {
return (
......@@ -21,6 +26,10 @@ export const MainRouter = () => {
<Route path="/" element={<Home />}></Route>
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<SignUp />} />
<Route element={<ResultLayout />}>
<Route path="/results/:surveyId" element={<ResultSurvey />} />
</Route>
<Route element={<AnswerLayout />}>
<Route path="/answers/:surveyId" element={<AnswerSurvey />} />
</Route>
......
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import App from "./App";
import { Login, SignUp } from "./auth";
import { RequireAuth } from "./auth/RequireAuth";
import { AnswerSurveyForm } from "./answers";
import { Home } from "./home";
import { Profile } from "./profile";
import { EditResultButton } from "./survey";
import { EditSurvey } from "./survey/EditSurvey";
import { ResultSurvey } from "./survey/ResultSurvey";
import { CompleteSurvey } from "./survey/CompleteSurvey";
import { SameSurvey } from "./survey/SameSurvey";
import { CreateSurvey } from "./survey/CreateSurvey";
export const SurveyRouter = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="login" element={<Login />} />
<Route path="signup" element={<SignUp />} />
<Route path="surveys/:surveyId/create" element={<CreateSurvey />} />
<Route path="surveys/:surveyId/" element={<EditResultButton />}>
<Route path="edit" element={<EditSurvey />} />
<Route path="result" element={<ResultSurvey />} />
</Route>
<Route path="survey/:surveyId" element={<AnswerSurveyForm />} />
<Route path="survey/complete" element={<CompleteSurvey />} />
<Route path="survey/same" element={<SameSurvey />} />
<Route
path="profile"
element={
<RequireAuth>
<Profile />
</RequireAuth>
}
/>
</Route>
</Routes>
</BrowserRouter>
);
};
import React from "react";
import { IQuestionData, AnswerQuestionType } from "../types";
import { ACheckboxForm } from "./ACheckboxForm";
import { ADateForm } from "./ADateForm";
import { ADropdownForm } from "./ADropdownForm";
import { AEssayForm } from "./AEssayForm";
import { AFileForm } from "./AFileForm";
import { ARadioForm } from "./ARadioForm";
import { ARatingForm } from "./ARatingForm";
type Props = {
question: IQuestionData;
answerQuestion: AnswerQuestionType;
addFiles: (oneFile: { questionId: string; file: File }) => void;
};
export const AQuestion = ({ question, answerQuestion, addFiles }: Props) => {
function getContent(question: IQuestionData) {
switch (question.type) {
case "essay":
return (
<AEssayForm element={question} answerQuestion={answerQuestion} />
);
case "radio":
return (
<ARadioForm element={question} answerQuestion={answerQuestion} />
);
case "checkbox":
return (
<ACheckboxForm element={question} answerQuestion={answerQuestion} />
);
case "dropdown":
return (
<ADropdownForm element={question} answerQuestion={answerQuestion} />
);
case "file":
return (
<AFileForm
element={question}
answerQuestion={answerQuestion}
addFiles={addFiles}
/>
);
case "rating":
return (
<ARatingForm element={question} answerQuestion={answerQuestion} />
);
case "date":
return <ADateForm element={question} answerQuestion={answerQuestion} />;
default:
return <></>;
}
}
return (
<div className="flex flex-col container w-4/5 h-auto border-2 border-themeColor items-center m-3 py-4 rounded-lg">
<div className="flex my-1 w-11/12 place-content-between items-center">
<div className="text-xl font-bold">{question.title}</div>
{question.isRequired ? (
<div className="text-xs text-red-600">* 필수질문</div>
) : (
<></>
)}
</div>
<div className="w-11/12 text-slate-500">{question.comment}</div>
{getContent(question)}
</div>
);
};
import React, { FormEvent, useEffect, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { surveyApi, answerApi } from "../apis";
import { catchErrors } from "../helpers";
import { AnswerSurveyType, AnswerType, ISurvey } from "../types";
import { AQuestion } from "./AQuestion";
export const AnswerSurveyForm = () => {
let { surveyId } = useParams<{ surveyId: string }>();
const [files, setFiles] = useState<{ questionId: string; file: File }[]>([]);
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const navigate = useNavigate();
const [survey, setSurvey] = useState<ISurvey>({
_id: surveyId || "",
user: {},
title: "",
comment: "",
questions: [],
});
const answerSurvey = useRef<AnswerSurveyType>({
_id: "surveyId",
user: {},
title: "",
comment: "",
questions: [],
});
useEffect(() => {
ansSurvey();
}, [surveyId]);
const isSurvey = sessionStorage.getItem(`survey_${surveyId}`);
if (isSurvey) {
console.log("object", isSurvey);
navigate("/survey/same");
}
const addFiles = (oneFile: { questionId: string; file: File }) => {
if (!files.find((a) => a.questionId === oneFile.questionId)) {
setFiles([...files, oneFile]);
}
};
async function ansSurvey() {
try {
if (surveyId) {
const getSurvey: any = await surveyApi.getSurveyById(surveyId);
console.log("survey가져옴ㅎㅎ", getSurvey);
if (getSurvey) {
answerSurvey.current._id = getSurvey._id;
answerSurvey.current.questions = getSurvey.questions;
setSurvey(getSurvey);
setSuccess(true);
setError("");
}
} else {
setLoading(true);
}
} catch (error) {
catchErrors(error, setError);
} finally {
setLoading(false);
}
}
async function handleSubmit(event: FormEvent) {
event.preventDefault();
const answers = answerSurvey.current.questions.map((q: any) => {
return { questionId: q._id, answer: q.answer, type: q.type };
});
const requiredErrorQ = answerSurvey.current.questions.find(
(q: any) => q.isRequired && q.isRequired !== q.requiredCheck
);
if (requiredErrorQ) {
alert("필수질문에 응답하지 않으셨습니다.");
} else {
try {
const formData = new FormData();
formData.append("surveyId", answerSurvey.current._id);
formData.append("guestId", "guest1");
formData.append("answers", JSON.stringify(answers));
files.map((f) => {
formData.append("uploadFiles", f.file);
});
const newAnswer: AnswerType = await answerApi.saveAnswers(formData);
console.log(newAnswer);
sessionStorage.setItem(`survey_${surveyId}`, surveyId ?? "");
navigate("/survey/complete", { replace: false });
setSuccess(true);
setError("");
} catch (error) {
catchErrors(error, setError);
} finally {
setLoading(false);
}
}
}
return (
<>
{console.log("rendering survey form", answerSurvey.current)}
<form onSubmit={handleSubmit}>
<div className="flex flex-col place-items-center">
<div className="flex flex-col container place-items-center mt-4">
<p className="font-bold text-4xl text-center m-2">{survey.title}</p>
<p className="font-bold text-1xl text-center m-2">
{survey.comment}
</p>
{survey.questions.map((question, index) => {
return (
<AQuestion
key={question._id}
question={question}
answerQuestion={answerSurvey.current.questions[index]}
addFiles={addFiles}
></AQuestion>
);
})}
<div>
<button
type="submit"
className="rounded bg-themeColor my-5 py-2 px-5 font-bold text-white"
>
제출하기
</button>
</div>
</div>
</div>
</form>
</>
);
};
export { AnswerSurveyForm } from "./AnswerSurveyForm";
import axios from "axios";
import { AnswerType } from "../types";
import { IAnswer, IAnswerRequestData } from "../types";
import baseUrl from "./baseUrl";
/**
* 파일을 제외한 json 형식의 답변 배열을 보내어 저장
* @param answers IAnswer 배열
* @returns 응답 배열?
*/
export const save = async (answers: IAnswerRequestData[]) => {
const { data } = await axios.post(`${baseUrl}/answers`, answers);
return data;
};
export const saveForm = async (answerForm: FormData) => {
const { data } = await axios.post(`${baseUrl}/answers/upload`, answerForm);
return data;
};
export const saveAnswers = async (answer: FormData) => {
const { data } = await axios.post(`${baseUrl}/answers`, answer);
return data;
......
export { Login } from "./Login";
export { AuthProvider, useAuth } from "./auth.context";
export { SignUp } from "./SignUp";
export { RequireAuth } from "./RequireAuth";
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { useAuth } from "../auth/auth.context";
import { useAuth } from "../auth";
import { UserIcon } from "../icons";
export const Header = () => {
......@@ -17,7 +17,7 @@ export const Header = () => {
<div className="bg-white border-b-2 border-b-themeColor px-2 sm:px-4 py-3.5">
<div className="container flex flex-wrap md:justify-start place-content-center mx-auto">
<Link to="/" className="font-bold text-2xl text-themeColor text-start">
Simple Survey Form
Simple Survey
</Link>
<div className="absolute right-4 top-2 hidden md:flex items-center justify-end md:flex-1">
{user.isLoggedIn ? (
......@@ -28,7 +28,7 @@ export const Header = () => {
>
로그아웃
</button>
<Link to="/profile">
<Link to="/surveys/profile">
<button className="font-bold text-gray-600 hover:text-themeColor mx-1 py-2 px-3 rounded-md">
프로필
</button>
......
export const QUESTION_TYPES = new Map([
["essay", "주관식"],
["radio", "객관식"],
["dropdown", "드롭다운"],
["checkbox", "체크박스"],
["file", "파일"],
["rating", "선형"],
["date", "날짜"],
]);
/**
* survey.js의 기본 타입들
* 참조: https://surveyjs.io/Documentation/Library?id=Base#getType
*
* "boolean": 둘 중 하나 선택
* "checkbox": 다중 선택
* "comment": 주관식 문자열 입력. 여러 줄 입력 가능(text(single input)와 비교)
* "dropdown": 드랍다운 형식으로 표시되고 한 개 선택
* "expression": 읽기 전용인데 용도가 정확히 뭔지 모르겠음.
* "file": 파일 업로드
* "html": ??
* "image": 이미지를 보여주기만 하는 것 같음. 문제가 아닌 것 같습니다.
* "imagepicker": 여러 개 그림 중 하나 선택
* "matrix": single choice matrix로 명명된 것 같고 여러 행이 나오는데 그 행에서 단 하나만 선택 가능
* "matrixdropdown": multiple choice matrix로 명명된 것 같고 여러 행에 여러 열이 나올 수 있고 원하는대로 선택
* "matrixdynamic": 여러 행을 추가/삭제할 수 있게 했고 열의 개수는 고정되어 있다.
* "multipletext": 여러 개의 주관식 문자열 입력할 때
* "panel": 여러 개의 질문을 넣을 수 있는 박스. 질문 안에 또 다른 질문을 넣을 때 사용??
* "paneldynamic": 만드는 사람이 미리 만든 질문들을 add New 버튼을 클릭하면 생길 수 있도록 한 것.
* "radiogroup": 라디오 버튼을 사용하여 한 개 선택 other를 선택하면 문자열 입력 가능
* "rating": 별 점 줄 때
* "ranking": 순위를 정할 때 사용; 드래그앤드랍 이용
* "signaturepad": 사인 입력할 때
* "text": 아마도 single input으로 변경된 것 같음. 이것은 한 문장 입력만 가능 comment는 여러 줄 입력 가능
*/
export enum QUESTION_TYPES {
singletext = "짧은글",
radio = "객관식",
dropdown = "드롭다운",
checkbox = "다중선택",
file = "파일",
rating = "점수",
date = "날짜",
// multitext = "긴글",
}
export function getEnumKeyByEnumValue<T extends { [index: string]: string }>(
myEnum: T,
enumValue: string
): keyof T | null {
let keys = Object.keys(myEnum).filter((x) => myEnum[x] === enumValue);
return keys.length > 0 ? keys[0] : null;
}
export { Header } from "./Header";
export { Footer } from "./Footer";
export { NotFound } from "./NotFound";
export { QUESTION_TYPES } from "./constants";
export { QUESTION_TYPES, getEnumKeyByEnumValue } from "./constants";
import React, { useState } from "react";
import { ICheckbox, AnswerProps } from "../types";
import { ICheckbox, IAnswerProps } from "../types";
interface Props extends AnswerProps {
element: ICheckbox;
}
export const ACheckboxForm = ({ element, answerQuestion }: Props) => {
const [answer, setAnswer] = useState("");
export const ACheckbox = ({
element,
answer: answerQuestion,
}: IAnswerProps) => {
const [content, setContent] = useState<string[]>([]);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.currentTarget;
if (answerQuestion.answer) {
if (answerQuestion.answer.find((a: any) => a === value)) {
const newList = answerQuestion.answer.filter((a: any) => a !== value);
answerQuestion.answer = newList;
if (answerQuestion.answer.length) {
answerQuestion.requiredCheck = true;
const { value, checked } = event.currentTarget;
// console.log("value:", value, "checked:", checked);
if (checked) {
content.push(value);
} else {
answerQuestion.requiredCheck = false;
const index = content.indexOf(value);
if (index !== -1) {
content.splice(index, 1);
}
} else {
answerQuestion.answer.push(value);
answerQuestion.requiredCheck = true;
}
} else {
answerQuestion.answer = [];
answerQuestion.answer.push(value);
// if (answerQuestion.content) {
// if (answerQuestion.content.find((a: any) => a === value)) {
// const newList = answerQuestion.content.filter((a: any) => a !== value);
// answerQuestion.content = newList;
// if (answerQuestion.content.length) {
// answerQuestion.requiredCheck = true;
// } else {
// answerQuestion.requiredCheck = false;
// }
// } else {
// answerQuestion.content.push(value);
// answerQuestion.requiredCheck = true;
// }
// } else {
// answerQuestion.content = [];
// answerQuestion.content.push(value);
// answerQuestion.requiredCheck = true;
// }
if (content.length > 0) {
answerQuestion.requiredCheck = true;
} else {
answerQuestion.requiredCheck = false;
}
setAnswer(value);
console.log(answerQuestion);
answerQuestion.content = [...content];
console.log("answer content", answerQuestion);
setContent([...content]);
};
// console.log("content:", content);
return (
<div className="flex w-full gap-2 justify-center my-3">
{element.content.choices.map((choice) => (
......
import React, { useState } from "react";
import { AnswerProps } from "../types";
import { IAnswerProps } from "../types";
export const ADateForm = ({ element, answerQuestion }: AnswerProps) => {
export const ADate = ({ element, answer: content }: IAnswerProps) => {
const [answer, setAnswer] = useState("");
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.currentTarget;
answerQuestion.answer = value;
content.content = value;
setAnswer(value);
if (answerQuestion.answer) {
answerQuestion.requiredCheck = true;
if (content.content) {
content.requiredCheck = true;
} else {
answerQuestion.requiredCheck = false;
content.requiredCheck = false;
}
console.log(answerQuestion);
console.log(content);
};
return (
<div className="justify-start w-11/12 m-3 py-4">
......
import React, { useState } from "react";
import { IDropdown, AnswerProps } from "../types";
import { IDropdown, IAnswerProps } from "../types";
interface Props extends AnswerProps {
element: IDropdown;
}
export const ADropdownForm = ({ element, answerQuestion }: Props) => {
export const ADropdown = ({
element,
answer: answerQuestion,
}: IAnswerProps) => {
const [answer, setAnswer] = useState("");
const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const { value } = event.currentTarget;
answerQuestion.answer = value;
answerQuestion.content = value;
setAnswer(value);
if (answerQuestion.answer) {
if (answerQuestion.content) {
answerQuestion.requiredCheck = true;
} else {
answerQuestion.requiredCheck = false;
......
import React, { useState } from "react";
import { AnswerProps } from "../types";
import { IAnswerProps } from "../types";
export const AEssayForm = ({ element, answerQuestion }: AnswerProps) => {
export const AEssay = ({ element, answer: answerQuestion }: IAnswerProps) => {
const [answer, setAnswer] = useState("");
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.currentTarget;
answerQuestion.answer = value;
answerQuestion.content = value;
setAnswer(value);
if (answerQuestion.answer) {
if (answerQuestion.content) {
answerQuestion.requiredCheck = true;
} else {
answerQuestion.requiredCheck = false;
......
import React, { useState } from "react";
import { AnswerProps } from "../types";
import { IAnswerProps } from "../types";
interface Props extends AnswerProps {
addFiles: (oneFile: { questionId: string; file: File }) => void;
interface Props extends IAnswerProps {
// addFiles: (oneFile: { questionId: string; file: File }) => void;
}
export const AFileForm = ({ element, answerQuestion, addFiles }: Props) => {
export const AFile = ({ element, answer: answerQuestion }: Props) => {
const [answer, setAnswer] = useState("");
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.currentTarget.files) {
const uploadFile = event.currentTarget.files[0];
addFiles({ questionId: element._id, file: uploadFile });
answerQuestion.answer = uploadFile.name;
if (answerQuestion.answer) {
const uploadFiles = event.currentTarget.files;
// const uploadFile = event.currentTarget.files[0];
// addFiles({ questionId: element._id, file: uploadFile });
// answerQuestion.content = uploadFile.name;
answerQuestion.content = uploadFiles;
if (answerQuestion.content) {
answerQuestion.requiredCheck = true;
} else {
answerQuestion.requiredCheck = false;
}
setAnswer(uploadFile.name);
setAnswer(uploadFiles[0].name);
console.log(answerQuestion);
}
};
......
import React, { useState } from "react";
import { IRadio, AnswerProps } from "../types";
import { IRadio, IAnswerProps } from "../types";
interface Props extends AnswerProps {
element: IRadio;
}
export const ARadioForm = ({ element, answerQuestion }: Props) => {
export const ARadio = ({ element, answer: answerQuestion }: IAnswerProps) => {
const [answer, setAnswer] = useState("");
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.currentTarget;
answerQuestion.answer = value;
answerQuestion.content = value;
setAnswer(value);
if (answerQuestion.answer) {
if (answerQuestion.content) {
answerQuestion.requiredCheck = true;
} else {
answerQuestion.requiredCheck = false;
......
import React, { useState } from "react";
import { IRating, AnswerProps } from "../types";
import { IRating, IAnswerProps } from "../types";
interface Props extends AnswerProps {
element: IRating;
}
export const ARatingForm = ({ element, answerQuestion }: Props) => {
export const ARating = ({ element, answer: answerQuestion }: IAnswerProps) => {
const [selectedchoice, setSelectedchoice] = useState("");
const [answer, setAnswer] = useState("");
function buttonClick(event: React.MouseEvent<HTMLButtonElement>) {
event.preventDefault();
const { name } = event.currentTarget;
answerQuestion.answer = name;
answerQuestion.content = name;
setAnswer(name);
setSelectedchoice(event.currentTarget.name);
if (answerQuestion.answer) {
if (answerQuestion.content) {
answerQuestion.requiredCheck = true;
} else {
answerQuestion.requiredCheck = false;
......
import React, { useState } from "react";
import { ICheckbox } from "../types";
import { IQuestionFormProps } from "../types";
type Props = {
element: ICheckbox;
handleQuestion: (id: string) => void;
isEditing: boolean;
};
export const CheckboxForm = ({ element, handleQuestion, isEditing }: Props) => {
export const QCheckbox = ({
element,
handleQuestion,
isEditing,
}: IQuestionFormProps) => {
const [choices, setChoices] = useState([...element.content.choices]);
function handleContent(event: React.ChangeEvent<HTMLInputElement>) {
......
import React from "react";
// import { DateType } from "../types";
type Props = {
// element: DateType;
// save: boolean;
};
export const DateForm = ({}: Props) => {
export const QDate = () => {
return (
<div id="content" className="flex mt-4 w-full justify-center">
<input type="date" className="w-11/12" disabled></input>
......
import React, { useState } from "react";
import { IDropdown } from "../types";
import { IQuestionFormProps } from "../types";
type Props = {
element: IDropdown;
handleQuestion: (id: string) => void;
isEditing: boolean;
};
export const DropdownForm = ({ element, handleQuestion, isEditing }: Props) => {
export const QDropdown = ({
element,
handleQuestion,
isEditing,
}: IQuestionFormProps) => {
const [choices, setChoices] = useState([...element.content.choices]);
function handleContent(event: React.ChangeEvent<HTMLInputElement>) {
......
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