diff --git a/client/src/pages/presentationEditor/components/answerComponents/AnswerText.tsx b/client/src/pages/presentationEditor/components/answerComponents/AnswerText.tsx index 9a15233e9b8828145ff980dce20bcbae828e202d..e96f65bcb18b950b674bd13b65f1e14f46c22206 100644 --- a/client/src/pages/presentationEditor/components/answerComponents/AnswerText.tsx +++ b/client/src/pages/presentationEditor/components/answerComponents/AnswerText.tsx @@ -59,10 +59,10 @@ const AnswerText = ({ activeSlide, competitionId }: AnswerTextProps) => { if (!team || !activeSlide) { return } - const activeAltId = activeSlide.questions[0].alternatives[0].id + const activeAltId = activeSlide.questions[0]?.alternatives[0]?.id return ( team.question_alternative_answers.find((questionAnswer) => questionAnswer.question_alternative_id === activeAltId) - ?.answer || 'Svar...' + ?.answer || '' ) } diff --git a/client/src/pages/presentationEditor/components/slideSettingsComponents/SlideType.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/SlideType.tsx index e9c3cd43c731b35bed1cffa69b445efadeb3bdd4..e8faa58e0e26f0bbed27549042278161df154c0b 100644 --- a/client/src/pages/presentationEditor/components/slideSettingsComponents/SlideType.tsx +++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/SlideType.tsx @@ -39,7 +39,6 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => { const components = useAppSelector( (state) => state.editor.competition.slides.find((slide) => slide.id === state.editor.activeSlideId)?.components ) - const questionComponentId = components?.find((qCompId) => qCompId.type_id === 3)?.id const openSlideTypeDialog = (type_id: number) => { setSelectedSlideType(type_id) @@ -82,6 +81,21 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => { removeQuestionComponent().then(() => createQuestionComponent(data.id)) }) .catch(console.log) + if (selectedSlideType === 1) { + // Add an alternative to text questions to allow giving answers. + await axios + .post( + `/api/competitions/${competitionId}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}/alternatives`, + { + text: '', + value: 1, + } + ) + .then(({ data }) => { + dispatch(getEditorCompetition(competitionId)) + }) + .catch(console.log) + } } } else if (!activeSlide.questions[0] && selectedSlideType !== 0) { // Change slide type from information to a question type @@ -96,6 +110,21 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => { createQuestionComponent(data.id) }) .catch(console.log) + if (selectedSlideType === 1) { + // Add an alternative to text questions to allow giving answers. + await axios + .post( + `/api/competitions/${competitionId}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}/alternatives`, + { + text: '', + value: 1, + } + ) + .then(({ data }) => { + dispatch(getEditorCompetition(competitionId)) + }) + .catch(console.log) + } } } } @@ -157,7 +186,7 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => { <DialogContent> <DialogContentText> Om du ändrar sidtypen kommer eventuella frÃ¥geinställningar gÃ¥ förlorade. Det inkluderar: frÃ¥gans namn, - poäng och svarsalternativ.{' '} + poäng, svarsalternativ och svar frÃ¥n lagen.{' '} </DialogContentText> </DialogContent> <DialogActions> diff --git a/client/src/pages/views/JudgeViewPage.tsx b/client/src/pages/views/JudgeViewPage.tsx index 675da803377ce23134485d0c2bf0bbc9c57c87a4..a287402434ae104a22cff282e6a5a43eb77989c9 100644 --- a/client/src/pages/views/JudgeViewPage.tsx +++ b/client/src/pages/views/JudgeViewPage.tsx @@ -2,7 +2,7 @@ import { Divider, List, ListItemText, Snackbar, Typography } from '@material-ui/ import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' import { Alert } from '@material-ui/lab' import React, { useEffect, useState } from 'react' -import { getPresentationCompetition } from '../../actions/presentation' +import { getPresentationCompetition, setPresentationTimer } from '../../actions/presentation' import { useAppDispatch, useAppSelector } from '../../hooks' import { RichSlide } from '../../interfaces/ApiRichModels' import { socketConnect } from '../../sockets' @@ -53,7 +53,6 @@ const JudgeViewPage: React.FC = () => { const [currentSlide, setCurrentSlide] = useState<RichSlide | undefined>(undefined) const currentQuestion = currentSlide?.questions[0] const operatorActiveSlideId = useAppSelector((state) => state.presentation.activeSlideId) - const timer = useAppSelector((state) => state.presentation.timer) const operatorActiveSlideOrder = useAppSelector( (state) => state.presentation.competition.slides.find((slide) => slide.id === operatorActiveSlideId)?.order ) @@ -74,14 +73,35 @@ const JudgeViewPage: React.FC = () => { dispatch(getPresentationCompetition(competitionId.toString())) } }, [operatorActiveSlideId]) - // useEffect(() => { - // // Every second tic of the timer, load new answers - // // TODO: use a set interval that updates every second ( look in Timer.tsx in clien/src/pages/views/components ) - // // Then clear interval when timer - Date.now() is negative - // if (timer !== null && timer - (Date.now() % 2) === 0 && competitionId) { - // dispatch(getPresentationCompetition(competitionId.toString())) - // } - // }, [timer]) + + const timer = useAppSelector((state) => state.presentation.timer) + const [timerIntervalId, setTimerIntervalId] = useState<NodeJS.Timeout | null>(null) + useEffect(() => { + if (!timer.enabled) { + if (timerIntervalId !== null && competitionId) { + clearInterval(timerIntervalId) + dispatch(getPresentationCompetition(competitionId.toString())) + } + return + } + setTimerIntervalId( + setInterval(() => { + if (timer.value === null) return + + if (timer.value - Date.now() < 0) { + if (competitionId) { + dispatch(getPresentationCompetition(competitionId.toString())) + } + dispatch(setPresentationTimer({ ...timer, enabled: false })) + return + } + + if (competitionId) { + dispatch(getPresentationCompetition(competitionId.toString())) + } + }, 1000) + ) + }, [timer.enabled]) return ( <div style={{ height: '100%' }}> <JudgeAppBar position="fixed"> diff --git a/client/src/pages/views/components/JudgeScoreDisplay.tsx b/client/src/pages/views/components/JudgeScoreDisplay.tsx index 332d4ac78cb8eb931b7ab3f16e9b943dc7ed09c6..e213d84418d799e7f1cc89305a59e9374bcd3258 100644 --- a/client/src/pages/views/components/JudgeScoreDisplay.tsx +++ b/client/src/pages/views/components/JudgeScoreDisplay.tsx @@ -1,10 +1,17 @@ -import { Box, Typography } from '@material-ui/core' +import { Box, Divider, Typography } from '@material-ui/core' import axios from 'axios' import React from 'react' import { getPresentationCompetition } from '../../../actions/presentation' import { useAppDispatch, useAppSelector } from '../../../hooks' import { RichSlide } from '../../../interfaces/ApiRichModels' -import { AnswerContainer, ScoreDisplayContainer, ScoreDisplayHeader, ScoreInput } from './styled' +import { + AnswerContainer, + Answers, + AnswersDisplay, + ScoreDisplayContainer, + ScoreDisplayHeader, + ScoreInput, +} from './styled' type ScoreDisplayProps = { teamIndex: number @@ -13,7 +20,6 @@ type ScoreDisplayProps = { const JudgeScoreDisplay = ({ teamIndex, activeSlide }: ScoreDisplayProps) => { const dispatch = useAppDispatch() - const questionTypes = useAppSelector((state) => state.types.questionTypes) const currentTeam = useAppSelector((state) => state.presentation.competition.teams[teamIndex]) const currentCompetititonId = useAppSelector((state) => state.presentation.competition.id) @@ -22,7 +28,6 @@ const JudgeScoreDisplay = ({ teamIndex, activeSlide }: ScoreDisplayProps) => { const questionMaxScore = activeQuestion?.total_score const scores = currentTeam.question_scores.map((questionAnswer) => questionAnswer.score) - const textQuestionType = questionTypes.find((questionType) => questionType.name === 'Text')?.id || 0 const handleEditScore = async (newScore: number, questionId: number) => { await axios .put(`/api/competitions/${currentCompetititonId}/teams/${currentTeam.id}/answers/question_scores/${questionId}`, { @@ -31,33 +36,38 @@ const JudgeScoreDisplay = ({ teamIndex, activeSlide }: ScoreDisplayProps) => { .then(() => dispatch(getPresentationCompetition(currentCompetititonId.toString()))) } - const getAlternativeAnswers = () => { + const getAnswers = () => { const result: string[] = [] if (!activeQuestion) { return result } - for (const alt of activeQuestion.alternatives) { - const value = currentTeam.question_alternative_answers.find((x) => x.question_alternative_id === alt.id) - if (!value) { + const ans = currentTeam.question_alternative_answers.find((x) => x.question_alternative_id === alt.id) + if (!ans) { continue } if (activeQuestion.type_id === 1) { - result.push(alt.text) - } else if (+value.answer > 0) { + // Text question + result.push(ans.answer) + } else if (+ans.answer > 0) { result.push(alt.text) } - /* - switch(activeQuestion.question_type.name){ - case "Text": - result.push(alt.text) - break; - default: - }*/ } + return result + } + const getAlternatives = () => { + const result: string[] = [] + if (!activeQuestion) { + return result + } + for (const alt of activeQuestion.alternatives) { + if (activeQuestion.type_id !== 1 && +alt.value > 0) { + // Not text question and correct answer + result.push(alt.text) + } + } return result - //const asdasd = currentTeam.question_alternative_answers.filter((x)=>x.question_alternative_id === activeQuestion.alternatives[0].io) } return ( @@ -81,15 +91,37 @@ const JudgeScoreDisplay = ({ teamIndex, activeSlide }: ScoreDisplayProps) => { </ScoreDisplayHeader> <Typography variant="h6">Alla poäng: [ {scores.map((score) => `${score} `)}]</Typography> <Typography variant="h6">Total poäng: {scores.reduce((a, b) => a + b, 0)}</Typography> - {activeQuestion && ( - <AnswerContainer> - {getAlternativeAnswers().map((v, k) => ( - <Typography variant="body1" key={k}> - <span>•</span> {v} - </Typography> - ))} - </AnswerContainer> - )} + + <AnswersDisplay> + <Answers> + <Divider /> + <Typography variant="body1">Lagets svar:</Typography> + {activeQuestion && ( + <AnswerContainer> + {getAnswers().map((v, k) => ( + <Typography variant="body1" key={k}> + <span>•</span> {v} + </Typography> + ))} + </AnswerContainer> + )} + </Answers> + + <Answers> + <Divider /> + <Typography variant="body1">Korrekta svar:</Typography> + {activeQuestion && ( + <AnswerContainer> + {getAlternatives().map((v, k) => ( + <Typography variant="body1" key={k}> + <span>•</span> {v} + </Typography> + ))} + </AnswerContainer> + )} + </Answers> + </AnswersDisplay> + {!activeQuestion && <Typography variant="body1">Inget svar</Typography>} </ScoreDisplayContainer> ) diff --git a/client/src/pages/views/components/styled.tsx b/client/src/pages/views/components/styled.tsx index b2fa87726816179791c6361fc97cfebb5eb84eb3..e73b1463d41e0d754aef2fa42ac1261973489202 100644 --- a/client/src/pages/views/components/styled.tsx +++ b/client/src/pages/views/components/styled.tsx @@ -48,3 +48,16 @@ export const ScoringInstructionsInner = styled.div` align-items: center; flex-direction: column; ` + +export const AnswersDisplay = styled.div` + display: flex; + flex-direction: row; +` + +export const Answers = styled.div` + margin-left: 15px; + margin-right: 15px; + display: flex; + align-items: center; + flex-direction: column; +`