From d488a39fed5732129f8adc7b12b9d77e077c8354 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Mod=C3=A9e?= <bjomo323@student.liu.se>
Date: Wed, 21 Apr 2021 13:31:06 +0000
Subject: [PATCH] Resolve "Competition view"

---
 .../admin/competitions/AddCompetition.tsx     |  24 +++-
 .../admin/competitions/CompetitionManager.tsx |  13 +-
 client/src/pages/views/AudienceViewPage.tsx   |   3 +-
 client/src/pages/views/JudgeViewPage.tsx      |  18 ++-
 client/src/pages/views/PresenterViewPage.tsx  | 127 ++++++++++++++----
 .../pages/views/components/SlideDisplay.tsx   |  17 ++-
 client/src/pages/views/components/Timer.tsx   |   3 +-
 client/src/pages/views/components/styled.tsx  |   7 +
 client/src/pages/views/styled.tsx             |  23 +++-
 client/src/sockets.ts                         |   9 +-
 10 files changed, 199 insertions(+), 45 deletions(-)

diff --git a/client/src/pages/admin/competitions/AddCompetition.tsx b/client/src/pages/admin/competitions/AddCompetition.tsx
index e9666770..0ce9a6ff 100644
--- a/client/src/pages/admin/competitions/AddCompetition.tsx
+++ b/client/src/pages/admin/competitions/AddCompetition.tsx
@@ -10,10 +10,17 @@ import { City } from '../../../interfaces/ApiModels'
 import { AddCompetitionModel, FormModel } from '../../../interfaces/FormModels'
 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>
 
 const noCitySelected = 'Välj stad'
 
+//Description of the form and what is required
 const competitionSchema: Yup.SchemaOf<formType> = Yup.object({
   model: Yup.object()
     .shape({
@@ -45,20 +52,25 @@ const AddCompetition: React.FC = (props: any) => {
   const dispatch = useAppDispatch()
   const id = open ? 'simple-popover' : undefined
   const currentYear = new Date().getFullYear()
+
+  // Handles the actual submition to the database
   const handleCompetitionSubmit = async (values: formType, actions: FormikHelpers<formType>) => {
+    // The parameters sent
     const params = {
       name: values.model.name,
       year: values.model.year,
       city_id: selectedCity?.id as number,
     }
+
     await axios
-      .post('/competitions', params)
+      .post('/competitions', params) // send to database
       .then(() => {
-        actions.resetForm()
+        actions.resetForm() // reset the form
         setAnchorEl(null)
-        dispatch(getCompetitions())
+        dispatch(getCompetitions()) // refresh competitions
         setSelectedCity(undefined)
       })
+      // if the post request fails
       .catch(({ response }) => {
         console.warn(response.data)
         if (response.data && response.data.message)
@@ -83,6 +95,12 @@ const AddCompetition: React.FC = (props: any) => {
       >
         Ny Tävling
       </AddButton>
+
+      {/**
+       *  The "pop up" menu for adding a competition
+       *  contains 3 fields; Name, Region and Year
+       *
+       */}
       <Popover
         id={id}
         open={open}
diff --git a/client/src/pages/admin/competitions/CompetitionManager.tsx b/client/src/pages/admin/competitions/CompetitionManager.tsx
index 81e644ad..e7085a5e 100644
--- a/client/src/pages/admin/competitions/CompetitionManager.tsx
+++ b/client/src/pages/admin/competitions/CompetitionManager.tsx
@@ -21,6 +21,13 @@ import { CompetitionFilterParams } from '../../../interfaces/FilterParams'
 import { FilterContainer, RemoveMenuItem, TopBar, YearFilterTextField } from '../styledComp'
 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) =>
   createStyles({
     table: {
@@ -41,6 +48,7 @@ const CompetitionManager: React.FC = (props: any) => {
   const filterParams = useAppSelector((state) => state.competitions.filterParams)
   const competitionTotal = useAppSelector((state) => state.competitions.total)
   const cities = useAppSelector((state) => state.cities.cities)
+
   const classes = useStyles()
   const noFilterText = 'Alla'
   const dispatch = useAppDispatch()
@@ -59,6 +67,7 @@ const CompetitionManager: React.FC = (props: any) => {
     dispatch(getCompetitions())
   }, [])
 
+  // Search funtion to search for a specific string
   const onSearchChange = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
     if (timerHandle) {
       clearTimeout(timerHandle)
@@ -69,13 +78,14 @@ const CompetitionManager: React.FC = (props: any) => {
     dispatch(setFilterParams({ ...filterParams, name: event.target.value }))
   }
 
+  // Function to remove a competition from the systems database
   const handleDeleteCompetition = async () => {
     if (activeId) {
       await axios
         .delete(`/competitions/${activeId}`)
         .then(() => {
           setAnchorEl(null)
-          dispatch(getCompetitions())
+          dispatch(getCompetitions()) // refresh the competition list
         })
         .catch(({ response }) => {
           console.warn(response.data)
@@ -177,6 +187,7 @@ const CompetitionManager: React.FC = (props: any) => {
               ))}
           </TableBody>
         </Table>
+        {/** We can't find any competitions at all or with a specific filter */}
         {(!competitions || competitions.length === 0) && (
           <Typography>Inga tävlingar hittades med nuvarande filter</Typography>
         )}
diff --git a/client/src/pages/views/AudienceViewPage.tsx b/client/src/pages/views/AudienceViewPage.tsx
index 45ec132d..00a821f3 100644
--- a/client/src/pages/views/AudienceViewPage.tsx
+++ b/client/src/pages/views/AudienceViewPage.tsx
@@ -1,7 +1,8 @@
 import React from 'react'
+import SlideDisplay from './components/SlideDisplay'
 
 const AudienceViewPage: React.FC = () => {
-  return <div>Publik</div>
+  return <SlideDisplay />
 }
 
 export default AudienceViewPage
diff --git a/client/src/pages/views/JudgeViewPage.tsx b/client/src/pages/views/JudgeViewPage.tsx
index 12e866a0..60018624 100644
--- a/client/src/pages/views/JudgeViewPage.tsx
+++ b/client/src/pages/views/JudgeViewPage.tsx
@@ -1,4 +1,4 @@
-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 React, { useEffect, useState } from 'react'
 import { useParams } from 'react-router-dom'
@@ -10,6 +10,7 @@ import {
 } from '../../actions/presentation'
 import { useAppDispatch, useAppSelector } from '../../hooks'
 import { ViewParams } from '../../interfaces/ViewParams'
+import { socket_connect } from '../../sockets'
 import { SlideListItem } from '../presentationEditor/styled'
 import JudgeScoreDisplay from './components/JudgeScoreDisplay'
 import SlideDisplay from './components/SlideDisplay'
@@ -43,17 +44,20 @@ const JudgeViewPage: React.FC = () => {
   const { id, code }: ViewParams = useParams()
   const dispatch = useAppDispatch()
   const [activeSlideIndex, setActiveSlideIndex] = useState<number>(0)
-  useEffect(() => {
-    dispatch(getPresentationCompetition(id))
-    dispatch(getPresentationTeams(id))
-    dispatch(setPresentationCode(code))
-  }, [])
   const teams = useAppSelector((state) => state.presentation.teams)
   const slides = useAppSelector((state) => state.presentation.competition.slides)
   const handleSelectSlide = (index: number) => {
     setActiveSlideIndex(index)
     dispatch(setCurrentSlide(slides[index]))
   }
+
+  useEffect(() => {
+    socket_connect()
+    dispatch(getPresentationCompetition(id))
+    dispatch(getPresentationTeams(id))
+    dispatch(setPresentationCode(code))
+  }, [])
+
   return (
     <div>
       <JudgeAppBar position="fixed">
@@ -80,6 +84,7 @@ const JudgeViewPage: React.FC = () => {
               button
               key={slide.id}
             >
+              <Typography variant="h6">Slide ID: {slide.id} </Typography>
               <ListItemText primary={slide.title} />
             </SlideListItem>
           ))}
@@ -103,7 +108,6 @@ const JudgeViewPage: React.FC = () => {
           ))}
         </List>
       </RightDrawer>
-      aaa
       <Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}>
         <div className={classes.toolbar} />
         <SlideDisplay />
diff --git a/client/src/pages/views/PresenterViewPage.tsx b/client/src/pages/views/PresenterViewPage.tsx
index 22a672ba..ff61343f 100644
--- a/client/src/pages/views/PresenterViewPage.tsx
+++ b/client/src/pages/views/PresenterViewPage.tsx
@@ -1,61 +1,139 @@
-import { List, ListItem, Popover } from '@material-ui/core'
+import { List, ListItem, Popover, Tooltip, Typography } 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 PlayArrowIcon from '@material-ui/icons/PlayArrow'
+import TimerIcon from '@material-ui/icons/Timer'
 import React, { useEffect } from 'react'
 import { useHistory, useParams } from 'react-router-dom'
 import { getPresentationCompetition, getPresentationTeams, setPresentationCode } from '../../actions/presentation'
 import { useAppDispatch, useAppSelector } from '../../hooks'
 import { ViewParams } from '../../interfaces/ViewParams'
+import {
+  socketEndPresentation,
+  socketSetSlide,
+  socketSetSlideNext,
+  socketSetSlidePrev,
+  socketStartPresentation,
+  socketStartTimer,
+  socket_connect,
+} from '../../sockets'
 import SlideDisplay from './components/SlideDisplay'
-import SocketTest from './components/SocketTest'
 import Timer from './components/Timer'
-import { PresenterButton, PresenterContainer, PresenterFooter, PresenterHeader } from './styled'
+import {
+  PresenterButton,
+  PresenterContainer,
+  PresenterFooter,
+  PresenterHeader,
+  SlideCounter,
+  ToolBarContainer,
+} from './styled'
 
 const PresenterViewPage: React.FC = () => {
   const teams = useAppSelector((state) => state.presentation.teams)
   const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null)
   const { id, code }: ViewParams = useParams()
+  const presentation = useAppSelector((state) => state.presentation)
   const history = useHistory()
   const dispatch = useAppDispatch()
+
   useEffect(() => {
+    socket_connect()
+    socketSetSlide
     dispatch(getPresentationCompetition(id))
     dispatch(getPresentationTeams(id))
     dispatch(setPresentationCode(code))
   }, [])
+
   const handleOpenPopover = (event: React.MouseEvent<HTMLButtonElement>) => {
     setAnchorEl(event.currentTarget)
   }
   const handleClose = () => {
     setAnchorEl(null)
   }
-  const handleNextSlidePressed = () => {
-    // dispatch(setCurrentSlideNext())
-    // syncSlide()
+
+  const startCompetition = () => {
+    socketStartPresentation()
+    const haveStarted = true
+    console.log('You have started the competition! GLHF!')
+    console.log(haveStarted)
   }
-  const handlePreviousSlidePressed = () => {
-    // dispatch(setCurrentSlidePrevious())
-    // syncSlide()
+
+  const endCompetition = () => {
+    if (confirm('Är du säker på att du vill avsluta tävlingen för alla?')) {
+      const haveStarted = false
+      socketEndPresentation()
+      history.push('/admin')
+      window.location.reload(false) // TODO: fix this ugly hack, we "need" to refresh site to be able to run the competition correctly again
+    }
   }
 
   return (
     <PresenterContainer>
       <PresenterHeader>
-        <PresenterButton onClick={handleOpenPopover} color="primary" variant="contained">
-          Visa ställning
-        </PresenterButton>
-        <PresenterButton onClick={() => history.push('/admin')} variant="contained" color="secondary">
-          Avsluta tävling
-        </PresenterButton>
+        <Tooltip title="Avsluta tävling" arrow>
+          <PresenterButton onClick={endCompetition} variant="contained" color="secondary">
+            <BackspaceIcon fontSize="large" />
+          </PresenterButton>
+        </Tooltip>
+        <SlideCounter>
+          <Typography variant="h3">
+            {presentation.slide.id} / {presentation.competition.slides.length}
+          </Typography>
+        </SlideCounter>
       </PresenterHeader>
       <SlideDisplay />
       <PresenterFooter>
-        <PresenterButton onClick={handlePreviousSlidePressed} variant="contained">
-          <ChevronRightIcon fontSize="large" />
-        </PresenterButton>
-        <SocketTest></SocketTest>
-        <Timer></Timer>
-        <PresenterButton onClick={handleNextSlidePressed} variant="contained">
-          <ChevronRightIcon fontSize="large" />
-        </PresenterButton>
+        <ToolBarContainer>
+          <Tooltip title="Previous Slide" arrow>
+            <PresenterButton onClick={socketSetSlidePrev} variant="contained">
+              <ChevronLeftIcon fontSize="large" />
+            </PresenterButton>
+          </Tooltip>
+
+          <Tooltip title="Start Presentation" arrow>
+            <PresenterButton onClick={startCompetition} variant="contained">
+              <PlayArrowIcon fontSize="large" />
+            </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>
       <Popover
         open={Boolean(anchorEl)}
@@ -71,6 +149,9 @@ const PresenterViewPage: React.FC = () => {
         }}
       >
         <List>
+          {/**  TODO:
+           *    Fix scoreboard
+           */}
           {teams.map((team) => (
             <ListItem key={team.id}>{team.name} score: 20</ListItem>
           ))}
diff --git a/client/src/pages/views/components/SlideDisplay.tsx b/client/src/pages/views/components/SlideDisplay.tsx
index 1c10f9cf..b7e6dcbe 100644
--- a/client/src/pages/views/components/SlideDisplay.tsx
+++ b/client/src/pages/views/components/SlideDisplay.tsx
@@ -1,14 +1,21 @@
 import { Typography } from '@material-ui/core'
-import React from 'react'
-import { useAppSelector } from '../../../hooks'
+import React, { useEffect } from 'react'
+import { useAppDispatch, useAppSelector } from '../../../hooks'
 import { SlideContainer } from './styled'
 
 const SlideDisplay: React.FC = () => {
   const currentSlide = useAppSelector((state) => state.presentation.slide)
+  const dispatch = useAppDispatch()
+  useEffect(() => {}, [])
+
   return (
-    <SlideContainer>
-      <Typography variant="h3">{currentSlide.title}</Typography>
-    </SlideContainer>
+    <div>
+      <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>
   )
 }
 
diff --git a/client/src/pages/views/components/Timer.tsx b/client/src/pages/views/components/Timer.tsx
index b4401a69..0cbd1fdf 100644
--- a/client/src/pages/views/components/Timer.tsx
+++ b/client/src/pages/views/components/Timer.tsx
@@ -38,8 +38,7 @@ const Timer: React.FC = (props: any) => {
 
   return (
     <>
-      <div>Timer: {props.timer.value}</div>
-      <div>Enabled: {props.timer.enabled.toString()}</div>
+      <div>{props.timer.value}</div>
     </>
   )
 }
diff --git a/client/src/pages/views/components/styled.tsx b/client/src/pages/views/components/styled.tsx
index 0034c39b..b522b20d 100644
--- a/client/src/pages/views/components/styled.tsx
+++ b/client/src/pages/views/components/styled.tsx
@@ -3,7 +3,14 @@ import styled from 'styled-components'
 
 export const SlideContainer = styled.div`
   display: flex;
+  flex-direction: column;
+  margin-left: auto;
+  margin-right: auto;
+  margin-top: 5%;
   justify-content: center;
+  background-color: grey;
+  width: 1280px;
+  height: 720px;
 `
 
 export const ScoreDisplayContainer = styled.div`
diff --git a/client/src/pages/views/styled.tsx b/client/src/pages/views/styled.tsx
index d4a9cf9e..1f3a61c6 100644
--- a/client/src/pages/views/styled.tsx
+++ b/client/src/pages/views/styled.tsx
@@ -50,8 +50,15 @@ export const PresenterFooter = styled.div`
 export const PresenterButton = styled(Button)`
   width: 100px;
   height: 100px;
-  padding-top: 16px;
-  padding-bottom: 16px;
+  margin-left: 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`
@@ -61,6 +68,18 @@ export const PresenterContainer = styled.div`
   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 {
   width: number
 }
diff --git a/client/src/sockets.ts b/client/src/sockets.ts
index 874bfc46..54f84c39 100644
--- a/client/src/sockets.ts
+++ b/client/src/sockets.ts
@@ -38,22 +38,27 @@ export const socket_connect = () => {
 
 export const socketStartPresentation = () => {
   socket.emit('start_presentation', { competition_id: store.getState().presentation.competition.id })
+  console.log('START PRESENTATION')
 }
 
 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 = () => {
   socket.emit('end_presentation', { competition_id: store.getState().presentation.competition.id })
+  console.log('END PRESENTATION')
 }
 
 export const socketSetSlideNext = () => {
   socketSetSlide(store.getState().presentation.slide.order + 1) // TODO: Check that this slide exists
+  console.log('NEXT SLIDE +1')
 }
 
 export const socketSetSlidePrev = () => {
   socketSetSlide(store.getState().presentation.slide.order - 1) // TODO: Check that this slide exists
+  console.log('PREVIOUS SLIDE -1')
 }
 
 export const socketSetSlide = (slide_order: number) => {
@@ -69,6 +74,7 @@ export const socketSetSlide = (slide_order: number) => {
 }
 
 export const socketSetTimer = (timer: Timer) => {
+  console.log('SET TIMER')
   socket.emit('set_timer', {
     competition_id: store.getState().presentation.competition.id,
     timer: timer,
@@ -76,5 +82,6 @@ export const socketSetTimer = (timer: Timer) => {
 }
 
 export const socketStartTimer = () => {
+  console.log('START TIMER')
   socketSetTimer({ enabled: true, value: store.getState().presentation.timer.value })
 }
-- 
GitLab