Skip to content

Commit 7ada0cb

Browse files
authored
Feature/Editing survey
1 parent f495a01 commit 7ada0cb

19 files changed

Lines changed: 369 additions & 90 deletions

File tree

lib/axiosConfig.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,23 @@ export const postFetch = (url: string, data = {}) => {
1919
data,
2020
}).then((response) => response.data);
2121
};
22+
2223
export const patchFetch = (url: string, data = {}) => {
2324
return instance({
2425
method: 'PATCH',
2526
url,
2627
data,
2728
}).then((response) => response.data);
2829
};
30+
31+
export const putFetch = (url: string, data = {}) => {
32+
return instance({
33+
method: 'PUT',
34+
url,
35+
data,
36+
}).then((response) => response.data);
37+
};
38+
2939
export const deleteFetch = (url: string) => {
3040
return instance({
3141
method: 'DELETE',

locales/en/surveyCreate.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
"title": "Create Survey",
33
"content": "Create Survey - FormsLab",
44
"heading": "Create new survey",
5+
"editHeading": "Edit survey",
56
"buttonCreate": "Create Survey",
7+
"editNote": "Some action like adding and removing questions or changing answers are not available in edit mode.",
8+
"editNoteTitle": "Note",
9+
"buttonSave": "Save changes",
10+
"discardChanges": "Discard changes",
611
"surveyTitleLable": "Survey Title",
712
"surveyTitlePlaceholder": "Survey Title...",
813
"questionPlaceholder": "Question...",

prisma/schema.prisma

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ model Survey {
7171
description String?
7272
questions Question[]
7373
answers Answer[]
74-
oneQuestionPerStep Boolean?
75-
displayTitle Boolean?
74+
oneQuestionPerStep Boolean
75+
displayTitle Boolean
7676
7777
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
7878
}
@@ -86,6 +86,7 @@ model Question {
8686
type QuestionType
8787
isRequired Boolean
8888
options String[]
89+
order Int
8990
9091
survey Survey @relation(fields: [surveyId], references: [id], onDelete: Cascade)
9192
}

public/images/creator.webp

-10.6 KB
Loading

src/features/surveys/components/AddQuestionButton/AddQuestionButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface AddQuestionButtonProps {
1414
export const AddQuestionButton = ({ onClick }: AddQuestionButtonProps) => {
1515
const { closeModal, isModalOpen, openModal } = useModal();
1616
return (
17-
<div className="mt-4">
17+
<div className="mb-2">
1818
<Button
1919
onClick={openModal}
2020
variant={ButtonVariant.OUTLINE}

src/features/surveys/components/QuestionBlocks/QuestionBlockFactory.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ interface QuestionBlockFactoryProps {
3333
dragHandleProps: DraggableProvidedDragHandleProps | null | undefined;
3434
expanded: boolean;
3535
expandQuestion: (questionIndex: number) => void;
36+
isEditMode: boolean;
3637
}
3738

3839
export default function QuestionBlockFactory({
@@ -53,6 +54,7 @@ export default function QuestionBlockFactory({
5354
dragHandleProps,
5455
expanded,
5556
expandQuestion,
57+
isEditMode,
5658
}: QuestionBlockFactoryProps) {
5759
return (
5860
<QuestionBlockWrapper
@@ -69,6 +71,7 @@ export default function QuestionBlockFactory({
6971
expanded={expanded}
7072
expandQuestion={expandQuestion}
7173
type={type}
74+
isEditMode={isEditMode}
7275
>
7376
{type === QuestionType.RATE && <RateQuestionBlock />}
7477
{type === QuestionType.INPUT && <InputQuestionBlock />}

src/features/surveys/components/QuestionBlocks/QuestionBlockWrapper/QuestionBlockWrapper.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface QuestionBlockWrapperProps {
2727
expanded: boolean;
2828
expandQuestion: (questionIndex: number) => void;
2929
type: QuestionType;
30+
isEditMode: boolean;
3031
}
3132

3233
export default function QuestionBlockWrapper({
@@ -44,6 +45,7 @@ export default function QuestionBlockWrapper({
4445
expanded,
4546
expandQuestion,
4647
type,
48+
isEditMode,
4749
}: PropsWithChildren<QuestionBlockWrapperProps>) {
4850
const { t } = useTranslation('surveyCreate');
4951

@@ -122,8 +124,9 @@ export default function QuestionBlockWrapper({
122124
{isRemovingPossible && (
123125
<button
124126
onClick={removeQuestion}
127+
disabled={isEditMode}
125128
data-test-id={`remove-question-${index}`}
126-
className="cursor-pointer rounded-md border bg-white p-[13px] shadow-sm hover:scale-95"
129+
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"
127130
>
128131
<TrashIcon className="w-[15px] text-red-700" />
129132
</button>
@@ -135,7 +138,15 @@ export default function QuestionBlockWrapper({
135138

136139
{expanded && (
137140
<div className="mb-4 px-3">
138-
{children}
141+
{isEditMode ? (
142+
<div className="relative opacity-50">
143+
<div className="absolute z-50 h-full w-full cursor-not-allowed"></div>
144+
145+
{children}
146+
</div>
147+
) : (
148+
children
149+
)}
139150

140151
<div className="mt-2 flex justify-end border-t">
141152
<Toggle

src/features/surveys/components/ShareSurveryModal/ShareSurveyModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default function ShareSurveyModal({
3333
content={
3434
<>
3535
<div className="mt-4">
36-
<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">
36+
<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">
3737
{link}
3838
</span>
3939
</div>

src/features/surveys/components/SurveyRow/SurveyRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default function SurveyRow({
4646
return (
4747
<div className="mb-4 flex w-full flex-col md:flex-row">
4848
<div className="flex w-full items-center justify-between rounded-md bg-white px-4 py-3 shadow-sm">
49-
<div title={question} className="w-40 truncate text-left">
49+
<div title={question} className="w-40 truncate text-left md:w-60">
5050
{question}
5151
</div>
5252
<div className="hidden items-center space-x-2 text-sm xsm:flex">

src/features/surveys/managers/createSurveyManager.ts

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import toast from 'react-hot-toast';
44
import useCopyToClipboard from 'shared/hooks/useCopyToClipboard';
55
import useTranslation from 'next-translate/useTranslation';
66
import { QuestionType } from '@prisma/client';
7-
import { postFetch } from '../../../../lib/axiosConfig';
7+
import { postFetch, putFetch } from '../../../../lib/axiosConfig';
88
import { defaultQuestions } from 'shared/constants/surveysConfig';
99
import { DRAFT_SURVEY_SESSION_STORAGE } from 'shared/constants/app';
10+
import { SurveyWithQuestions } from 'types/SurveyWithQuestions';
11+
import { Question as QuestionDto } from '@prisma/client';
1012

1113
export interface Question {
1214
id: string;
@@ -22,19 +24,32 @@ export interface SurveyOptions {
2224
displayTitle: boolean;
2325
}
2426

25-
export const useCreateSurveyManager = () => {
26-
const [title, setTitle] = useState('');
27-
const [questions, setQuestions] = useState<Question[]>(defaultQuestions);
27+
export const useCreateSurveyManager = (initialData?: SurveyWithQuestions) => {
28+
const [isEditMode] = useState(!!initialData);
29+
30+
const [title, setTitle] = useState(initialData?.title ?? '');
31+
32+
const mapQuestionsWithExpanded = (questions?: QuestionDto[]) => {
33+
return questions?.map((question) => ({
34+
...question,
35+
expanded: false,
36+
}));
37+
};
38+
39+
const [questions, setQuestions] = useState<Question[]>(
40+
mapQuestionsWithExpanded(initialData?.questions) ?? defaultQuestions
41+
);
42+
const [surveyOptions, setSurveyOptions] = useState<SurveyOptions>({
43+
oneQuestionPerStep: initialData?.oneQuestionPerStep ?? true,
44+
displayTitle: initialData?.displayTitle ?? true,
45+
});
46+
2847
const [error, setError] = useState('');
2948
const [isCreating, setIsCreating] = useState(false);
3049
const [isSubmitted, setIsSubmitted] = useState(false);
3150
const router = useRouter();
3251
const { copy } = useCopyToClipboard();
3352
const { t } = useTranslation('surveyCreate');
34-
const [surveyOptions, setSurveyOptions] = useState<SurveyOptions>({
35-
oneQuestionPerStep: true,
36-
displayTitle: true,
37-
});
3853

3954
const signInToCreateSurvey = () => {
4055
router.push('/login');
@@ -249,6 +264,40 @@ export const useCreateSurveyManager = () => {
249264
setIsCreating(false);
250265
};
251266

267+
const confirmEditSurvey = async () => {
268+
if (!isSurveyValid() || !initialData) return;
269+
270+
setIsCreating(true);
271+
272+
try {
273+
const newSurvey = await putFetch(`/api/survey/${initialData.id}`, {
274+
title,
275+
oneQuestionPerStep: surveyOptions.oneQuestionPerStep,
276+
displayTitle: surveyOptions.displayTitle,
277+
questions: questions.map((question) => ({
278+
id: question.id,
279+
title: question.title,
280+
options: question.options,
281+
type: question.type,
282+
isRequired: question.isRequired,
283+
})),
284+
});
285+
286+
await router.push(`/survey/answer/${newSurvey.id}`, undefined, {
287+
scroll: false,
288+
});
289+
} catch (error) {
290+
toast.error(t('surveyCreationFailed'));
291+
}
292+
setIsCreating(false);
293+
};
294+
295+
const discardChanges = () => {
296+
router.push(`/survey/answer/${initialData?.id}`, undefined, {
297+
scroll: false,
298+
});
299+
};
300+
252301
const reorderQuestion = (startIndex: number, endIndex: number) => {
253302
const newOrderedQuestions = Array.from(questions);
254303

@@ -288,5 +337,8 @@ export const useCreateSurveyManager = () => {
288337
surveyOptions,
289338
updateSurveyOptions,
290339
signInToCreateSurvey,
340+
isEditMode,
341+
confirmEditSurvey,
342+
discardChanges,
291343
};
292344
};

0 commit comments

Comments
 (0)