Skip to content

Commit

Permalink
Feature/Editing survey
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryczko authored Jan 5, 2024
1 parent f495a01 commit 7ada0cb
Show file tree
Hide file tree
Showing 19 changed files with 369 additions and 90 deletions.
10 changes: 10 additions & 0 deletions lib/axiosConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,23 @@ export const postFetch = (url: string, data = {}) => {
data,
}).then((response) => response.data);
};

export const patchFetch = (url: string, data = {}) => {
return instance({
method: 'PATCH',
url,
data,
}).then((response) => response.data);
};

export const putFetch = (url: string, data = {}) => {
return instance({
method: 'PUT',
url,
data,
}).then((response) => response.data);
};

export const deleteFetch = (url: string) => {
return instance({
method: 'DELETE',
Expand Down
5 changes: 5 additions & 0 deletions locales/en/surveyCreate.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
"title": "Create Survey",
"content": "Create Survey - FormsLab",
"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.",
"editNoteTitle": "Note",
"buttonSave": "Save changes",
"discardChanges": "Discard changes",
"surveyTitleLable": "Survey Title",
"surveyTitlePlaceholder": "Survey Title...",
"questionPlaceholder": "Question...",
Expand Down
5 changes: 3 additions & 2 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ model Survey {
description String?
questions Question[]
answers Answer[]
oneQuestionPerStep Boolean?
displayTitle Boolean?
oneQuestionPerStep Boolean
displayTitle Boolean
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
Expand All @@ -86,6 +86,7 @@ model Question {
type QuestionType
isRequired Boolean
options String[]
order Int
survey Survey @relation(fields: [surveyId], references: [id], onDelete: Cascade)
}
Expand Down
Binary file modified public/images/creator.webp
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface AddQuestionButtonProps {
export const AddQuestionButton = ({ onClick }: AddQuestionButtonProps) => {
const { closeModal, isModalOpen, openModal } = useModal();
return (
<div className="mt-4">
<div className="mb-2">
<Button
onClick={openModal}
variant={ButtonVariant.OUTLINE}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ interface QuestionBlockFactoryProps {
dragHandleProps: DraggableProvidedDragHandleProps | null | undefined;
expanded: boolean;
expandQuestion: (questionIndex: number) => void;
isEditMode: boolean;
}

export default function QuestionBlockFactory({
Expand All @@ -53,6 +54,7 @@ export default function QuestionBlockFactory({
dragHandleProps,
expanded,
expandQuestion,
isEditMode,
}: QuestionBlockFactoryProps) {
return (
<QuestionBlockWrapper
Expand All @@ -69,6 +71,7 @@ export default function QuestionBlockFactory({
expanded={expanded}
expandQuestion={expandQuestion}
type={type}
isEditMode={isEditMode}
>
{type === QuestionType.RATE && <RateQuestionBlock />}
{type === QuestionType.INPUT && <InputQuestionBlock />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface QuestionBlockWrapperProps {
expanded: boolean;
expandQuestion: (questionIndex: number) => void;
type: QuestionType;
isEditMode: boolean;
}

export default function QuestionBlockWrapper({
Expand All @@ -44,6 +45,7 @@ export default function QuestionBlockWrapper({
expanded,
expandQuestion,
type,
isEditMode,
}: PropsWithChildren<QuestionBlockWrapperProps>) {
const { t } = useTranslation('surveyCreate');

Expand Down Expand Up @@ -122,8 +124,9 @@ export default function QuestionBlockWrapper({
{isRemovingPossible && (
<button
onClick={removeQuestion}
disabled={isEditMode}
data-test-id={`remove-question-${index}`}
className="cursor-pointer rounded-md border bg-white p-[13px] shadow-sm hover:scale-95"
className="cursor-pointer rounded-md border bg-white p-[13px] shadow-sm hover:scale-95 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:scale-100"
>
<TrashIcon className="w-[15px] text-red-700" />
</button>
Expand All @@ -135,7 +138,15 @@ export default function QuestionBlockWrapper({

{expanded && (
<div className="mb-4 px-3">
{children}
{isEditMode ? (
<div className="relative opacity-50">
<div className="absolute z-50 h-full w-full cursor-not-allowed"></div>

{children}
</div>
) : (
children
)}

<div className="mt-2 flex justify-end border-t">
<Toggle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function ShareSurveyModal({
content={
<>
<div className="mt-4">
<span className="scrollbar-hide block w-full select-all overflow-x-auto rounded-md border border-gray-300 px-3 py-2 text-center text-sm focus:outline-none">
<span className="scrollbar-hide block w-full select-all overflow-x-auto whitespace-nowrap rounded-md border border-gray-300 px-3 py-2 text-center text-sm focus:outline-none">
{link}
</span>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/features/surveys/components/SurveyRow/SurveyRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default function SurveyRow({
return (
<div className="mb-4 flex w-full flex-col md:flex-row">
<div className="flex w-full items-center justify-between rounded-md bg-white px-4 py-3 shadow-sm">
<div title={question} className="w-40 truncate text-left">
<div title={question} className="w-40 truncate text-left md:w-60">
{question}
</div>
<div className="hidden items-center space-x-2 text-sm xsm:flex">
Expand Down
68 changes: 60 additions & 8 deletions src/features/surveys/managers/createSurveyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import toast from 'react-hot-toast';
import useCopyToClipboard from 'shared/hooks/useCopyToClipboard';
import useTranslation from 'next-translate/useTranslation';
import { QuestionType } from '@prisma/client';
import { postFetch } from '../../../../lib/axiosConfig';
import { postFetch, putFetch } from '../../../../lib/axiosConfig';
import { defaultQuestions } from 'shared/constants/surveysConfig';
import { DRAFT_SURVEY_SESSION_STORAGE } from 'shared/constants/app';
import { SurveyWithQuestions } from 'types/SurveyWithQuestions';
import { Question as QuestionDto } from '@prisma/client';

export interface Question {
id: string;
Expand All @@ -22,19 +24,32 @@ export interface SurveyOptions {
displayTitle: boolean;
}

export const useCreateSurveyManager = () => {
const [title, setTitle] = useState('');
const [questions, setQuestions] = useState<Question[]>(defaultQuestions);
export const useCreateSurveyManager = (initialData?: SurveyWithQuestions) => {
const [isEditMode] = useState(!!initialData);

const [title, setTitle] = useState(initialData?.title ?? '');

const mapQuestionsWithExpanded = (questions?: QuestionDto[]) => {
return questions?.map((question) => ({
...question,
expanded: false,
}));
};

const [questions, setQuestions] = useState<Question[]>(
mapQuestionsWithExpanded(initialData?.questions) ?? defaultQuestions
);
const [surveyOptions, setSurveyOptions] = useState<SurveyOptions>({
oneQuestionPerStep: initialData?.oneQuestionPerStep ?? true,
displayTitle: initialData?.displayTitle ?? true,
});

const [error, setError] = useState('');
const [isCreating, setIsCreating] = useState(false);
const [isSubmitted, setIsSubmitted] = useState(false);
const router = useRouter();
const { copy } = useCopyToClipboard();
const { t } = useTranslation('surveyCreate');
const [surveyOptions, setSurveyOptions] = useState<SurveyOptions>({
oneQuestionPerStep: true,
displayTitle: true,
});

const signInToCreateSurvey = () => {
router.push('/login');
Expand Down Expand Up @@ -249,6 +264,40 @@ export const useCreateSurveyManager = () => {
setIsCreating(false);
};

const confirmEditSurvey = async () => {
if (!isSurveyValid() || !initialData) return;

setIsCreating(true);

try {
const newSurvey = await putFetch(`/api/survey/${initialData.id}`, {
title,
oneQuestionPerStep: surveyOptions.oneQuestionPerStep,
displayTitle: surveyOptions.displayTitle,
questions: questions.map((question) => ({
id: question.id,
title: question.title,
options: question.options,
type: question.type,
isRequired: question.isRequired,
})),
});

await router.push(`/survey/answer/${newSurvey.id}`, undefined, {
scroll: false,
});
} catch (error) {
toast.error(t('surveyCreationFailed'));
}
setIsCreating(false);
};

const discardChanges = () => {
router.push(`/survey/answer/${initialData?.id}`, undefined, {
scroll: false,
});
};

const reorderQuestion = (startIndex: number, endIndex: number) => {
const newOrderedQuestions = Array.from(questions);

Expand Down Expand Up @@ -288,5 +337,8 @@ export const useCreateSurveyManager = () => {
surveyOptions,
updateSurveyOptions,
signInToCreateSurvey,
isEditMode,
confirmEditSurvey,
discardChanges,
};
};
2 changes: 1 addition & 1 deletion src/layout/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';
export default function Footer() {
return (
<footer className="w-full border-t py-3 text-center text-sm">
FormsLab © 2023
FormsLab © 2024
</footer>
);
}
31 changes: 20 additions & 11 deletions src/pages/api/answer/[id].ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,27 @@ interface AnswerData {
answersData: { questionId: string; answer?: string }[];
}

export async function getSurveyData(surveyId: string) {
const survey = await prismadb.survey.findUnique({
where: {
id: surveyId,
},
include: {
questions: true,
answers: false,
},
});
export async function getSurveyData(surveyId: string, userId?: string) {
try {
const survey = await prismadb.survey.findFirst({
where: {
id: surveyId,
userId,
},
include: {
questions: {
orderBy: {
order: 'asc',
},
},
answers: false,
},
});

return survey;
return survey;
} catch (error) {
return null;
}
}

const isAnswerDataValid = (answerData: AnswerData) => {
Expand Down
13 changes: 12 additions & 1 deletion src/pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,20 @@ export const authOptions: NextAuthOptions = {
},
callbacks: {
session: async ({ session, token }) => {
if (session?.user) {
if (session?.user && token.uid) {
const isSessionValid = await prismadb.user.findUnique({
where: {
id: token.uid as string,
},
});

if (!isSessionValid) {
return Promise.reject(new Error('Session not valid'));
}

session.user.id = token.uid;
}

return session;
},
jwt: async ({ user, token }) => {
Expand Down
Loading

1 comment on commit 7ada0cb

@vercel
Copy link

@vercel vercel bot commented on 7ada0cb Jan 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

formslab – ./

formslab.vercel.app
formslab-git-main-ryczko.vercel.app
formslab-ryczko.vercel.app

Please sign in to comment.