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 (5)
Showing
with 371 additions and 64 deletions
...@@ -10,10 +10,17 @@ import { City } from '../../../interfaces/ApiModels' ...@@ -10,10 +10,17 @@ import { City } from '../../../interfaces/ApiModels'
import { AddCompetitionModel, FormModel } from '../../../interfaces/FormModels' import { AddCompetitionModel, FormModel } from '../../../interfaces/FormModels'
import { AddButton, AddContent, AddForm } from '../styledComp' import { AddButton, AddContent, AddForm } from '../styledComp'
/**
* Component description:
* This component handles the functionality when adding a competition to the system
* This component is a child component to CompetitionManager.tsx
*/
type formType = FormModel<AddCompetitionModel> type formType = FormModel<AddCompetitionModel>
const noCitySelected = 'Välj stad' const noCitySelected = 'Välj stad'
//Description of the form and what is required
const competitionSchema: Yup.SchemaOf<formType> = Yup.object({ const competitionSchema: Yup.SchemaOf<formType> = Yup.object({
model: Yup.object() model: Yup.object()
.shape({ .shape({
...@@ -45,20 +52,25 @@ const AddCompetition: React.FC = (props: any) => { ...@@ -45,20 +52,25 @@ const AddCompetition: React.FC = (props: any) => {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const id = open ? 'simple-popover' : undefined const id = open ? 'simple-popover' : undefined
const currentYear = new Date().getFullYear() const currentYear = new Date().getFullYear()
// Handles the actual submition to the database
const handleCompetitionSubmit = async (values: formType, actions: FormikHelpers<formType>) => { const handleCompetitionSubmit = async (values: formType, actions: FormikHelpers<formType>) => {
// The parameters sent
const params = { const params = {
name: values.model.name, name: values.model.name,
year: values.model.year, year: values.model.year,
city_id: selectedCity?.id as number, city_id: selectedCity?.id as number,
} }
await axios await axios
.post('/competitions', params) .post('/competitions', params) // send to database
.then(() => { .then(() => {
actions.resetForm() actions.resetForm() // reset the form
setAnchorEl(null) setAnchorEl(null)
dispatch(getCompetitions()) dispatch(getCompetitions()) // refresh competitions
setSelectedCity(undefined) setSelectedCity(undefined)
}) })
// if the post request fails
.catch(({ response }) => { .catch(({ response }) => {
console.warn(response.data) console.warn(response.data)
if (response.data && response.data.message) if (response.data && response.data.message)
...@@ -83,6 +95,12 @@ const AddCompetition: React.FC = (props: any) => { ...@@ -83,6 +95,12 @@ const AddCompetition: React.FC = (props: any) => {
> >
Ny Tävling Ny Tävling
</AddButton> </AddButton>
{/**
* The "pop up" menu for adding a competition
* contains 3 fields; Name, Region and Year
*
*/}
<Popover <Popover
id={id} id={id}
open={open} open={open}
......
...@@ -21,6 +21,13 @@ import { CompetitionFilterParams } from '../../../interfaces/FilterParams' ...@@ -21,6 +21,13 @@ import { CompetitionFilterParams } from '../../../interfaces/FilterParams'
import { FilterContainer, RemoveMenuItem, TopBar, YearFilterTextField } from '../styledComp' import { FilterContainer, RemoveMenuItem, TopBar, YearFilterTextField } from '../styledComp'
import AddCompetition from './AddCompetition' import AddCompetition from './AddCompetition'
/**
* Component description:
* This component shows a list of all the competitions which a user can search through
* We can also start, duplicate or delete a competition
*/
// Use defined styling
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
table: { table: {
...@@ -41,6 +48,7 @@ const CompetitionManager: React.FC = (props: any) => { ...@@ -41,6 +48,7 @@ const CompetitionManager: React.FC = (props: any) => {
const filterParams = useAppSelector((state) => state.competitions.filterParams) const filterParams = useAppSelector((state) => state.competitions.filterParams)
const competitionTotal = useAppSelector((state) => state.competitions.total) const competitionTotal = useAppSelector((state) => state.competitions.total)
const cities = useAppSelector((state) => state.cities.cities) const cities = useAppSelector((state) => state.cities.cities)
const classes = useStyles() const classes = useStyles()
const noFilterText = 'Alla' const noFilterText = 'Alla'
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
...@@ -59,6 +67,7 @@ const CompetitionManager: React.FC = (props: any) => { ...@@ -59,6 +67,7 @@ const CompetitionManager: React.FC = (props: any) => {
dispatch(getCompetitions()) dispatch(getCompetitions())
}, []) }, [])
// Searchfuntion to search for a specific string
const onSearchChange = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => { const onSearchChange = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
if (timerHandle) { if (timerHandle) {
clearTimeout(timerHandle) clearTimeout(timerHandle)
...@@ -69,13 +78,14 @@ const CompetitionManager: React.FC = (props: any) => { ...@@ -69,13 +78,14 @@ const CompetitionManager: React.FC = (props: any) => {
dispatch(setFilterParams({ ...filterParams, name: event.target.value })) dispatch(setFilterParams({ ...filterParams, name: event.target.value }))
} }
// Function to remove a competition from the systems database
const handleDeleteCompetition = async () => { const handleDeleteCompetition = async () => {
if (activeId) { if (activeId) {
await axios await axios
.delete(`/competitions/${activeId}`) .delete(`/competitions/${activeId}`)
.then(() => { .then(() => {
setAnchorEl(null) setAnchorEl(null)
dispatch(getCompetitions()) dispatch(getCompetitions()) // refresh the competition list
}) })
.catch(({ response }) => { .catch(({ response }) => {
console.warn(response.data) console.warn(response.data)
...@@ -83,6 +93,11 @@ const CompetitionManager: React.FC = (props: any) => { ...@@ -83,6 +93,11 @@ const CompetitionManager: React.FC = (props: any) => {
} }
} }
const handleStartCompetition = () => {
history.push(`/presenter/id=${activeId}&code=123123`)
console.log('GLHF!')
}
const handleDuplicateCompetition = async () => { const handleDuplicateCompetition = async () => {
if (activeId) { if (activeId) {
await axios await axios
...@@ -177,6 +192,7 @@ const CompetitionManager: React.FC = (props: any) => { ...@@ -177,6 +192,7 @@ const CompetitionManager: React.FC = (props: any) => {
))} ))}
</TableBody> </TableBody>
</Table> </Table>
{/** We can't find any competitions at all or with a specific filter */}
{(!competitions || competitions.length === 0) && ( {(!competitions || competitions.length === 0) && (
<Typography>Inga tävlingar hittades med nuvarande filter</Typography> <Typography>Inga tävlingar hittades med nuvarande filter</Typography>
)} )}
...@@ -190,7 +206,7 @@ const CompetitionManager: React.FC = (props: any) => { ...@@ -190,7 +206,7 @@ const CompetitionManager: React.FC = (props: any) => {
onChangePage={(event, newPage) => handleFilterChange({ ...filterParams, page: newPage })} onChangePage={(event, newPage) => handleFilterChange({ ...filterParams, page: newPage })}
/> />
<Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}> <Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
<MenuItem onClick={() => history.push(`/presenter/id=${activeId}&code=123123`)}>Starta</MenuItem> <MenuItem onClick={handleStartCompetition}>Starta</MenuItem>
<MenuItem onClick={handleDuplicateCompetition}>Duplicera</MenuItem> <MenuItem onClick={handleDuplicateCompetition}>Duplicera</MenuItem>
<RemoveMenuItem onClick={handleDeleteCompetition}>Ta bort</RemoveMenuItem> <RemoveMenuItem onClick={handleDeleteCompetition}>Ta bort</RemoveMenuItem>
</Menu> </Menu>
......
import { import {
Button, Button,
createStyles, createStyles,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
FormControl, FormControl,
InputLabel, InputLabel,
makeStyles, makeStyles,
...@@ -8,6 +13,8 @@ import { ...@@ -8,6 +13,8 @@ import {
Popover, Popover,
TextField, TextField,
Theme, Theme,
useMediaQuery,
useTheme,
} from '@material-ui/core' } from '@material-ui/core'
import MoreHorizIcon from '@material-ui/icons/MoreHoriz' import MoreHorizIcon from '@material-ui/icons/MoreHoriz'
import { Alert, AlertTitle } from '@material-ui/lab' import { Alert, AlertTitle } from '@material-ui/lab'
...@@ -62,6 +69,11 @@ type UserIdProps = { ...@@ -62,6 +69,11 @@ type UserIdProps = {
} }
const EditUser = ({ user }: UserIdProps) => { const EditUser = ({ user }: UserIdProps) => {
// for dialog alert
const [openAlert, setOpen] = React.useState(false)
const theme = useTheme()
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'))
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const classes = useStyles() const classes = useStyles()
...@@ -87,21 +99,25 @@ const EditUser = ({ user }: UserIdProps) => { ...@@ -87,21 +99,25 @@ const EditUser = ({ user }: UserIdProps) => {
setAnchorEl(event.currentTarget) setAnchorEl(event.currentTarget)
} }
const handleClose = () => { const handleClose = () => {
setOpen(false)
setAnchorEl(null) setAnchorEl(null)
} }
const handleVerifyDelete = () => {
setOpen(true)
}
const handleDeleteUsers = async () => { const handleDeleteUsers = async () => {
if (confirm('Are u sure?')) { setOpen(false)
await axios await axios
.delete(`/auth/delete/${user.id}`) .delete(`/auth/delete/${user.id}`)
.then(() => { .then(() => {
setAnchorEl(null) setAnchorEl(null)
dispatch(getSearchUsers()) dispatch(getSearchUsers())
}) })
.catch(({ response }) => { .catch(({ response }) => {
console.warn(response.data) console.warn(response.data)
}) })
}
} }
const handleSubmit = async (values: formType, actions: FormikHelpers<formType>) => { const handleSubmit = async (values: formType, actions: FormikHelpers<formType>) => {
...@@ -273,7 +289,7 @@ const EditUser = ({ user }: UserIdProps) => { ...@@ -273,7 +289,7 @@ const EditUser = ({ user }: UserIdProps) => {
Ändra Ändra
</Button> </Button>
<Button <Button
onClick={handleDeleteUsers} onClick={handleVerifyDelete}
className={classes.deleteButton} className={classes.deleteButton}
fullWidth fullWidth
variant="contained" variant="contained"
...@@ -281,6 +297,27 @@ const EditUser = ({ user }: UserIdProps) => { ...@@ -281,6 +297,27 @@ const EditUser = ({ user }: UserIdProps) => {
> >
Ta bort Ta bort
</Button> </Button>
<Dialog
fullScreen={fullScreen}
open={openAlert}
onClose={handleClose}
aria-labelledby="responsive-dialog-title"
>
<DialogTitle id="responsive-dialog-title">{'Ta bort användare?'}</DialogTitle>
<DialogContent>
<DialogContentText>
Är du säker på att du vill ta bort användaren och all dess information från systemet?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleClose} color="primary">
Avbryt
</Button>
<Button onClick={handleDeleteUsers} color="primary" autoFocus>
Ta bort
</Button>
</DialogActions>
</Dialog>
{formik.errors.error && ( {formik.errors.error && (
<Alert severity="error"> <Alert severity="error">
......
import React from 'react';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import { useTheme } from '@material-ui/core/styles';
export default function ResponsiveDialog() {
const [open, setOpen] = React.useState(false);
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open responsive dialog
</Button>
<Dialog
fullScreen={fullScreen}
open={open}
onClose={handleClose}
aria-labelledby="responsive-dialog-title"
>
<DialogTitle id="responsive-dialog-title">{"Use Google's location service?"}</DialogTitle>
<DialogContent>
<DialogContentText>
Let Google help apps determine location. This means sending anonymous location data to
Google, even when no apps are running.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleClose} color="primary">
Disagree
</Button>
<Button onClick={handleClose} color="primary" autoFocus>
Agree
</Button>
</DialogActions>
</Dialog>
</div>
);
}
\ No newline at end of file
import React from 'react' import React from 'react'
import SlideDisplay from './components/SlideDisplay'
const AudienceViewPage: React.FC = () => { const AudienceViewPage: React.FC = () => {
return <div>Publik</div> return <SlideDisplay />
} }
export default AudienceViewPage export default AudienceViewPage
import { Divider, List, ListItemText } from '@material-ui/core' import { Divider, List, ListItemText, Typography } from '@material-ui/core'
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
...@@ -10,6 +10,7 @@ import { ...@@ -10,6 +10,7 @@ import {
} from '../../actions/presentation' } from '../../actions/presentation'
import { useAppDispatch, useAppSelector } from '../../hooks' import { useAppDispatch, useAppSelector } from '../../hooks'
import { ViewParams } from '../../interfaces/ViewParams' import { ViewParams } from '../../interfaces/ViewParams'
import { socket_connect } from '../../sockets'
import { SlideListItem } from '../presentationEditor/styled' import { SlideListItem } from '../presentationEditor/styled'
import JudgeScoreDisplay from './components/JudgeScoreDisplay' import JudgeScoreDisplay from './components/JudgeScoreDisplay'
import SlideDisplay from './components/SlideDisplay' import SlideDisplay from './components/SlideDisplay'
...@@ -43,17 +44,20 @@ const JudgeViewPage: React.FC = () => { ...@@ -43,17 +44,20 @@ const JudgeViewPage: React.FC = () => {
const { id, code }: ViewParams = useParams() const { id, code }: ViewParams = useParams()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const [activeSlideIndex, setActiveSlideIndex] = useState<number>(0) const [activeSlideIndex, setActiveSlideIndex] = useState<number>(0)
useEffect(() => {
dispatch(getPresentationCompetition(id))
dispatch(getPresentationTeams(id))
dispatch(setPresentationCode(code))
}, [])
const teams = useAppSelector((state) => state.presentation.teams) const teams = useAppSelector((state) => state.presentation.teams)
const slides = useAppSelector((state) => state.presentation.competition.slides) const slides = useAppSelector((state) => state.presentation.competition.slides)
const handleSelectSlide = (index: number) => { const handleSelectSlide = (index: number) => {
setActiveSlideIndex(index) setActiveSlideIndex(index)
dispatch(setCurrentSlide(slides[index])) dispatch(setCurrentSlide(slides[index]))
} }
useEffect(() => {
socket_connect()
dispatch(getPresentationCompetition(id))
dispatch(getPresentationTeams(id))
dispatch(setPresentationCode(code))
}, [])
return ( return (
<div> <div>
<JudgeAppBar position="fixed"> <JudgeAppBar position="fixed">
...@@ -80,6 +84,7 @@ const JudgeViewPage: React.FC = () => { ...@@ -80,6 +84,7 @@ const JudgeViewPage: React.FC = () => {
button button
key={slide.id} key={slide.id}
> >
<Typography variant="h6">Slide ID: {slide.id} </Typography>
<ListItemText primary={slide.title} /> <ListItemText primary={slide.title} />
</SlideListItem> </SlideListItem>
))} ))}
...@@ -103,7 +108,6 @@ const JudgeViewPage: React.FC = () => { ...@@ -103,7 +108,6 @@ const JudgeViewPage: React.FC = () => {
))} ))}
</List> </List>
</RightDrawer> </RightDrawer>
aaa
<Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}> <Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}>
<div className={classes.toolbar} /> <div className={classes.toolbar} />
<SlideDisplay /> <SlideDisplay />
......
import { List, ListItem, Popover } from '@material-ui/core' import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
List,
ListItem,
Popover,
Tooltip,
Typography,
useMediaQuery,
useTheme,
} from '@material-ui/core'
import AssignmentIcon from '@material-ui/icons/Assignment'
import BackspaceIcon from '@material-ui/icons/Backspace'
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'
import ChevronRightIcon from '@material-ui/icons/ChevronRight' import ChevronRightIcon from '@material-ui/icons/ChevronRight'
import TimerIcon from '@material-ui/icons/Timer'
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import { useHistory, useParams } from 'react-router-dom' import { useHistory, useParams } from 'react-router-dom'
import { getPresentationCompetition, getPresentationTeams, setPresentationCode } from '../../actions/presentation' import { getPresentationCompetition, getPresentationTeams, setPresentationCode } from '../../actions/presentation'
import { useAppDispatch, useAppSelector } from '../../hooks' import { useAppDispatch, useAppSelector } from '../../hooks'
import { ViewParams } from '../../interfaces/ViewParams' import { ViewParams } from '../../interfaces/ViewParams'
import {
socketEndPresentation,
socketSetSlide,
socketSetSlideNext,
socketSetSlidePrev,
socketStartPresentation,
socketStartTimer,
socket_connect,
} from '../../sockets'
import SlideDisplay from './components/SlideDisplay' import SlideDisplay from './components/SlideDisplay'
import SocketTest from './components/SocketTest'
import Timer from './components/Timer' import Timer from './components/Timer'
import { PresenterButton, PresenterContainer, PresenterFooter, PresenterHeader } from './styled' import {
PresenterButton,
PresenterContainer,
PresenterFooter,
PresenterHeader,
SlideCounter,
ToolBarContainer,
} from './styled'
/**
* Presentation is an active competition
*/
const PresenterViewPage: React.FC = () => { const PresenterViewPage: React.FC = () => {
// for dialog alert
const [openAlert, setOpen] = React.useState(false)
const theme = useTheme()
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'))
const teams = useAppSelector((state) => state.presentation.teams) const teams = useAppSelector((state) => state.presentation.teams)
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null) const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null)
const { id, code }: ViewParams = useParams() const { id, code }: ViewParams = useParams()
const presentation = useAppSelector((state) => state.presentation)
const history = useHistory() const history = useHistory()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
useEffect(() => { useEffect(() => {
dispatch(getPresentationCompetition(id)) dispatch(getPresentationCompetition(id))
dispatch(getPresentationTeams(id)) dispatch(getPresentationTeams(id))
dispatch(setPresentationCode(code)) dispatch(setPresentationCode(code))
socket_connect()
socketSetSlide // Behövs denna?
setTimeout(startCompetition, 500) // Ghetto, wait for everything to load
console.log(id)
}, []) }, [])
const handleOpenPopover = (event: React.MouseEvent<HTMLButtonElement>) => { const handleOpenPopover = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget) setAnchorEl(event.currentTarget)
} }
const handleClose = () => { const handleClose = () => {
setOpen(false)
setAnchorEl(null) setAnchorEl(null)
} }
const handleNextSlidePressed = () => {
// dispatch(setCurrentSlideNext()) const startCompetition = () => {
// syncSlide() socketStartPresentation()
console.log('started competition for')
console.log(id)
}
const handleVerifyExit = () => {
setOpen(true)
} }
const handlePreviousSlidePressed = () => {
// dispatch(setCurrentSlidePrevious()) const endCompetition = () => {
// syncSlide() setOpen(false)
socketEndPresentation()
history.push('/admin/tävlingshanterare')
window.location.reload(false) // TODO: fix this ugly hack, we "need" to refresh site to be able to run the competition correctly again
} }
return ( return (
<PresenterContainer> <PresenterContainer>
<PresenterHeader> <PresenterHeader>
<PresenterButton onClick={handleOpenPopover} color="primary" variant="contained"> <Tooltip title="Avsluta tävling" arrow>
Visa ställning <PresenterButton onClick={handleVerifyExit} variant="contained" color="secondary">
</PresenterButton> <BackspaceIcon fontSize="large" />
<PresenterButton onClick={() => history.push('/admin')} variant="contained" color="secondary"> </PresenterButton>
Avsluta tävling </Tooltip>
</PresenterButton>
<Dialog
fullScreen={fullScreen}
open={openAlert}
onClose={handleClose}
aria-labelledby="responsive-dialog-title"
>
<DialogTitle id="responsive-dialog-title">{'Vill du avsluta tävlingen?'}</DialogTitle>
<DialogContent>
<DialogContentText>
Genom att avsluta tävlingen kommer den avslutas för alla. Du kommer gå tillbaka till startsidan.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleClose} color="primary">
Avbryt
</Button>
<Button onClick={endCompetition} color="primary" autoFocus>
Avsluta tävling
</Button>
</DialogActions>
</Dialog>
<Typography variant="h3">{presentation.competition.name}</Typography>
<SlideCounter>
<Typography variant="h3">
{presentation.slide.order + 1} / {presentation.competition.slides.length}
</Typography>
</SlideCounter>
</PresenterHeader> </PresenterHeader>
<SlideDisplay /> <SlideDisplay />
<PresenterFooter> <PresenterFooter>
<PresenterButton onClick={handlePreviousSlidePressed} variant="contained"> <ToolBarContainer>
<ChevronRightIcon fontSize="large" /> <Tooltip title="Previous Slide" arrow>
</PresenterButton> <PresenterButton onClick={socketSetSlidePrev} variant="contained">
<SocketTest></SocketTest> <ChevronLeftIcon fontSize="large" />
<Timer></Timer> </PresenterButton>
<PresenterButton onClick={handleNextSlidePressed} variant="contained"> </Tooltip>
<ChevronRightIcon fontSize="large" />
</PresenterButton> {/*
// Manual start button
<Tooltip title="Start Presentation" arrow>
<PresenterButton onClick={startCompetition} variant="contained">
start
</PresenterButton>
</Tooltip>
// This creates a join button, but presenter should not join others, others should join presenter
<Tooltip title="Join Presentation" arrow>
<PresenterButton onClick={socketJoinPresentation} variant="contained">
<GroupAddIcon fontSize="large" />
</PresenterButton>
</Tooltip>
// This creates another end button, it might not be needed since we already have one
<Tooltip title="End Presentation" arrow>
<PresenterButton onClick={socketEndPresentation} variant="contained">
<CancelIcon fontSize="large" />
</PresenterButton>
</Tooltip>
*/}
<Tooltip title="Start Timer" arrow>
<PresenterButton onClick={socketStartTimer} variant="contained">
<TimerIcon fontSize="large" />
<Timer></Timer>
</PresenterButton>
</Tooltip>
<Tooltip title="Scoreboard" arrow>
<PresenterButton onClick={handleOpenPopover} variant="contained">
<AssignmentIcon fontSize="large" />
</PresenterButton>
</Tooltip>
<Tooltip title="Next Slide" arrow>
<PresenterButton onClick={socketSetSlideNext} variant="contained">
<ChevronRightIcon fontSize="large" />
</PresenterButton>
</Tooltip>
</ToolBarContainer>
</PresenterFooter> </PresenterFooter>
<Popover <Popover
open={Boolean(anchorEl)} open={Boolean(anchorEl)}
...@@ -71,6 +201,9 @@ const PresenterViewPage: React.FC = () => { ...@@ -71,6 +201,9 @@ const PresenterViewPage: React.FC = () => {
}} }}
> >
<List> <List>
{/** TODO:
* Fix scoreboard
*/}
{teams.map((team) => ( {teams.map((team) => (
<ListItem key={team.id}>{team.name} score: 20</ListItem> <ListItem key={team.id}>{team.name} score: 20</ListItem>
))} ))}
...@@ -81,3 +214,6 @@ const PresenterViewPage: React.FC = () => { ...@@ -81,3 +214,6 @@ const PresenterViewPage: React.FC = () => {
} }
export default PresenterViewPage export default PresenterViewPage
function componentDidMount() {
throw new Error('Function not implemented.')
}
...@@ -5,10 +5,15 @@ import { SlideContainer } from './styled' ...@@ -5,10 +5,15 @@ import { SlideContainer } from './styled'
const SlideDisplay: React.FC = () => { const SlideDisplay: React.FC = () => {
const currentSlide = useAppSelector((state) => state.presentation.slide) const currentSlide = useAppSelector((state) => state.presentation.slide)
return ( return (
<SlideContainer> <div>
<Typography variant="h3">{currentSlide.title}</Typography> <SlideContainer>
</SlideContainer> <Typography variant="h3">Slide Title: {currentSlide.title} </Typography>
<Typography variant="h3">Timer: {currentSlide.timer} </Typography>
<Typography variant="h3">Slide ID: {currentSlide.id} </Typography>
</SlideContainer>
</div>
) )
} }
......
...@@ -38,8 +38,7 @@ const Timer: React.FC = (props: any) => { ...@@ -38,8 +38,7 @@ const Timer: React.FC = (props: any) => {
return ( return (
<> <>
<div>Timer: {props.timer.value}</div> <div>{props.timer.value}</div>
<div>Enabled: {props.timer.enabled.toString()}</div>
</> </>
) )
} }
......
...@@ -3,7 +3,14 @@ import styled from 'styled-components' ...@@ -3,7 +3,14 @@ import styled from 'styled-components'
export const SlideContainer = styled.div` export const SlideContainer = styled.div`
display: flex; display: flex;
flex-direction: column;
margin-left: auto;
margin-right: auto;
margin-top: 5%;
justify-content: center; justify-content: center;
background-color: grey;
width: 1280px;
height: 720px;
` `
export const ScoreDisplayContainer = styled.div` export const ScoreDisplayContainer = styled.div`
......
...@@ -50,8 +50,15 @@ export const PresenterFooter = styled.div` ...@@ -50,8 +50,15 @@ export const PresenterFooter = styled.div`
export const PresenterButton = styled(Button)` export const PresenterButton = styled(Button)`
width: 100px; width: 100px;
height: 100px; height: 100px;
padding-top: 16px; margin-left: 16px;
padding-bottom: 16px; margin-right: 16px;
margin-top: 16px;
`
export const SlideCounter = styled(Button)`
margin-left: 16px;
margin-right: 16px;
margin-top: 16px;
` `
export const PresenterContainer = styled.div` export const PresenterContainer = styled.div`
...@@ -61,6 +68,18 @@ export const PresenterContainer = styled.div` ...@@ -61,6 +68,18 @@ export const PresenterContainer = styled.div`
height: 100%; height: 100%;
` `
export const ToolBarContainer = styled.div`
align-self: center;
display: flex;
flex-direction: row;
justify-content: space-between;
height: 100%;
width: auto;
margin-right: auto;
margin-left: auto;
margin-bottom: 20px;
`
interface DrawerProps { interface DrawerProps {
width: number width: number
} }
......
...@@ -38,22 +38,27 @@ export const socket_connect = () => { ...@@ -38,22 +38,27 @@ export const socket_connect = () => {
export const socketStartPresentation = () => { export const socketStartPresentation = () => {
socket.emit('start_presentation', { competition_id: store.getState().presentation.competition.id }) socket.emit('start_presentation', { competition_id: store.getState().presentation.competition.id })
console.log('START PRESENTATION')
} }
export const socketJoinPresentation = () => { export const socketJoinPresentation = () => {
socket.emit('join_presentation', { code: 'OEM1V4' }) // TODO: Send code gotten from auth/login/<code> api call socket.emit('join_presentation', { code: 'CO0ART' }) // TODO: Send code gotten from auth/login/<code> api call
console.log('JOIN PRESENTATION')
} }
export const socketEndPresentation = () => { export const socketEndPresentation = () => {
socket.emit('end_presentation', { competition_id: store.getState().presentation.competition.id }) socket.emit('end_presentation', { competition_id: store.getState().presentation.competition.id })
console.log('END PRESENTATION')
} }
export const socketSetSlideNext = () => { export const socketSetSlideNext = () => {
socketSetSlide(store.getState().presentation.slide.order + 1) // TODO: Check that this slide exists socketSetSlide(store.getState().presentation.slide.order + 1) // TODO: Check that this slide exists
console.log('NEXT SLIDE +1')
} }
export const socketSetSlidePrev = () => { export const socketSetSlidePrev = () => {
socketSetSlide(store.getState().presentation.slide.order - 1) // TODO: Check that this slide exists socketSetSlide(store.getState().presentation.slide.order - 1) // TODO: Check that this slide exists
console.log('PREVIOUS SLIDE -1')
} }
export const socketSetSlide = (slide_order: number) => { export const socketSetSlide = (slide_order: number) => {
...@@ -69,6 +74,7 @@ export const socketSetSlide = (slide_order: number) => { ...@@ -69,6 +74,7 @@ export const socketSetSlide = (slide_order: number) => {
} }
export const socketSetTimer = (timer: Timer) => { export const socketSetTimer = (timer: Timer) => {
console.log('SET TIMER')
socket.emit('set_timer', { socket.emit('set_timer', {
competition_id: store.getState().presentation.competition.id, competition_id: store.getState().presentation.competition.id,
timer: timer, timer: timer,
...@@ -76,5 +82,6 @@ export const socketSetTimer = (timer: Timer) => { ...@@ -76,5 +82,6 @@ export const socketSetTimer = (timer: Timer) => {
} }
export const socketStartTimer = () => { export const socketStartTimer = () => {
console.log('START TIMER')
socketSetTimer({ enabled: true, value: store.getState().presentation.timer.value }) socketSetTimer({ enabled: true, value: store.getState().presentation.timer.value })
} }
...@@ -37,7 +37,7 @@ class QuestionAlternatives(Resource): ...@@ -37,7 +37,7 @@ class QuestionAlternatives(Resource):
def put(self, CID, SOrder, QID, AID): def put(self, CID, SOrder, QID, AID):
args = question_alternative_parser.parse_args(strict=True) args = question_alternative_parser.parse_args(strict=True)
item = dbc.get.one(QuestionAlternative, AID) item = dbc.get.one(QuestionAlternative, AID)
item = dbc.edit.question_alternative(item, **args) item = dbc.edit.default(item, **args)
return item_response(schema.dump(item)) return item_response(schema.dump(item))
@check_jwt(editor=True) @check_jwt(editor=True)
......
...@@ -37,5 +37,5 @@ class QuestionAnswers(Resource): ...@@ -37,5 +37,5 @@ class QuestionAnswers(Resource):
def put(self, CID, TID, AID): def put(self, CID, TID, AID):
args = question_answer_edit_parser.parse_args(strict=True) args = question_answer_edit_parser.parse_args(strict=True)
item = dbc.get.one(QuestionAnswer, AID) item = dbc.get.one(QuestionAnswer, AID)
item = dbc.edit.question_answer(item, **args) item = dbc.edit.default(item, **args)
return item_response(schema.dump(item)) return item_response(schema.dump(item))
...@@ -42,7 +42,7 @@ class Competitions(Resource): ...@@ -42,7 +42,7 @@ class Competitions(Resource):
def put(self, CID): def put(self, CID):
args = competition_parser.parse_args(strict=True) args = competition_parser.parse_args(strict=True)
item = dbc.get.one(Competition, CID) item = dbc.get.one(Competition, CID)
item = dbc.edit.competition(item, **args) item = dbc.edit.default(item, **args)
return item_response(schema.dump(item)) return item_response(schema.dump(item))
......
...@@ -25,7 +25,7 @@ class ComponentByID(Resource): ...@@ -25,7 +25,7 @@ class ComponentByID(Resource):
def put(self, CID, SOrder, component_id): def put(self, CID, SOrder, component_id):
args = component_parser.parse_args() args = component_parser.parse_args()
item = dbc.get.one(Component, component_id) item = dbc.get.one(Component, component_id)
item = dbc.edit.component(item, **args) item = dbc.edit.default(item, **args)
return item_response(schema.dump(item)) return item_response(schema.dump(item))
@check_jwt(editor=True) @check_jwt(editor=True)
......
...@@ -48,7 +48,7 @@ class QuestionById(Resource): ...@@ -48,7 +48,7 @@ class QuestionById(Resource):
args = question_parser.parse_args(strict=True) args = question_parser.parse_args(strict=True)
item_question = dbc.get.question(CID, SID, QID) item_question = dbc.get.question(CID, SID, QID)
item_question = dbc.edit.question(item_question, **args) item_question = dbc.edit.default(item_question, **args)
return item_response(schema.dump(item_question)) return item_response(schema.dump(item_question))
......
...@@ -44,7 +44,7 @@ class Slides(Resource): ...@@ -44,7 +44,7 @@ class Slides(Resource):
timer = args.get("timer") timer = args.get("timer")
item_slide = dbc.get.slide(CID, SOrder) item_slide = dbc.get.slide(CID, SOrder)
item_slide = dbc.edit.slide(item_slide, title, timer) item_slide = dbc.edit.default(item_slide, title=title, timer=timer)
return item_response(schema.dump(item_slide)) return item_response(schema.dump(item_slide))
......
...@@ -53,5 +53,5 @@ class Teams(Resource): ...@@ -53,5 +53,5 @@ class Teams(Resource):
item_team = dbc.get.team(CID, TID) item_team = dbc.get.team(CID, TID)
item_team = dbc.edit.team(item_team, name=name, competition_id=CID) item_team = dbc.edit.default(item_team, name=name, competition_id=CID)
return item_response(schema.dump(item_team)) return item_response(schema.dump(item_team))
...@@ -19,7 +19,12 @@ def edit_user(item_user, args): ...@@ -19,7 +19,12 @@ def edit_user(item_user, args):
if User.query.filter(User.email == args["email"]).count() > 0: if User.query.filter(User.email == args["email"]).count() > 0:
api.abort(codes.BAD_REQUEST, "Email is already in use") api.abort(codes.BAD_REQUEST, "Email is already in use")
return dbc.edit.user(item_user, **args) try:
args["name"] = args.get("name").title()
except Exception:
pass
return dbc.edit.default(item_user, **args)
@api.route("/") @api.route("/")
......