[TestPage] add Test component
This commit is contained in:
parent
c21c8eda7d
commit
56e1381631
2
src/config/cdn.ts
Normal file
2
src/config/cdn.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export const CDN_URI =
|
||||||
|
process.env.NEXT_PUBLIC_CDN_URI ?? 'http://localhost:9000/';
|
|
@ -19,6 +19,7 @@ import {
|
||||||
import Layout from 'common/Layout/Layout';
|
import Layout from 'common/Layout/Layout';
|
||||||
import SEO from 'common/SEO/SEO';
|
import SEO from 'common/SEO/SEO';
|
||||||
import Suggestions from './components/Suggestions/Suggestions';
|
import Suggestions from './components/Suggestions/Suggestions';
|
||||||
|
import Test from './components/Test/Test';
|
||||||
|
|
||||||
export type TestPageParams = {
|
export type TestPageParams = {
|
||||||
slug: string;
|
slug: string;
|
||||||
|
@ -40,6 +41,7 @@ const TestPage = ({ questions, suggestions, qualification }: TestPageProps) => {
|
||||||
questions.length
|
questions.length
|
||||||
} ${polishPlurals('pytanie', 'pytania', 'pytań', questions.length)}`}
|
} ${polishPlurals('pytanie', 'pytania', 'pytań', questions.length)}`}
|
||||||
/>
|
/>
|
||||||
|
<Test initialQuestions={questions} qualification={qualification} />
|
||||||
<Suggestions suggestions={suggestions} />
|
<Suggestions suggestions={suggestions} />
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import buildURL from 'utils/buildURL';
|
||||||
|
import { Answer, Question as QuestionT } from 'libs/graphql';
|
||||||
|
|
||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
import { Typography, Button } from '@material-ui/core';
|
||||||
|
import { Alert } from '@material-ui/lab';
|
||||||
|
|
||||||
|
export interface QuestionProps {
|
||||||
|
question: QuestionT;
|
||||||
|
answer: Answer;
|
||||||
|
onChangeAnswer: (answer: Answer) => void;
|
||||||
|
reviewMode: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Question({
|
||||||
|
question,
|
||||||
|
answer,
|
||||||
|
onChangeAnswer,
|
||||||
|
reviewMode,
|
||||||
|
}: QuestionProps) {
|
||||||
|
const classes = useStyles();
|
||||||
|
return (
|
||||||
|
<div className={classes.question}>
|
||||||
|
{question.from && (
|
||||||
|
<Typography variant="caption">{question.from}</Typography>
|
||||||
|
)}
|
||||||
|
<Typography variant="h4" component="h3">
|
||||||
|
{question.content.split('\n').map(fragment => {
|
||||||
|
return (
|
||||||
|
<Fragment key={fragment}>
|
||||||
|
{fragment}
|
||||||
|
<br />
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
{question.image && (
|
||||||
|
<Typography align="center">
|
||||||
|
<img src={buildURL('cdn', question.image)} alt={question.content} />
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
{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>
|
||||||
|
)}
|
||||||
|
{Object.values(Answer).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={() => onChangeAnswer(a)}
|
||||||
|
color={'primary'}
|
||||||
|
>
|
||||||
|
{`${upper}. `}
|
||||||
|
{image ? (
|
||||||
|
<img
|
||||||
|
src={buildURL('cdn', image)}
|
||||||
|
alt={answerContent ?? `Odpowiedź ${upper}.`}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
answerContent.split('\n').map(fragment => (
|
||||||
|
<Fragment key={fragment}>
|
||||||
|
{fragment}
|
||||||
|
<br />
|
||||||
|
</Fragment>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles(theme => {
|
||||||
|
return {
|
||||||
|
question: {
|
||||||
|
'& > *:not(:last-child)': {
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
},
|
||||||
|
'& img': {
|
||||||
|
maxWidth: '100%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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;
|
|
@ -0,0 +1,18 @@
|
||||||
|
export interface TabPanelProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
value: number;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TabPanel = ({ children, value, index }: TabPanelProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
hidden={value !== index}
|
||||||
|
style={{ display: value === index ? 'block' : 'none' }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TabPanel;
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Answer, Qualification, Question as QuestionT } from 'libs/graphql';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Container,
|
||||||
|
Paper,
|
||||||
|
Tab,
|
||||||
|
Typography,
|
||||||
|
Tabs,
|
||||||
|
AppBar,
|
||||||
|
Box,
|
||||||
|
} from '@material-ui/core';
|
||||||
|
import Section from 'common/Section/Section';
|
||||||
|
import TabPanel from './TabPanel';
|
||||||
|
import Question from './Question';
|
||||||
|
|
||||||
|
export interface TestProps {
|
||||||
|
initialQuestions: QuestionT[];
|
||||||
|
qualification: Qualification;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Test = ({ initialQuestions, qualification }: TestProps) => {
|
||||||
|
const [questions, setQuestions] = useState(initialQuestions);
|
||||||
|
const [selectedAnswers, setSelectedAnswers] = useState<Answer[]>(
|
||||||
|
new Array(initialQuestions.length).fill('')
|
||||||
|
);
|
||||||
|
const [currentTab, setCurrentTab] = useState(0);
|
||||||
|
const [reviewMode, setReviewMode] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section>
|
||||||
|
<Container>
|
||||||
|
<Typography align="center" variant="h1" gutterBottom>
|
||||||
|
Kwalifikacja <strong>{qualification.code}</strong>
|
||||||
|
</Typography>
|
||||||
|
{questions.length === 0 ? (
|
||||||
|
<Typography align="center" variant="h2">
|
||||||
|
Do tej kwalifikacji nie zostały dodane żadne pytania.
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Paper>
|
||||||
|
<AppBar color="default" position="static">
|
||||||
|
<Tabs
|
||||||
|
value={currentTab}
|
||||||
|
textColor="primary"
|
||||||
|
indicatorColor="primary"
|
||||||
|
variant="scrollable"
|
||||||
|
onChange={(_, newTab: number) => setCurrentTab(newTab)}
|
||||||
|
>
|
||||||
|
{questions.map((question, index) => {
|
||||||
|
return (
|
||||||
|
<Tab key={question.id} label={`Pytanie ${index + 1}`} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tabs>
|
||||||
|
</AppBar>
|
||||||
|
<Box padding={3}>
|
||||||
|
{questions.map((question, index) => (
|
||||||
|
<TabPanel key={question.id} value={index} index={index}>
|
||||||
|
<Question
|
||||||
|
question={question}
|
||||||
|
answer={selectedAnswers[index]}
|
||||||
|
onChangeAnswer={newAnswer =>
|
||||||
|
setSelectedAnswers(answers =>
|
||||||
|
answers.map((oldAnswer, index2) =>
|
||||||
|
index2 === index ? newAnswer : oldAnswer
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
reviewMode={reviewMode}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Test;
|
11
src/utils/buildURL.ts
Normal file
11
src/utils/buildURL.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { CDN_URI } from 'config/cdn';
|
||||||
|
|
||||||
|
const buildURL = (type: 'cdn', str: string): string => {
|
||||||
|
switch (type) {
|
||||||
|
case 'cdn':
|
||||||
|
return CDN_URI + str;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default buildURL;
|
Reference in New Issue
Block a user