From 3f86e45085b25997fb7c7feb675ed9b42e5de930 Mon Sep 17 00:00:00 2001 From: Ryczko Date: Tue, 23 Jan 2024 17:33:26 +0100 Subject: [PATCH 1/6] Logic path WIP --- prisma/schema.prisma | 50 +++++--- .../AddQuestionButton/AddQuestionButton.tsx | 4 +- .../components/LogicalJump/Condition.tsx | 111 ++++++++++++++++++ .../components/LogicalJump/LogicalJump.tsx | 65 ++++++++++ .../NewQuestionModal/NewQuestionModal.tsx | 13 +- .../AdvancedSettings/RateAdvancedSettings.tsx | 75 ++++++++++++ .../QuestionBlocks/QuestionBlockFactory.tsx | 19 ++- .../QuestionBlockWrapper.tsx | 37 +++++- .../createSurveyManager.ts | 64 +++++++++- src/shared/components/Select/Select.tsx | 80 +++++++++++++ src/shared/components/Tabs/Tabs.tsx | 2 +- src/shared/constants/surveysConfig.ts | 5 +- 12 files changed, 486 insertions(+), 39 deletions(-) create mode 100644 src/features/surveys/features/SurveyCreator/components/LogicalJump/Condition.tsx create mode 100644 src/features/surveys/features/SurveyCreator/components/LogicalJump/LogicalJump.tsx create mode 100644 src/features/surveys/features/SurveyCreator/components/QuestionBlocks/AdvancedSettings/RateAdvancedSettings.tsx create mode 100644 src/shared/components/Select/Select.tsx diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 715d6f7..59edf64 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -63,14 +63,14 @@ model VerificationToken { } model Survey { - id String @id @default(auto()) @map("_id") @db.ObjectId - userId String @db.ObjectId - createdAt DateTime @default(now()) - title String - isActive Boolean - description String? - questions Question[] - answers Answer[] + id String @id @default(auto()) @map("_id") @db.ObjectId + userId String @db.ObjectId + createdAt DateTime @default(now()) + title String + isActive Boolean + description String? + questions Question[] + answers Answer[] oneQuestionPerStep Boolean displayTitle Boolean hideProgressBar Boolean? @@ -89,16 +89,26 @@ model Question { isRequired Boolean options String[] order Int + logicPaths LogicPath[] survey Survey @relation(fields: [surveyId], references: [id], onDelete: Cascade) } +model LogicPath { + id String @id @default(auto()) @map("_id") @db.ObjectId + questionId String @db.ObjectId + comparisonType ComparisonType + selectedOption String + nextQuestionId String + + question Question @relation(fields: [questionId], references: [id], onDelete: Cascade) +} model Answer { - id String @id @default(auto()) @map("_id") @db.ObjectId - userId String? @db.ObjectId - createdAt DateTime @default(now()) - surveyId String @db.ObjectId + id String @id @default(auto()) @map("_id") @db.ObjectId + userId String? @db.ObjectId + createdAt DateTime @default(now()) + surveyId String @db.ObjectId answerData AnswerData[] @@ -107,13 +117,13 @@ model Answer { } model AnswerData { - id String @id @default(auto()) @map("_id") @db.ObjectId - answerId String @db.ObjectId - questionId String @db.ObjectId + id String @id @default(auto()) @map("_id") @db.ObjectId + answerId String @db.ObjectId + questionId String @db.ObjectId providedAnswer String? - answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade) - } + answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade) +} enum QuestionType { EMOJI @@ -121,3 +131,9 @@ enum QuestionType { CHOICE RATE } + +enum ComparisonType { + EQUAL + GREATER_THAN + LESS_THAN +} diff --git a/src/features/surveys/features/SurveyCreator/components/AddQuestionButton/AddQuestionButton.tsx b/src/features/surveys/features/SurveyCreator/components/AddQuestionButton/AddQuestionButton.tsx index 3a4a3ad..1e03081 100644 --- a/src/features/surveys/features/SurveyCreator/components/AddQuestionButton/AddQuestionButton.tsx +++ b/src/features/surveys/features/SurveyCreator/components/AddQuestionButton/AddQuestionButton.tsx @@ -5,10 +5,10 @@ import Button, { } from 'shared/components/Button/Button'; import NewQuestionModal from 'features/surveys/features/SurveyCreator/components/NewQuestionModal/NewQuestionModal'; import useModal from 'features/surveys/hooks/useModal'; -import { Question } from 'features/surveys/features/SurveyCreator/managers/createSurveyManager/createSurveyManager'; +import { DraftQuestion } from 'features/surveys/features/SurveyCreator/managers/createSurveyManager/createSurveyManager'; interface AddQuestionButtonProps { - onClick: (newQuestion: Question) => void; + onClick: (newQuestion: DraftQuestion) => void; } export const AddQuestionButton = ({ onClick }: AddQuestionButtonProps) => { diff --git a/src/features/surveys/features/SurveyCreator/components/LogicalJump/Condition.tsx b/src/features/surveys/features/SurveyCreator/components/LogicalJump/Condition.tsx new file mode 100644 index 0000000..34998e9 --- /dev/null +++ b/src/features/surveys/features/SurveyCreator/components/LogicalJump/Condition.tsx @@ -0,0 +1,111 @@ +import { + ArrowDownIcon, + ArrowRightIcon, + TrashIcon, +} from '@heroicons/react/outline'; +import React from 'react'; +import Select from 'shared/components/Select/Select'; +import { useSurveyCreatorContext } from 'features/surveys/features/SurveyCreator/managers/createSurveyManager/context'; +import { ConditionOptions } from 'features/surveys/features/SurveyCreator/components/LogicalJump/LogicalJump'; +import { ComparisonType } from '@prisma/client'; + +interface ConditionProps { + elseCondition?: boolean; + stepIndex: number; + questionIndex: number; + conditionOptions?: ConditionOptions; +} + +export default function Condition({ + elseCondition, + questionIndex, + stepIndex, + conditionOptions, +}: ConditionProps) { + const { removeLogicPath, updateLogicPath, questions } = + useSurveyCreatorContext(); + + return ( + <> +
+
+ {elseCondition ? ( + <> + + Else continue to the next question + + ) : ( + <> + + if this answer + + updateLogicPath(questionIndex, stepIndex, { + value: option.value, + }) + } + options={ + conditionOptions?.options.map((comparison) => ({ + name: comparison.name, + value: comparison.value, + })) ?? [] + } + /> + jump to + updateLogicPath(questionIndex, stepIndex, { - value: option.value, + selectedOption: option.value, }) } options={ diff --git a/src/features/surveys/features/SurveyCreator/managers/createSurveyManager/createSurveyManager.ts b/src/features/surveys/features/SurveyCreator/managers/createSurveyManager/createSurveyManager.ts index 3ee2749..445d7c0 100644 --- a/src/features/surveys/features/SurveyCreator/managers/createSurveyManager/createSurveyManager.ts +++ b/src/features/surveys/features/SurveyCreator/managers/createSurveyManager/createSurveyManager.ts @@ -245,6 +245,7 @@ export const useCreateSurveyManager = (initialData?: SurveyWithQuestions) => { setIsCreating(true); try { + console.log(questions); const newSurvey = await postFetch('/api/survey', { title, oneQuestionPerStep: surveyOptions.oneQuestionPerStep, @@ -252,10 +253,12 @@ export const useCreateSurveyManager = (initialData?: SurveyWithQuestions) => { hideProgressBar: surveyOptions.hideProgressBar, accentColor: surveyOptions.accentColor, questions: questions.map((question) => ({ + draftId: question.id, title: question.title, options: question.options, type: question.type, isRequired: question.isRequired, + logicPaths: question.logicPath ?? [], })), }); @@ -294,6 +297,7 @@ export const useCreateSurveyManager = (initialData?: SurveyWithQuestions) => { options: question.options, type: question.type, isRequired: question.isRequired, + logicPaths: question.logicPath, })), }); @@ -371,6 +375,7 @@ export const useCreateSurveyManager = (initialData?: SurveyWithQuestions) => { logicPathIndex: number, conditions: Partial ) => { + console.log('update', conditions); setQuestions((oldQuestions) => { const newQuestions = [...oldQuestions]; const newQuestion = { ...newQuestions[questionIndex] }; diff --git a/src/pages/api/survey/index.ts b/src/pages/api/survey/index.ts index face0bc..0d88a44 100644 --- a/src/pages/api/survey/index.ts +++ b/src/pages/api/survey/index.ts @@ -3,7 +3,7 @@ import { NextApiRequest, NextApiResponse } from 'next'; import prismadb from '../../../../lib/prismadb'; import serverAuth from '../../../../lib/serverAuth'; -import { Question } from '@prisma/client'; +import { LogicPath, Question } from '@prisma/client'; import { MAX_QUESTIONS, MAX_QUESTION_LENGTH, @@ -14,7 +14,7 @@ import { export interface SurveyData { title: string; description: string; - questions: Question[]; + questions: ({ draftId: string; logicPaths: LogicPath[] } & Question)[]; oneQuestionPerStep: boolean; displayTitle: boolean; hideProgressBar: boolean; @@ -82,6 +82,8 @@ export default async function handler( accentColor, } = req.body as SurveyData; + console.log('req.body', questions); + if (!isSurveyValid(req.body)) { return res.status(400).end(); } @@ -109,6 +111,34 @@ export default async function handler( }, }); + const createdQuestions = await prismadb.question.findMany({ + where: { + surveyId: survey.id, + }, + }); + + console.log('result', createdQuestions); + + //create logic paths to questions + for (let i = 0; i < questions.length; i++) { + const question = questions[i]; + for (let j = 0; j < question.logicPaths.length; j++) { + const path = question.logicPaths[j]; + await prismadb.logicPath.create({ + data: { + comparisonType: path.comparisonType, + selectedOption: path.selectedOption, + nextQuestionId: path.nextQuestionId, + question: { + connect: { + id: createdQuestions[i].id, + }, + }, + }, + }); + } + } + return res.status(200).json({ id: survey.id }); } default: From 57042c121ba82aed1a9bf2cd1e9959553c0909a8 Mon Sep 17 00:00:00 2001 From: Ryczko Date: Thu, 9 May 2024 19:06:28 +0200 Subject: [PATCH 3/6] Finilize logic path saving --- lib/axiosConfig.ts | 17 +- locales/en/surveyCreate.json | 7 +- .../components/LogicalJump/Condition.tsx | 27 +-- .../components/LogicalJump/LogicalJump.tsx | 25 ++- .../NewQuestionModal/NewQuestionModal.tsx | 14 +- .../components/PreviewPanel/PreviewPanel.tsx | 3 +- .../AdvancedSettings/RateAdvancedSettings.tsx | 26 ++- .../QuestionBlockWrapper.tsx | 12 +- .../QuestionsSection/QuestionsSection.tsx | 6 +- .../createSurveyManager.test.ts | 6 +- .../createSurveyManager.ts | 163 ++++++++++++------ src/pages/api/answer/[id].ts | 3 + src/pages/api/survey/[id].ts | 7 +- src/pages/api/survey/index.ts | 82 +++++---- src/shared/components/Select/Select.tsx | 24 ++- src/shared/constants/surveysConfig.ts | 10 +- src/types/QuestionWithLogicPath.ts | 5 + src/types/SurveyWithQuestions.ts | 5 +- 18 files changed, 276 insertions(+), 166 deletions(-) create mode 100644 src/types/QuestionWithLogicPath.ts diff --git a/lib/axiosConfig.ts b/lib/axiosConfig.ts index 215eb58..72e9821 100644 --- a/lib/axiosConfig.ts +++ b/lib/axiosConfig.ts @@ -1,35 +1,36 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import axios from 'axios'; const instance = axios.create({ baseURL: process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000', }); -export const getFetch = (url: string, params = {}) => { - return instance({ +export const getFetch = (url: string, params = {}) => { + return instance({ method: 'GET', url, params, }).then((response) => response.data); }; -export const postFetch = (url: string, data = {}) => { - return instance({ +export const postFetch = (url: string, data: Req) => { + return instance({ method: 'POST', url, data, }).then((response) => response.data); }; -export const patchFetch = (url: string, data = {}) => { - return instance({ +export const patchFetch = (url: string, data: Req) => { + return instance({ method: 'PATCH', url, data, }).then((response) => response.data); }; -export const putFetch = (url: string, data = {}) => { - return instance({ +export const putFetch = (url: string, data: Req) => { + return instance({ method: 'PUT', url, data, diff --git a/locales/en/surveyCreate.json b/locales/en/surveyCreate.json index 63ed722..2990bd8 100644 --- a/locales/en/surveyCreate.json +++ b/locales/en/surveyCreate.json @@ -4,7 +4,7 @@ "heading": "Create new survey", "editHeading": "Edit survey", "buttonCreate": "Create Survey", - "editNote": "Some action like adding and removing questions or changing answers are not available in edit mode.", + "editNote": "Some action like adding, removing questions or changing answers are not currently available in edit mode.", "editNoteTitle": "Note", "buttonSave": "Save changes", "discardChanges": "Discard changes", @@ -19,5 +19,8 @@ "fillRequiredFields": "Please fill all required fields", "surveyCreationSucessCopiedCorrectly": "and link copied to clipboard", "signInToCreateSurvey": "Sign in to create survey", - "options": "Display options" + "options": "Display options", + "GREATER_THAN": "Greater than", + "LESS_THAN": "Less than", + "EQUAL": "Equal" } diff --git a/src/features/surveys/features/SurveyCreator/components/LogicalJump/Condition.tsx b/src/features/surveys/features/SurveyCreator/components/LogicalJump/Condition.tsx index b62eee2..083ef65 100644 --- a/src/features/surveys/features/SurveyCreator/components/LogicalJump/Condition.tsx +++ b/src/features/surveys/features/SurveyCreator/components/LogicalJump/Condition.tsx @@ -22,26 +22,28 @@ export default function Condition({ stepIndex, conditionOptions, }: ConditionProps) { - const { removeLogicPath, updateLogicPath, questions } = + const { removeLogicPath, updateLogicPath, questions, isEditMode } = useSurveyCreatorContext(); return ( <> -
+
{elseCondition ? ( - <> +
Else continue to the next question - +
) : ( <> if this answer updateLogicPath(questionIndex, stepIndex, { @@ -75,8 +79,10 @@ export default function Condition({ jump to + updateLogicPath(questionIndex, stepIndex, { + comparisonType: option.value as ComparisonType, + }) + } + options={conditionOptions?.comparisons ?? []} + /> + )} updateLogicPath(questionIndex, stepIndex, { selectedOption: option.value, }) } - options={ - conditionOptions?.options.map((comparison) => ({ - name: comparison.name, - value: comparison.value, - })) ?? [] - } + options={conditionOptions?.options ?? []} /> jump to