Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • tddd96-grupp11/teknikattan-scoring-system
1 result
Show changes
Commits on Source (10)
Showing
with 157 additions and 32 deletions
![coverage report](https://gitlab.liu.se/tddd96-grupp11/teknikattan-scoring-system/badges/dev/coverage.svg?job=client:test&key_text=Client+Coverage&key_width=110)
![coverage report](https://gitlab.liu.se/tddd96-grupp11/teknikattan-scoring-system/badges/dev/coverage.svg?job=server:test&key_text=Server+Coverage&key_width=115)
# Scoring system for Teknikåttan # Scoring system for Teknikåttan
This is the scoring system for Teknikåttan! This is the scoring system for Teknikåttan!
......
...@@ -17285,7 +17285,8 @@ ...@@ -17285,7 +17285,8 @@
}, },
"ssri": { "ssri": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
"integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
"requires": { "requires": {
"figgy-pudding": "^3.5.1" "figgy-pudding": "^3.5.1"
} }
......
export enum ComponentTypes { export enum ComponentTypes {
Text = 1, Text = 1,
Image, Image,
QuestionAlternative, Question,
} }
...@@ -94,6 +94,16 @@ export interface TextComponent extends Component { ...@@ -94,6 +94,16 @@ export interface TextComponent extends Component {
font: string font: string
} }
export interface QuestionAlternativeComponent extends Component { export interface QuestionComponent extends Component {
id: number
x: number
y: number
w: number
h: number
slide_id: number
type_id: number
view_type_id: number
text: string
media: Media
question_id: number question_id: number
} }
...@@ -131,6 +131,7 @@ const CompetitionManager: React.FC = (props: any) => { ...@@ -131,6 +131,7 @@ const CompetitionManager: React.FC = (props: any) => {
} }
} }
/** Start the competition by redirecting with URL with Code */
const handleStartCompetition = () => { const handleStartCompetition = () => {
const operatorCode = codes.find((code) => code.view_type_id === 4)?.code const operatorCode = codes.find((code) => code.view_type_id === 4)?.code
if (operatorCode) { if (operatorCode) {
...@@ -138,6 +139,7 @@ const CompetitionManager: React.FC = (props: any) => { ...@@ -138,6 +139,7 @@ const CompetitionManager: React.FC = (props: any) => {
} }
} }
/** Fetch all the connection codes from the server */
const getCodes = async (id: number) => { const getCodes = async (id: number) => {
await axios await axios
.get(`/api/competitions/${id}/codes`) .get(`/api/competitions/${id}/codes`)
...@@ -147,6 +149,7 @@ const CompetitionManager: React.FC = (props: any) => { ...@@ -147,6 +149,7 @@ const CompetitionManager: React.FC = (props: any) => {
.catch(console.log) .catch(console.log)
} }
/** Fetch all the teams from the server that is connected to a specific competition*/
const getTeams = async (id: number) => { const getTeams = async (id: number) => {
await axios await axios
.get(`/api/competitions/${id}/teams`) .get(`/api/competitions/${id}/teams`)
...@@ -159,6 +162,7 @@ const CompetitionManager: React.FC = (props: any) => { ...@@ -159,6 +162,7 @@ const CompetitionManager: React.FC = (props: any) => {
}) })
} }
/** Fetch the copetition name from the server */
const getCompetitionName = async () => { const getCompetitionName = async () => {
await axios await axios
.get(`/api/competitions/${activeId}`) .get(`/api/competitions/${activeId}`)
...@@ -198,16 +202,18 @@ const CompetitionManager: React.FC = (props: any) => { ...@@ -198,16 +202,18 @@ const CompetitionManager: React.FC = (props: any) => {
return typeName return typeName
} }
/** Handles the opening of the code dialog box */
const handleOpenDialog = async () => { const handleOpenDialog = async () => {
await getCompetitionName() await getCompetitionName()
setDialogIsOpen(true) setDialogIsOpen(true)
} }
/** Handles the closing of the code dialog box */
const handleCloseDialog = () => { const handleCloseDialog = () => {
setDialogIsOpen(false) setDialogIsOpen(false)
setAnchorEl(null) setAnchorEl(null)
} }
/** Function that copies an existing competition */
const handleDuplicateCompetition = async () => { const handleDuplicateCompetition = async () => {
if (activeId) { if (activeId) {
await axios await axios
......
...@@ -6,6 +6,11 @@ import NumberOfCompetitions from './components/NumberOfCompetitions' ...@@ -6,6 +6,11 @@ import NumberOfCompetitions from './components/NumberOfCompetitions'
import NumberOfRegions from './components/NumberOfRegions' import NumberOfRegions from './components/NumberOfRegions'
import NumberOfUsers from './components/NumberOfUsers' import NumberOfUsers from './components/NumberOfUsers'
/**
* This is the first page that is shown after a user logs in. It shows som statistics about the site.
*
*/
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
root: { root: {
......
...@@ -2,6 +2,8 @@ import { Box, Typography } from '@material-ui/core' ...@@ -2,6 +2,8 @@ import { Box, Typography } from '@material-ui/core'
import React from 'react' import React from 'react'
import { useAppSelector } from '../../../../hooks' import { useAppSelector } from '../../../../hooks'
/** This component show information about the currently logged in user */
const CurrentUser: React.FC = () => { const CurrentUser: React.FC = () => {
const currentUser = useAppSelector((state: { user: { userInfo: any } }) => state.user.userInfo) const currentUser = useAppSelector((state: { user: { userInfo: any } }) => state.user.userInfo)
return ( return (
......
...@@ -2,6 +2,8 @@ import { Box, Typography } from '@material-ui/core' ...@@ -2,6 +2,8 @@ import { Box, Typography } from '@material-ui/core'
import React from 'react' import React from 'react'
import { useAppSelector } from '../../../../hooks' import { useAppSelector } from '../../../../hooks'
/** Shows how many competitions is on the system */
const NumberOfCompetitions: React.FC = () => { const NumberOfCompetitions: React.FC = () => {
const competitions = useAppSelector((state) => state.statistics.competitions) const competitions = useAppSelector((state) => state.statistics.competitions)
......
...@@ -3,6 +3,8 @@ import React, { useEffect } from 'react' ...@@ -3,6 +3,8 @@ import React, { useEffect } from 'react'
import { getCities } from '../../../../actions/cities' import { getCities } from '../../../../actions/cities'
import { useAppDispatch, useAppSelector } from '../../../../hooks' import { useAppDispatch, useAppSelector } from '../../../../hooks'
/** Shows how many regions is on the system */
const NumberOfRegions: React.FC = () => { const NumberOfRegions: React.FC = () => {
const regions = useAppSelector((state) => state.statistics.regions) const regions = useAppSelector((state) => state.statistics.regions)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
......
...@@ -3,6 +3,8 @@ import React, { useEffect } from 'react' ...@@ -3,6 +3,8 @@ import React, { useEffect } from 'react'
import { getSearchUsers } from '../../../../actions/searchUser' import { getSearchUsers } from '../../../../actions/searchUser'
import { useAppDispatch, useAppSelector } from '../../../../hooks' import { useAppDispatch, useAppSelector } from '../../../../hooks'
/** Shows how many users are on the system */
const NumberOfUsers: React.FC = () => { const NumberOfUsers: React.FC = () => {
const usersTotal = useAppSelector((state) => state.statistics.users) const usersTotal = useAppSelector((state) => state.statistics.users)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
......
...@@ -30,6 +30,7 @@ const useStyles = makeStyles((theme: Theme) => ...@@ -30,6 +30,7 @@ const useStyles = makeStyles((theme: Theme) =>
type formType = FormModel<AddCityModel> type formType = FormModel<AddCityModel>
/** add a region form with some constraints. */
const schema: Yup.SchemaOf<formType> = Yup.object({ const schema: Yup.SchemaOf<formType> = Yup.object({
model: Yup.object() model: Yup.object()
.shape({ .shape({
......
...@@ -14,6 +14,9 @@ import { getCities } from '../../../actions/cities' ...@@ -14,6 +14,9 @@ import { getCities } from '../../../actions/cities'
import { useAppDispatch, useAppSelector } from '../../../hooks' import { useAppDispatch, useAppSelector } from '../../../hooks'
import { RemoveMenuItem, TopBar } from '../styledComp' import { RemoveMenuItem, TopBar } from '../styledComp'
import AddRegion from './AddRegion' import AddRegion from './AddRegion'
/** shows all the regions in a list */
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
table: { table: {
......
/** Add a user component */
import { Button, FormControl, InputLabel, MenuItem, Popover, TextField } from '@material-ui/core' import { Button, FormControl, InputLabel, MenuItem, Popover, TextField } from '@material-ui/core'
import PersonAddIcon from '@material-ui/icons/PersonAdd' import PersonAddIcon from '@material-ui/icons/PersonAdd'
import { Alert, AlertTitle } from '@material-ui/lab' import { Alert, AlertTitle } from '@material-ui/lab'
...@@ -16,6 +18,7 @@ type formType = FormModel<AddUserModel> ...@@ -16,6 +18,7 @@ type formType = FormModel<AddUserModel>
const noRoleSelected = 'Välj roll' const noRoleSelected = 'Välj roll'
const noCitySelected = 'Välj stad' const noCitySelected = 'Välj stad'
/** Form when adding a user with some constraints */
const userSchema: Yup.SchemaOf<formType> = Yup.object({ const userSchema: Yup.SchemaOf<formType> = Yup.object({
model: Yup.object() model: Yup.object()
.shape({ .shape({
......
/** This is the login page, it contains two child components, one is
* to log in as an admin, the other is to connect to a competition using a code
*/
import { AppBar, Tab, Tabs } from '@material-ui/core' import { AppBar, Tab, Tabs } from '@material-ui/core'
import React from 'react' import React from 'react'
import AdminLogin from './components/AdminLogin' import AdminLogin from './components/AdminLogin'
......
/** Component that handles the log in when a user is an admin */
import { Button, TextField, Typography } from '@material-ui/core' import { Button, TextField, Typography } from '@material-ui/core'
import { Alert, AlertTitle } from '@material-ui/lab' import { Alert, AlertTitle } from '@material-ui/lab'
import { Formik, FormikHelpers } from 'formik' import { Formik, FormikHelpers } from 'formik'
......
/** Component that handles the log in when a user connects to a competition through a code */
import { Button, TextField, Typography } from '@material-ui/core' import { Button, TextField, Typography } from '@material-ui/core'
import { Alert, AlertTitle } from '@material-ui/lab' import { Alert, AlertTitle } from '@material-ui/lab'
import { Formik } from 'formik' import { Formik } from 'formik'
...@@ -38,7 +40,7 @@ const CompetitionLogin: React.FC = () => { ...@@ -38,7 +40,7 @@ const CompetitionLogin: React.FC = () => {
const handleCompetitionSubmit = async (values: CompetitionLoginFormModel) => { const handleCompetitionSubmit = async (values: CompetitionLoginFormModel) => {
dispatch(loginCompetition(values.model.code, history, true)) dispatch(loginCompetition(values.model.code, history, true))
} }
return ( return (
<Formik <Formik
initialValues={competitionInitialValues} initialValues={competitionInitialValues}
......
import { Card, Divider, ListItem, Typography } from '@material-ui/core'
import React from 'react'
import { useAppSelector } from '../../../hooks'
import AnswerMultiple from './answerComponents/AnswerMultiple'
import AnswerSingle from './answerComponents/AnswerSingle'
import AnswerText from './answerComponents/AnswerText'
import { Center } from './styled'
type QuestionComponentProps = {
variant: 'editor' | 'presentation'
}
const QuestionComponentDisplay = ({ variant }: QuestionComponentProps) => {
const activeSlide = useAppSelector((state) => {
if (variant === 'editor')
return state.editor.competition.slides.find((slide) => slide.id === state.editor.activeSlideId)
return state.presentation.competition.slides.find((slide) => slide.id === state.presentation.slide?.id)
})
const timer = activeSlide?.timer
const total_score = activeSlide?.questions[0].total_score
const questionName = activeSlide?.questions[0].name
const questionTypeId = activeSlide?.questions[0].type_id
const questionTypeName = useAppSelector(
(state) => state.types.questionTypes.find((qType) => qType.id === questionTypeId)?.name
)
const getAlternatives = () => {
switch (questionTypeName) {
case 'Text':
if (activeSlide) {
return <AnswerText activeSlide={activeSlide} competitionId={activeSlide.competition_id.toString()} />
}
return
case 'Practical':
return
case 'Multiple':
if (activeSlide) {
return (
<AnswerMultiple
variant={variant}
activeSlide={activeSlide}
competitionId={activeSlide.competition_id.toString()}
/>
)
}
return
case 'Single':
if (activeSlide) {
return (
<AnswerSingle
variant={variant}
activeSlide={activeSlide}
competitionId={activeSlide.competition_id.toString()}
/>
)
}
return
default:
break
}
}
return (
<Card style={{ maxHeight: '100%', overflowY: 'auto' }}>
<ListItem>
<Center style={{ justifyContent: 'space-evenly' }}>
<Typography>Poäng: {total_score}</Typography>
<Typography>{questionName}</Typography>
<Typography>Timer: {timer}</Typography>
</Center>
</ListItem>
<Divider />
{getAlternatives()}
</Card>
)
}
export default QuestionComponentDisplay
...@@ -9,6 +9,7 @@ import { Component, ImageComponent, TextComponent } from '../../../interfaces/Ap ...@@ -9,6 +9,7 @@ import { Component, ImageComponent, TextComponent } from '../../../interfaces/Ap
import { Position, Size } from '../../../interfaces/Components' import { Position, Size } from '../../../interfaces/Components'
import { RemoveMenuItem } from '../../admin/styledComp' import { RemoveMenuItem } from '../../admin/styledComp'
import ImageComponentDisplay from './ImageComponentDisplay' import ImageComponentDisplay from './ImageComponentDisplay'
import QuestionComponentDisplay from './QuestionComponentDisplay'
import { HoverContainer } from './styled' import { HoverContainer } from './styled'
import TextComponentDisplay from './TextComponentDisplay' import TextComponentDisplay from './TextComponentDisplay'
//import NestedMenuItem from 'material-ui-nested-menu-item' //import NestedMenuItem from 'material-ui-nested-menu-item'
...@@ -126,6 +127,12 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) => ...@@ -126,6 +127,12 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) =>
/> />
</HoverContainer> </HoverContainer>
) )
case ComponentTypes.Question:
return (
<HoverContainer hover={hover}>
<QuestionComponentDisplay variant="editor" />
</HoverContainer>
)
default: default:
break break
} }
......
...@@ -77,15 +77,7 @@ const SlideDisplay = ({ variant, activeViewTypeId }: SlideDisplayProps) => { ...@@ -77,15 +77,7 @@ const SlideDisplay = ({ variant, activeViewTypeId }: SlideDisplayProps) => {
scale={scale} scale={scale}
/> />
) )
return ( return <PresentationComponent key={component.id} component={component} scale={scale} />
<PresentationComponent
height={height}
width={width}
key={component.id}
component={component}
scale={scale}
/>
)
})} })}
</SlideEditorPaper> </SlideEditorPaper>
</SlideEditorContainerRatio> </SlideEditorContainerRatio>
......
/* This file compiles and renders the right hand slide settings bar, under the tab "SIDA". /* This file compiles and renders the right hand slide settings bar, under the tab "SIDA".
*/ */
import { Divider, List, ListItem, ListItemText, TextField, Typography } from '@material-ui/core' import { Divider } from '@material-ui/core'
import React, { useState } from 'react' import React from 'react'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { useAppSelector } from '../../../hooks' import { useAppSelector } from '../../../hooks'
import BackgroundImageSelect from './BackgroundImageSelect'
import Images from './slideSettingsComponents/Images'
import Instructions from './slideSettingsComponents/Instructions' import Instructions from './slideSettingsComponents/Instructions'
import MultipleChoiceAlternatives from './slideSettingsComponents/MultipleChoiceAlternatives' import MultipleChoiceAlternatives from './slideSettingsComponents/MultipleChoiceAlternatives'
import QuestionSettings from './slideSettingsComponents/QuestionSettings'
import SingleChoiceAlternatives from './slideSettingsComponents/SingleChoiceAlternatives'
import SlideType from './slideSettingsComponents/SlideType' import SlideType from './slideSettingsComponents/SlideType'
import { Center, ImportedImage, SettingsList, PanelContainer } from './styled'
import Timer from './slideSettingsComponents/Timer'
import Images from './slideSettingsComponents/Images'
import Texts from './slideSettingsComponents/Texts' import Texts from './slideSettingsComponents/Texts'
import QuestionSettings from './slideSettingsComponents/QuestionSettings' import Timer from './slideSettingsComponents/Timer'
import BackgroundImageSelect from './BackgroundImageSelect' import { PanelContainer, SettingsList } from './styled'
interface CompetitionParams { interface CompetitionParams {
competitionId: string competitionId: string
...@@ -36,19 +37,21 @@ const SlideSettings: React.FC = () => { ...@@ -36,19 +37,21 @@ const SlideSettings: React.FC = () => {
</SettingsList> </SettingsList>
{activeSlide?.questions[0] && <QuestionSettings activeSlide={activeSlide} competitionId={competitionId} />} {activeSlide?.questions[0] && <QuestionSettings activeSlide={activeSlide} competitionId={competitionId} />}
{ {
// Choose answer alternatives depending on the slide type // Choose answer alternatives, depending on the slide type
} }
{activeSlide?.questions[0]?.type_id === 1 && ( {(activeSlide?.questions[0]?.type_id === 1 || activeSlide?.questions[0]?.type_id === 2) && (
<Instructions activeSlide={activeSlide} competitionId={competitionId} />
)}
{activeSlide?.questions[0]?.type_id === 2 && (
<Instructions activeSlide={activeSlide} competitionId={competitionId} /> <Instructions activeSlide={activeSlide} competitionId={competitionId} />
)} )}
{activeSlide?.questions[0]?.type_id === 3 && ( {activeSlide?.questions[0]?.type_id === 3 && (
<MultipleChoiceAlternatives activeSlide={activeSlide} competitionId={competitionId} /> <MultipleChoiceAlternatives activeSlide={activeSlide} competitionId={competitionId} />
)} )}
{activeSlide?.questions[0]?.type_id === 4 && (
<SingleChoiceAlternatives activeSlide={activeSlide} competitionId={competitionId} />
)}
{activeSlide && ( {activeSlide && (
<Texts activeViewTypeId={activeViewTypeId} activeSlide={activeSlide} competitionId={competitionId} /> <Texts activeViewTypeId={activeViewTypeId} activeSlide={activeSlide} competitionId={competitionId} />
)} )}
......