Commit 10f7f53d authored by Jiwon Yoon's avatar Jiwon Yoon
Browse files

result aggregate, answer props

parent 77262376
......@@ -13,6 +13,7 @@ export const save = async (answers: IAnswerRequestData[]) => {
};
export const saveForm = async (answerForm: FormData) => {
console.log("formdata", answerForm);
const { data } = await axios.post(`${baseUrl}/answers/upload`, answerForm);
return data;
};
......
......@@ -3,10 +3,11 @@ import { IQuestionData } from "../types";
type Props = {
question: IQuestionData;
answers: any;
};
export const RCheckbox = ({ question }: Props) => {
const result = question.answers.flat().reduce((acc: any, cur: any) => {
export const RCheckbox = ({ question, answers }: Props) => {
const result = answers.flat().reduce((acc: any, cur: any) => {
acc[cur] = (acc[cur] || 0) + 1;
return acc;
}, {});
......
......@@ -3,12 +3,13 @@ import { IQuestionData } from "../types";
type Props = {
question: IQuestionData;
answers: any;
};
export const RDate = ({ question }: Props) => {
export const RDate = ({ question, answers }: Props) => {
return (
<div className="m-5">
{question.answers.map((answer: any) => (
{answers.map((answer: any) => (
<div key={answer} className="font-bold">
{answer}
</div>
......
......@@ -3,10 +3,11 @@ import { IQuestionData } from "../types";
type Props = {
question: IQuestionData;
answers: any;
};
export const RDropdown = ({ question }: Props) => {
const result = question.answers.reduce((acc: any, cur: any) => {
export const RDropdown = ({ question, answers }: Props) => {
const result = answers.reduce((acc: any, cur: any) => {
acc[cur] = (acc[cur] || 0) + 1;
return acc;
}, {});
......
......@@ -3,12 +3,13 @@ import { IQuestionData } from "../types";
type Props = {
question: IQuestionData;
answers: any;
};
export const REssay = ({ question }: Props) => {
export const REssay = ({ question, answers }: Props) => {
return (
<div className="m-5">
{question.answers.map((answer: any, index: number) => (
{answers.map((answer: any, index: number) => (
<div key={index} className="font-bold">
{answer}
</div>
......
......@@ -3,13 +3,14 @@ import { baseImageUrl } from "../apis";
type Props = {
question: any;
answers: any;
};
export const RFile = ({ question }: Props) => {
export const RFile = ({ question, answers }: Props) => {
console.log("question", question);
return (
<div className="m-5 flex justify-start items-center">
{question.answers.map((answer: any, index: number) => (
{answers.map((answer: any, index: number) => (
<Fragment key={index}>
<img
className="h-14"
......
......@@ -3,10 +3,11 @@ import { IQuestionData } from "../types";
type Props = {
question: IQuestionData;
answers: any;
};
export const RRadio = ({ question }: Props) => {
const result = question.answers.reduce((acc: any, cur: any) => {
export const RRadio = ({ question, answers }: Props) => {
const result = answers.reduce((acc: any, cur: any) => {
acc[cur] = (acc[cur] || 0) + 1;
return acc;
}, {});
......
......@@ -3,10 +3,11 @@ import { IQuestionData } from "../types";
type Props = {
question: IQuestionData;
answers: any;
};
export const RRating = ({ question }: Props) => {
const result = question.answers.reduce((acc: any, cur: any) => {
export const RRating = ({ question, answers }: Props) => {
const result = answers.reduce((acc: any, cur: any) => {
acc[cur] = (acc[cur] || 0) + 1;
return acc;
}, {});
......
......@@ -109,22 +109,25 @@ export const getAnswerElementByType = (
}
};
export const getResultElementByType = (question: IQuestionData) => {
export const getResultElementByType = (
question: IQuestionData,
answers: any
) => {
switch (question.type) {
case "singletext":
return <REssay question={question} />;
return <REssay question={question} answers={answers} />;
case "radio":
return <RRadio question={question} />;
return <RRadio question={question} answers={answers} />;
case "checkbox":
return <RCheckbox question={question} />;
return <RCheckbox question={question} answers={answers} />;
case "dropdown":
return <RDropdown question={question} />;
return <RDropdown question={question} answers={answers} />;
case "file":
return <RFile question={question} />;
return <RFile question={question} answers={answers} />;
case "rating":
return <RRating question={question} />;
return <RRating question={question} answers={answers} />;
case "date":
return <RDate question={question} />;
return <RDate question={question} answers={answers} />;
default:
return <></>;
}
......
......@@ -4,9 +4,10 @@ import { getResultElementByType } from "../helpers/question.helper";
type AccordionProps = {
question: IQuestionData;
answers: any;
};
export const Accordion = ({ question }: AccordionProps) => {
export const Accordion = ({ question, answers }: AccordionProps) => {
const [isOpened, setOpened] = useState<boolean>(false);
const [height, setHeight] = useState<string>("0px");
const contentElement = useRef<HTMLDivElement>(null);
......@@ -32,7 +33,7 @@ export const Accordion = ({ question }: AccordionProps) => {
style={{ height: height }}
className="bg-gray-100 overflow-hidden transition-all duration-300"
>
{question.answers && getResultElementByType(question)}
{answers && getResultElementByType(question, answers)}
</div>
</div>
</div>
......
......@@ -20,7 +20,9 @@ export const AnswerSurvey = () => {
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
console.log("answers:", answers);
const needAnswer = answers.some((answer) => !answer.requiredCheck);
const needAnswer = answers.some(
(answer) => answer.question.isRequired && !answer.requiredCheck
);
if (needAnswer) {
alert("필수질문에 응답하셔야 합니다.");
return;
......@@ -46,12 +48,14 @@ export const AnswerSurvey = () => {
formData.append("guestId", "guest");
const files: FileList = answer.content;
files &&
[...files].map((f) => {
console.log("파일 없음", f);
formData.append("uploadFiles", f);
});
return formData;
});
console.log("forms", forms);
setError("");
const results = await answerApi.save(
otherAnswers.map((answer) => ({
......
......@@ -25,9 +25,9 @@ export const ResultSurvey = () => {
async function getAnswers() {
try {
if (surveyId) {
const survey = await answerApi.getAnswers(surveyId);
// console.log(survey);
setSurvey(survey);
const result = await answerApi.getAnswers(surveyId);
console.log(result);
setSurvey(result);
} else {
setLoading(true);
}
......@@ -51,7 +51,11 @@ export const ResultSurvey = () => {
<div className="container w-11/12 place-self-center">
{survey.questions.map((question) => (
<Accordion key={question._id} question={question} />
<Accordion
key={question._id}
question={question.questionInfo}
answers={question.answers}
/>
))}
</div>
</div>
......
......@@ -22,8 +22,8 @@ export const SurveyCard = ({ survey, handleDelete }: Props) => {
return (
<div className="w-40 h-48 md:w-52 md:h-60 rounded border-2 hover:border-2 hover:border-themeColor">
<Link to={`${survey._id}`} state={survey} className="w-full pt-1">
<p className="font-bold">
<Link to={`${survey._id}`} state={survey} className="w-full">
<p className="font-bold text-center mt-1.5">
{survey.title ? survey.title : "제목없는 설문조사"}
</p>
......@@ -32,7 +32,7 @@ export const SurveyCard = ({ survey, handleDelete }: Props) => {
{survey.comment ? survey.comment : "설명없는 설문조사"}
</p>
</div>
<p className="text-gray-500 text-sm">
<p className="text-gray-500 text-sm text-center">
{survey.updatedAt?.substring(0, 10)}
</p>
</Link>
......
......@@ -90,6 +90,7 @@ export const createAnswerWithFile = asyncWrap(async (reqExp, res) => {
let fileInfos;
const files = formidableFilesToArray(req.files.uploadFiles);
console.log("files=", files);
if (files) {
fileInfos = await Promise.all(
files.map(async (file) => await fileDb.createFile(file))
......@@ -103,30 +104,41 @@ export const createAnswerWithFile = asyncWrap(async (reqExp, res) => {
res.json(newAnswer);
});
// export const getAnswers = asyncWrap(async (reqExp, res) => {
// const req = reqExp as TypedRequest;
// const { surveyId } = req.params;
// try {
// const survey = await surveyDb.getSurveyById(surveyId);
// const answers = await answerDb.getAnswers(surveyId);
// console.log(answers);
// const jsonSurvey = survey?.toJSON();
// if (jsonSurvey && answers) {
// const a = answers.map(async (a) => {
// const targetObj = jsonSurvey.questions.find(
// (q: any) => String(q._id) === String(a._id)
// ) as any;
// if (targetObj) {
// if (a.file.length) {
// targetObj.answers = a.file;
// } else {
// targetObj.answers = a.answers;
// }
// }
// });
// await Promise.all(a);
// }
// return res.json(jsonSurvey);
// } catch (error: any) {
// res.status(422).send(error.message || "설문조사 결과 불러오기 오류");
// }
// });
export const getAnswers = asyncWrap(async (reqExp, res) => {
const req = reqExp as TypedRequest;
const { surveyId } = req.params;
try {
const survey = await surveyDb.getSurveyById(surveyId);
const answers = await answerDb.getAnswers(surveyId);
console.log(answers);
const jsonSurvey = survey?.toJSON();
if (jsonSurvey && answers) {
const a = answers.map(async (a) => {
const targetObj = jsonSurvey.questions.find(
(q: any) => String(q._id) === String(a._id)
) as any;
if (targetObj) {
if (a.file.length) {
targetObj.answers = a.file;
} else {
targetObj.answers = a.answers;
}
}
});
await Promise.all(a);
}
return res.json(jsonSurvey);
const result = await answerDb.getAnswers(surveyId);
console.log("result===", result);
return res.json(result);
} catch (error: any) {
res.status(422).send(error.message || "설문조사 결과 불러오기 오류");
}
......
......@@ -7,22 +7,62 @@ export const createAnswer = async (answer: IAnswer) => {
};
export const getAnswers = async (surveyId: string) => {
const answers = await Answer.aggregate([
const result = await Answer.aggregate([
// surveyId에 해당하는 답변들 find
{ $match: { surveyId: new Types.ObjectId(surveyId) } },
// 같은 question에 대한 답변들을 answers[]에 push
// {surveyId,questionId,guestId,content} => {_id:questionId, surveyId, answers:[content,content]}
{
$group: {
_id: "$questionId",
surveyId: { $first: "$surveyId" },
answers: { $push: "$content" },
},
},
// question DB popluate
{
$lookup: {
from: "questions",
localField: "_id",
foreignField: "_id",
as: "questionInfo",
},
},
{
$unwind: "$questionInfo",
},
// 질문 순서대로 정렬
{ $sort: { "questionInfo.order": 1 } },
// surveyId로 묶고 questions 내에 { questionInfo, answers }[]
{
$group: {
_id: "$surveyId",
questions: {
$push: { questionInfo: "$questionInfo", answers: "$answers" },
},
},
},
// survey DB populate
{
$lookup: {
from: "fileinfos",
localField: "answers",
from: "surveys",
localField: "_id",
foreignField: "_id",
as: "file",
as: "survey",
},
},
{
$unwind: "$survey",
},
//밖에 있던 questions를 survey 내부로 이동시키고 survey를 가장 root로 변경
{ $set: { "survey.questions": "$questions" } },
{ $replaceRoot: { newRoot: "$survey" } },
]);
return answers;
return result[0];
};
......@@ -4,8 +4,8 @@ export { asyncWrap } from "./asyncWrap";
export const isEmpty = (obj: any) => {
return (
obj && // 👈 null and undefined check
Object.keys(obj).length === 0 &&
!obj || // 👈 null and undefined check
Object.keys(obj).length === 0 ||
Object.getPrototypeOf(obj) === Object.prototype
);
};
......
......@@ -3,6 +3,7 @@ import { model, ObjectId, Schema, Types } from "mongoose";
export interface IQuestion {
_id?: Types.ObjectId;
user?: Types.ObjectId;
order: number;
type: string;
title?: string;
isRequired: boolean;
......@@ -13,7 +14,8 @@ export interface IQuestion {
const schema = new Schema<IQuestion>(
{
user: { type: Schema.Types.ObjectId, ref: "User" },
type: { type: String },
order: { type: Number },
type: { type: String, required: true },
title: { type: String },
isRequired: { type: Boolean },
comment: { type: String },
......
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