Skip to content

Commit

Permalink
Refactor/Survey displaying
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryczko authored Jan 17, 2024
1 parent b892de3 commit d0b8659
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 204 deletions.
51 changes: 6 additions & 45 deletions src/features/surveys/features/SurveyDisplay/SurveyDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,19 @@
import React from 'react';
import { SurveyWithQuestions } from 'types/SurveyWithQuestions';
import { useSurveyAnswerManager } from 'features/surveys/features/SurveyDisplay/managers/surveyAnswerManager';
import useTranslation from 'next-translate/useTranslation';
import AllQuestionsView from 'features/surveys/features/SurveyDisplay/components/AllQuestionsView/AllQuestionsView';
import OneQuestionView from 'features/surveys/features/SurveyDisplay/components/OneQuestionView/OneQuestionView';
import { SurveyDisplayContext } from 'features/surveys/features/SurveyDisplay/context';
import SurveyDisplayContent from 'features/surveys/features/SurveyDisplay/SurveyDisplayContent';

interface SurveyDisplayProps {
initialData: SurveyWithQuestions;
}

export default function SurveyDisplay({ initialData }: SurveyDisplayProps) {
const { t } = useTranslation('survey');

const {
handleAnswerChange,
handleSave,
isAnswering,
formData,
isSubmitted,
activeQuestionIndex,
handleNextQuestion,
handlePreviousQuestion,
} = useSurveyAnswerManager(initialData);
const manager = useSurveyAnswerManager(initialData);

return (
<>
<div className="w-full">
{formData?.isActive ? (
formData.oneQuestionPerStep ? (
<OneQuestionView
activeQuestionIndex={activeQuestionIndex}
formData={formData}
handleNextQuestion={handleNextQuestion}
handlePreviousQuestion={handlePreviousQuestion}
isSubmitted={isSubmitted}
handleAnswerChange={handleAnswerChange}
isAnswering={isAnswering}
/>
) : (
<AllQuestionsView
formData={formData}
handleSave={handleSave}
handleAnswerChange={handleAnswerChange}
isAnswering={isAnswering}
isSubmitted={isSubmitted}
/>
)
) : (
<>
<div className="text-5xl">🙁</div>
<div className="my-5 text-xl">{t('surveyNoLongerActive')}</div>
</>
)}
</div>
</>
<SurveyDisplayContext.Provider value={manager}>
<SurveyDisplayContent />
</SurveyDisplayContext.Provider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import { useSurveyDisplayContext } from 'features/surveys/features/SurveyDisplay/context';
import OneQuestionView from 'features/surveys/features/SurveyDisplay/components/OneQuestionView/OneQuestionView';
import AllQuestionsView from 'features/surveys/features/SurveyDisplay/components/AllQuestionsView/AllQuestionsView';
import SurveyNoActive from 'features/surveys/features/SurveyDisplay/components/SurveyNoActive/SurveyNoActive';

export default function SurveyDisplayContent() {
const { formData } = useSurveyDisplayContext();

return (
<div className="w-full">
{formData?.isActive ? (
formData.oneQuestionPerStep ? (
<OneQuestionView />
) : (
<AllQuestionsView />
)
) : (
<SurveyNoActive />
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { SurveyWithQuestionsAndUsersAnswers } from 'features/surveys/features/SurveyDisplay/managers/surveyAnswerManager';
import React from 'react';
import Header from 'shared/components/Header/Header';
import Button, {
Expand All @@ -7,41 +6,20 @@ import Button, {
} from 'shared/components/Button/Button';
import useTranslation from 'next-translate/useTranslation';
import { AnswersComponentFactory } from 'features/surveys/features/SurveyDisplay/components/AnswersComponent/AnswersComponentFactory';
import { useSurveyDisplayContext } from 'features/surveys/features/SurveyDisplay/context';

interface AllQuestionsViewProps {
formData: SurveyWithQuestionsAndUsersAnswers;
handleSave: () => Promise<void>;
isAnswering: boolean;
isSubmitted: boolean;
handleAnswerChange: (questionId: string, answer: string) => void;
}

export default function AllQuestionView({
formData,
handleSave,
handleAnswerChange,
isAnswering,
isSubmitted,
}: AllQuestionsViewProps) {
export default function AllQuestionView() {
const { t } = useTranslation('survey');

const { handleSave, isAnswering, formData } = useSurveyDisplayContext();

return (
<>
{formData?.displayTitle && <Header>{formData?.title}</Header>}

{formData?.questions.map((question) => {
{formData?.questions.map((question, index) => {
return (
<AnswersComponentFactory
key={question.id}
question={question.title}
type={question.type}
options={question.options}
answer={question.answer}
questionId={question.id}
handleAnswerChange={handleAnswerChange}
isSubmitted={isSubmitted}
isRequired={question.isRequired}
/>
<AnswersComponentFactory key={question.id} questionIndex={index} />
);
})}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,52 +9,58 @@ import ChoiceComponent from 'features/surveys/features/SurveyDisplay/components/
import ListAnswersComponent from 'features/surveys/features/SurveyDisplay/components/AnswersComponent/ListAnswersComponent';
import RateAnswersComponent from 'features/surveys/features/SurveyDisplay/components/AnswersComponent/RateComponent/RateComponent';
import TextAnswersComponent from 'features/surveys/features/SurveyDisplay/components/AnswersComponent/TextAnswersComponent';
import { useSurveyDisplayContext } from 'features/surveys/features/SurveyDisplay/context';

interface AnswersComponentFactoryProps {
type: QuestionType;
options: string[];
handleAnswerChange: (answer: string, questionId: string) => void;
answer?: string;
questionId: string;
question: string;
isSubmitted: boolean;
isRequired: boolean;
goToNextQuestion?: () => void;
handlePreviousQuestion?: () => void;
activeQuestionIndex?: number;
isLastQuestion?: boolean;
isAnswering?: boolean;
questionIndex: number;
}
export const AnswersComponentFactory = (
props: AnswersComponentFactoryProps
) => {
const { t } = useTranslation('survey');

const { type } = props;
const {
isAnswering,
formData,
activeQuestionIndex,
handleNextQuestion,
handlePreviousQuestion,
} = useSurveyDisplayContext();

const isBackButtonVisible =
!!props.handlePreviousQuestion &&
!!props.activeQuestionIndex &&
props.activeQuestionIndex > 0;
!!handlePreviousQuestion &&
!!activeQuestionIndex &&
activeQuestionIndex > 0;

const isNextButtonVisible = !!props.goToNextQuestion;
const isNextButtonVisible = !!handleNextQuestion;

const currentQuestion = formData?.questions[props.questionIndex];
const isLastQuestion = activeQuestionIndex === formData?.questions.length - 1;

return (
<div className="mb-3 rounded-md border bg-white/50 px-6 py-4 shadow">
<h2 className="mb-4 text-lg font-semibold">{props.question}</h2>
{type === QuestionType.EMOJI && <ListAnswersComponent {...props} />}
{type === QuestionType.INPUT && <TextAnswersComponent {...props} />}
{type === QuestionType.CHOICE && <ChoiceComponent {...props} />}
{type === QuestionType.RATE && <RateAnswersComponent {...props} />}
<h2 className="mb-4 text-lg font-semibold">{currentQuestion.title}</h2>
{currentQuestion.type === QuestionType.EMOJI && (
<ListAnswersComponent questionData={currentQuestion} />
)}
{currentQuestion.type === QuestionType.INPUT && (
<TextAnswersComponent questionData={currentQuestion} />
)}
{currentQuestion.type === QuestionType.CHOICE && (
<ChoiceComponent questionData={currentQuestion} />
)}
{currentQuestion.type === QuestionType.RATE && (
<RateAnswersComponent questionData={currentQuestion} />
)}

{(isNextButtonVisible || isBackButtonVisible) && (
{formData.oneQuestionPerStep && (
<div className="mt-6 flex flex-col gap-x-4 gap-y-2 sm:flex-row">
{isBackButtonVisible && (
<Button
variant={ButtonVariant.OUTLINE}
sizeType={ButtonSize.FULL}
onClick={props.handlePreviousQuestion}
disabled={props.isAnswering}
onClick={handlePreviousQuestion}
disabled={isAnswering}
>
{t('back')}
</Button>
Expand All @@ -64,10 +70,10 @@ export const AnswersComponentFactory = (
<Button
variant={ButtonVariant.PRIMARY}
sizeType={ButtonSize.FULL}
onClick={props.goToNextQuestion}
isLoading={props.isAnswering}
onClick={handleNextQuestion}
isLoading={isAnswering}
>
{props.isLastQuestion ? t('finish') : t('next')}
{isLastQuestion ? t('finish') : t('next')}
</Button>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,35 @@
import clsx from 'clsx';
import React from 'react';
import useTranslation from 'next-translate/useTranslation';
import { DraftQuestionWithAnswer } from 'features/surveys/features/SurveyDisplay/managers/surveyAnswerManager';
import { useSurveyDisplayContext } from 'features/surveys/features/SurveyDisplay/context';

interface ChoiceComponentProps {
options: string[];
handleAnswerChange: (answer: string, questionId: string) => void;
answer?: string;
questionId: string;
isSubmitted: boolean;
isRequired: boolean;
questionData: DraftQuestionWithAnswer;
}

export default function ChoiceComponent({
handleAnswerChange,
isRequired,
isSubmitted,
options,
questionId,
answer,
questionData,
}: ChoiceComponentProps) {
const { t } = useTranslation('survey');

const { handleAnswerChange, isSubmitted } = useSurveyDisplayContext();

return (
<div>
{options.map((option, idx) => (
{questionData.options.map((option, idx) => (
<button
key={idx}
className={clsx(
'mb-2 w-full rounded border p-4 text-center text-sm font-medium hover:bg-gray-100',
answer === option && 'bg-gray-200'
questionData.answer === option && 'bg-gray-200'
)}
onClick={() => handleAnswerChange(option, questionId)}
onClick={() => handleAnswerChange(option, questionData.id)}
>
{option}
</button>
))}
{isSubmitted && !answer && isRequired && (
{isSubmitted && !questionData.answer && questionData.isRequired && (
<p className="mt-2 text-sm text-red-500">{t('requiredField')}</p>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,38 @@
import React from 'react';
import useTranslation from 'next-translate/useTranslation';
import EmojiListItem from 'features/surveys/features/SurveyDisplay/components/AnswersComponent/EmojiListItem/EmojiListItem';
import { DraftQuestionWithAnswer } from 'features/surveys/features/SurveyDisplay/managers/surveyAnswerManager';
import { useSurveyDisplayContext } from 'features/surveys/features/SurveyDisplay/context';

interface ListAnswersComponentProps {
options: string[];
handleAnswerChange: (answer: string, questionId: string) => void;
answer?: string;
questionId: string;
isSubmitted: boolean;
isRequired: boolean;
questionData: DraftQuestionWithAnswer;
}

export default function ListAnswersComponent({
options,
handleAnswerChange,
answer,
questionId,
isSubmitted,
isRequired,
questionData,
}: ListAnswersComponentProps) {
const { t } = useTranslation('survey');

const { handleAnswerChange, isSubmitted } = useSurveyDisplayContext();

const onAnswerChange = (answer: string) => {
handleAnswerChange(answer, questionId);
handleAnswerChange(answer, questionData.id);
};

return (
<>
<div className="mb-2 flex flex-wrap justify-center gap-y-2">
{options.map((icon, idx) => (
{questionData.options.map((icon, idx) => (
<EmojiListItem
icon={icon}
selected={answer === icon}
isAnySelected={!!answer}
selected={questionData.answer === icon}
isAnySelected={!!questionData.answer}
key={icon}
onClick={onAnswerChange}
/>
))}
</div>
{isSubmitted && !answer && isRequired && (
{isSubmitted && !questionData.answer && questionData.isRequired && (
<p className="mt-4 text-sm text-red-500">{t('requiredField')}</p>
)}
</>
Expand Down
Loading

0 comments on commit d0b8659

Please sign in to comment.