This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
website/src/features/QualificationPage/features/TestPage/components/Test/Question.tsx

176 lines
4.7 KiB
TypeScript

import React, { Fragment } from 'react';
import { useKeyPressEvent } from 'react-use';
import clsx from 'clsx';
import buildURL from 'utils/buildURL';
import { Answer, Question as QuestionT } from 'libs/graphql';
import Image from 'next/image';
import { makeStyles } from '@material-ui/core/styles';
import { Typography, Button } from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import WordWrap from './WordWrap';
import ImageWrapper from './ImageWrapper';
export interface QuestionProps {
question: QuestionT;
answer: Answer;
onSelectAnswer: (answer: Answer) => void;
reviewMode: boolean;
current: boolean;
}
const ANSWERS = Object.values(Answer);
const QuestionKeyPressEvents = ({
onSelectAnswer,
}: Pick<QuestionProps, 'onSelectAnswer'>) => {
useKeyPressEvent(
e => {
return (ANSWERS as string[]).includes(e.key.toLowerCase());
},
e => {
onSelectAnswer(e.key.toLowerCase() as Answer);
}
);
return null;
};
const Question = ({
question,
answer,
onSelectAnswer,
reviewMode,
current,
}: QuestionProps) => {
const classes = useStyles();
const updatedAt = new Date(question.updatedAt).getTime();
return (
<div className={classes.question}>
{question.from && (
<Typography variant="caption">{question.from}</Typography>
)}
<Typography variant="h4" component="h3">
{question.content.split('\n').map((fragment, index) => {
return (
<Fragment key={index}>
<WordWrap>{fragment}</WordWrap>
<br />
</Fragment>
);
})}
</Typography>
{question.image && (
<ImageWrapper height="300px">
<Image
layout="fill"
src={buildURL('cdn', question.image) + `?${updatedAt}`}
alt={question.content}
objectFit="contain"
/>
</ImageWrapper>
)}
{reviewMode && (
<div className={classes.alertContainer}>
{!answer && (
<Alert severity="error">
Nie wybrałeś w tym pytaniu żadnej odpowiedzi.
</Alert>
)}
{question.explanation && (
<Alert severity="info">{question.explanation}</Alert>
)}
</div>
)}
{ANSWERS.map(a => {
const upper = a.toUpperCase();
const image = question[
`answer${upper}Image` as keyof QuestionT
] as string;
const answerContent = question[
`answer${upper}` as keyof QuestionT
] as string;
return (
<Button
key={a}
className={clsx(classes.button, {
[classes.buttonWithImage]: !!image,
correct: reviewMode && a === question.correctAnswer,
wrong: reviewMode && a !== question.correctAnswer && a === answer,
})}
variant={
a === answer || (reviewMode && a === question.correctAnswer)
? 'contained'
: 'outlined'
}
fullWidth
onClick={() => onSelectAnswer(a)}
color={'primary'}
>
<span>
{`${upper}. `}
{answerContent.split('\n').map(fragment => (
<Fragment key={fragment}>
{fragment}
<br />
</Fragment>
))}
</span>
{image && (
<ImageWrapper height="300px">
<Image
layout="fill"
src={buildURL('cdn', image) + `?${updatedAt}`}
alt={answerContent ?? `Odpowiedź ${upper}.`}
objectFit="contain"
/>
</ImageWrapper>
)}
</Button>
);
})}
{current && <QuestionKeyPressEvents onSelectAnswer={onSelectAnswer} />}
</div>
);
};
const useStyles = makeStyles(theme => {
return {
question: {
'& > *:not(:last-child)': {
marginBottom: theme.spacing(2),
},
},
button: {
justifyContent: 'flex-start',
transition: 'background-color .2s',
textAlign: 'left',
textTransform: 'none',
'& img ': {
display: 'block',
},
'&.correct': {
backgroundColor: theme.palette.success.main,
color: theme.palette.success.contrastText,
},
'&.wrong': {
backgroundColor: theme.palette.error.main,
color: theme.palette.error.contrastText,
},
},
buttonWithImage: {
'& .MuiButton-label': {
flexDirection: 'column',
alignItems: 'flex-start',
},
},
alertContainer: {
'& > *:not(:last-child)': {
marginBottom: theme.spacing(1),
},
},
};
});
export default Question;