diff --git a/client/src/Main.tsx b/client/src/Main.tsx
index b7c74b556e3e072f8da96d328617d7461977b4c9..b1f0675d7129f755eb70ff9506c4c145a9b5a83c 100644
--- a/client/src/Main.tsx
+++ b/client/src/Main.tsx
@@ -7,8 +7,8 @@ import LoginPage from './pages/login/LoginPage'
 import PresentationEditorPage from './pages/presentationEditor/PresentationEditorPage'
 import AudienceViewPage from './pages/views/AudienceViewPage'
 import JudgeViewPage from './pages/views/JudgeViewPage'
-import ParticipantViewPage from './pages/views/ParticipantViewPage'
-import PresenterViewPage from './pages/views/PresenterViewPage'
+import TeamViewPage from './pages/views/TeamViewPage'
+import OperatorViewPage from './pages/views/OperatorViewPage'
 import ViewSelectPage from './pages/views/ViewSelectPage'
 import SecureRoute from './utils/SecureRoute'
 
@@ -22,10 +22,10 @@ const Main: React.FC = () => {
       <Switch>
         <SecureRoute login exact path="/" component={LoginPage} />
         <SecureRoute path="/admin" component={AdminPage} />
-        <SecureRoute path="/editor/competition-id=:id" component={PresentationEditorPage} />
+        <SecureRoute path="/editor/competition-id=:competitionId" component={PresentationEditorPage} />
         <Route exact path="/:code" component={ViewSelectPage} />
-        <Route exact path="/participant/id=:id&code=:code" component={ParticipantViewPage} />
-        <SecureRoute exact path="/presenter/id=:id&code=:code" component={PresenterViewPage} />
+        <Route exact path="/team/id=:id&code=:code" component={TeamViewPage} />
+        <SecureRoute exact path="/operator/id=:id&code=:code" component={OperatorViewPage} />
         <Route exact path="/judge/id=:id&code=:code" component={JudgeViewPage} />
         <Route exact path="/audience/id=:id&code=:code" component={AudienceViewPage} />
       </Switch>
diff --git a/client/src/actions/editor.ts b/client/src/actions/editor.ts
index db688dcb59f3732c6bda801b0aada1b3063a1238..526ad9ff6ae1698644e32ea02806c7a6e8e2cd78 100644
--- a/client/src/actions/editor.ts
+++ b/client/src/actions/editor.ts
@@ -18,16 +18,28 @@ export const getEditorCompetition = (id: string) => async (dispatch: AppDispatch
       if (getState().editor.activeSlideId === -1 && res.data.slides[0]) {
         setEditorSlideId(res.data.slides[0].id)(dispatch)
       }
+      const defaultViewType = getState().types.viewTypes.find((viewType) => viewType.name === 'Audience')
+      if (getState().editor.activeViewTypeId === -1 && defaultViewType) {
+        setEditorViewId(defaultViewType.id)(dispatch)
+      }
     })
     .catch((err) => {
       console.log(err)
     })
 }
 
-// Set currentSlideId in editor state
+// Set activeSlideId in editor state
 export const setEditorSlideId = (id: number) => (dispatch: AppDispatch) => {
   dispatch({
     type: Types.SET_EDITOR_SLIDE_ID,
     payload: id,
   })
 }
+
+// Set activeViewTypeId in editor state
+export const setEditorViewId = (id: number) => (dispatch: AppDispatch) => {
+  dispatch({
+    type: Types.SET_EDITOR_VIEW_ID,
+    payload: id,
+  })
+}
diff --git a/client/src/actions/presentation.test.ts b/client/src/actions/presentation.test.ts
index 53ea4847dcf1aaf3e1ba9a04c643d1d44b5f27f0..d95d9db767b214e4e2583a1a903ed06b226eec16 100644
--- a/client/src/actions/presentation.test.ts
+++ b/client/src/actions/presentation.test.ts
@@ -2,10 +2,9 @@ import mockedAxios from 'axios'
 import expect from 'expect' // You can use any testing library
 import configureMockStore from 'redux-mock-store'
 import thunk from 'redux-thunk'
-import { Slide } from '../interfaces/Slide'
+import { Slide } from '../interfaces/ApiModels'
 import {
   getPresentationCompetition,
-  getPresentationTeams,
   setCurrentSlide,
   setCurrentSlideNext,
   setCurrentSlidePrevious,
@@ -26,18 +25,8 @@ it('dispatches no actions when failing to get competitions', async () => {
   expect(console.log).toHaveBeenCalled()
 })
 
-it('dispatches no actions when failing to get teams', async () => {
-  console.log = jest.fn()
-  ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => {
-    return Promise.reject(new Error('getting teams failed'))
-  })
-  const store = mockStore({ competitions: { filterParams: [] } })
-  await getPresentationTeams('0')(store.dispatch)
-  expect(store.getActions()).toEqual([])
-  expect(console.log).toHaveBeenCalled()
-})
 it('dispatches correct actions when setting slide', () => {
-  const testSlide: Slide = { competition_id: 0, id: 5, order: 5, timer: 20, title: '' }
+  const testSlide: Slide = { competition_id: 0, id: 5, order: 5, timer: 20, title: '', background_image_id: 0 }
   const expectedActions = [{ type: Types.SET_PRESENTATION_SLIDE, payload: testSlide }]
   const store = mockStore({})
   setCurrentSlide(testSlide)(store.dispatch)
diff --git a/client/src/actions/presentation.ts b/client/src/actions/presentation.ts
index 9e7881d86239b1d1225a983a86ac73034d609a35..90b728c54aa18a483ed6ab000f718c407b46abfd 100644
--- a/client/src/actions/presentation.ts
+++ b/client/src/actions/presentation.ts
@@ -5,11 +5,11 @@ This file handles actions for the presentation redux state
 import axios from 'axios'
 import { Slide } from '../interfaces/ApiModels'
 import { Timer } from '../interfaces/Timer'
-import store, { AppDispatch } from './../store'
+import store, { AppDispatch, RootState } from './../store'
 import Types from './types'
 
 // Save competition in presentation state from input id
-export const getPresentationCompetition = (id: string) => async (dispatch: AppDispatch) => {
+export const getPresentationCompetition = (id: string) => async (dispatch: AppDispatch, getState: () => RootState) => {
   await axios
     .get(`/api/competitions/${id}`)
     .then((res) => {
@@ -17,21 +17,9 @@ export const getPresentationCompetition = (id: string) => async (dispatch: AppDi
         type: Types.SET_PRESENTATION_COMPETITION,
         payload: res.data,
       })
-    })
-    .catch((err) => {
-      console.log(err)
-    })
-}
-
-// Get all teams from current presentation competition
-export const getPresentationTeams = (id: string) => async (dispatch: AppDispatch) => {
-  await axios
-    .get(`/api/competitions/${id}/teams`)
-    .then((res) => {
-      dispatch({
-        type: Types.SET_PRESENTATION_TEAMS,
-        payload: res.data.items,
-      })
+      if (getState().presentation.slide.id === -1 && res.data.slides[0]) {
+        setCurrentSlideByOrder(0)(dispatch)
+      }
     })
     .catch((err) => {
       console.log(err)
diff --git a/client/src/actions/types.ts b/client/src/actions/types.ts
index 2e529a8839fd374504a8d2a0e9d79939cdc3ec09..2e9fc776f4c1828427df8424ef93d9588f20afea 100644
--- a/client/src/actions/types.ts
+++ b/client/src/actions/types.ts
@@ -24,12 +24,12 @@ export default {
   SET_COMPETITIONS_COUNT: 'SET_COMPETITIONS_COUNT',
   SET_EDITOR_COMPETITION: 'SET_EDITOR_COMPETITION',
   SET_EDITOR_SLIDE_ID: 'SET_EDITOR_SLIDE_ID',
+  SET_EDITOR_VIEW_ID: 'SET_EDITOR_VIEW_ID',
   SET_PRESENTATION_COMPETITION: 'SET_PRESENTATION_COMPETITION',
   SET_PRESENTATION_SLIDE: 'SET_PRESENTATION_SLIDE',
   SET_PRESENTATION_SLIDE_PREVIOUS: 'SET_PRESENTATION_SLIDE_PREVIOUS',
   SET_PRESENTATION_SLIDE_NEXT: 'SET_PRESENTATION_SLIDE_NEXT',
   SET_PRESENTATION_SLIDE_BY_ORDER: 'SET_PRESENTATION_SLIDE_BY_ORDER',
-  SET_PRESENTATION_TEAMS: 'SET_PRESENTATION_TEAMS',
   SET_PRESENTATION_CODE: 'SET_PRESENTATION_CODE',
   SET_PRESENTATION_TIMER: 'SET_PRESENTATION_TIMER',
   SET_CITIES: 'SET_CITIES',
diff --git a/client/src/interfaces/ApiModels.ts b/client/src/interfaces/ApiModels.ts
index 73fe6db65f1b733abd4d0c7c7e81f1e74f907e19..a6fa68a0e9b58dcc63ad67d29884bbf3e781a64a 100644
--- a/client/src/interfaces/ApiModels.ts
+++ b/client/src/interfaces/ApiModels.ts
@@ -38,13 +38,14 @@ export interface Slide {
   order: number
   timer: number
   title: string
+  background_image?: Media
 }
 
 export interface Competition extends NameID {
   font: string
   city_id: number
   year: number
-  background_image_id: number
+  background_image?: Media
 }
 
 export interface Team extends NameID {
@@ -53,7 +54,6 @@ export interface Team extends NameID {
 
 export interface Question extends NameID {
   slide_id: number
-  title: string
   total_score: number
   type_id: number
 }
@@ -69,7 +69,7 @@ export interface QuestionAnswer {
   id: number
   question_id: number
   team_id: number
-  data: string
+  answer: string
   score: number
 }
 
@@ -80,11 +80,12 @@ export interface Component {
   w: number
   h: number
   type_id: number
+  view_type_id: number
+  slide_id: number
 }
 
 export interface ImageComponent extends Component {
-  media_id: number
-  filename: string
+  media: Media
 }
 
 export interface TextComponent extends Component {
@@ -93,11 +94,5 @@ export interface TextComponent extends Component {
 }
 
 export interface QuestionAlternativeComponent extends Component {
-  data: {
-    question_id: number
-    text: string
-    value: number
-    question_alternative_id: number
-    font: string
-  }
+  question_id: number
 }
diff --git a/client/src/interfaces/ApiRichModels.ts b/client/src/interfaces/ApiRichModels.ts
index 2388f8308ba8bfa38d4d5376040f937244f4cbd5..b9ca9f333640071518a9f216e08a77af670eb153 100644
--- a/client/src/interfaces/ApiRichModels.ts
+++ b/client/src/interfaces/ApiRichModels.ts
@@ -7,6 +7,7 @@ export interface RichCompetition {
   city_id: number
   slides: RichSlide[]
   teams: RichTeam[]
+  background_image?: Media
 }
 
 export interface RichSlide {
@@ -15,9 +16,9 @@ export interface RichSlide {
   timer: number
   title: string
   competition_id: number
+  background_image?: Media
   components: Component[]
   questions: RichQuestion[]
-  medias: Media[]
 }
 
 export interface RichTeam {
diff --git a/client/src/pages/admin/competitions/CompetitionManager.tsx b/client/src/pages/admin/competitions/CompetitionManager.tsx
index 95b7476610e4c63ca9e4897d96dbf33d92a79002..35dbd3b6fd13e7de27bda7067e163bf3e2de834b 100644
--- a/client/src/pages/admin/competitions/CompetitionManager.tsx
+++ b/client/src/pages/admin/competitions/CompetitionManager.tsx
@@ -129,15 +129,15 @@ const CompetitionManager: React.FC = (props: any) => {
   }
 
   const handleStartCompetition = () => {
-    history.push(`/presenter/id=${activeId}&code=123123`)
+    history.push(`/operator/id=${activeId}&code=123123`)
   }
 
   const getCodes = async () => {
     await axios
       .get(`/api/competitions/${activeId}/codes`)
       .then((response) => {
-        console.log(response.data[0])
-        setCodes(response.data[0].items)
+        console.log(response.data)
+        setCodes(response.data.items)
       })
       .catch(console.log)
   }
diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx
index c645724e3c7fabda69a2ad2434f4bd7b587c8c2e..5426152ff33783745f8dc1181237cd2926b0a16a 100644
--- a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx
+++ b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx
@@ -27,8 +27,19 @@ it('renders presentation editor', () => {
       ],
     },
   }
+  const typesRes: any = {
+    data: {
+      view_types: [
+        {
+          name: '',
+          id: 0,
+        },
+      ],
+    },
+  }
   ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => {
     if (path.startsWith('/api/competitions')) return Promise.resolve(competitionRes)
+    if (path.startsWith('/api/misc/types')) return Promise.resolve(typesRes)
     return Promise.resolve(citiesRes)
   })
   render(
diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
index da18cd52fd6222b4a37c7a88bc111f0c4974ecc9..6d9bc11905e9cca28526bd27ceb0921648ef5ed7 100644
--- a/client/src/pages/presentationEditor/PresentationEditorPage.tsx
+++ b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
@@ -1,36 +1,37 @@
-import { Button, Checkbox, CircularProgress, Divider, Menu, MenuItem, Typography } from '@material-ui/core'
-import AppBar from '@material-ui/core/AppBar'
-import { CheckboxProps } from '@material-ui/core/Checkbox'
+import { Button, CircularProgress, Divider, Menu, MenuItem, Typography } from '@material-ui/core'
 import CssBaseline from '@material-ui/core/CssBaseline'
-import Drawer from '@material-ui/core/Drawer'
 import ListItemText from '@material-ui/core/ListItemText'
-import { createStyles, makeStyles, Theme, withStyles } from '@material-ui/core/styles'
 import AddOutlinedIcon from '@material-ui/icons/AddOutlined'
-import BuildOutlinedIcon from '@material-ui/icons/BuildOutlined'
-import CreateOutlinedIcon from '@material-ui/icons/CreateOutlined'
-import DnsOutlinedIcon from '@material-ui/icons/DnsOutlined'
-import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined'
 import axios from 'axios'
 import React, { useEffect, useState } from 'react'
 import { Link, useParams } from 'react-router-dom'
 import { getCities } from '../../actions/cities'
-import { getEditorCompetition, setEditorSlideId } from '../../actions/editor'
+import { getEditorCompetition, setEditorSlideId, setEditorViewId } from '../../actions/editor'
 import { getTypes } from '../../actions/typesAction'
 import { useAppDispatch, useAppSelector } from '../../hooks'
-import { RichSlide } from '../../interfaces/ApiRichModels'
+import { renderSlideIcon } from '../../utils/renderSlideIcon'
 import { RemoveMenuItem } from '../admin/styledComp'
-import { Content } from '../views/styled'
+import { Content, InnerContent } from '../views/styled'
 import SettingsPanel from './components/SettingsPanel'
-import SlideEditor from './components/SlideEditor'
+import SlideDisplay from './components/SlideDisplay'
 import {
+  AppBarEditor,
   CenteredSpinnerContainer,
   HomeIcon,
+  LeftDrawer,
+  RightDrawer,
   PresentationEditorContainer,
   SlideList,
   SlideListItem,
   ToolBarContainer,
   ViewButton,
   ViewButtonGroup,
+  ToolbarMargin,
+  FillLeftContainer,
+  PositionBottom,
+  FillRightContainer,
+  CompetitionName,
+  RightPanelScroll,
 } from './styled'
 
 const initialState = {
@@ -42,61 +43,21 @@ const initialState = {
 const leftDrawerWidth = 150
 const rightDrawerWidth = 390
 
-const useStyles = makeStyles((theme: Theme) =>
-  createStyles({
-    appBar: {
-      width: `calc(100% - ${rightDrawerWidth}px)`,
-      marginLeft: leftDrawerWidth,
-      marginRight: rightDrawerWidth,
-    },
-    leftDrawer: {
-      width: leftDrawerWidth,
-      flexShrink: 0,
-      position: 'relative',
-      zIndex: 1,
-    },
-    rightDrawer: {
-      width: rightDrawerWidth,
-      flexShrink: 0,
-    },
-    leftDrawerPaper: {
-      width: leftDrawerWidth,
-    },
-    rightDrawerPaper: {
-      width: rightDrawerWidth,
-      background: '#EAEAEA',
-    },
-    // necessary for content to be below app bar
-    toolbar: theme.mixins.toolbar,
-    content: {
-      flexGrow: 1,
-      backgroundColor: theme.palette.background.default,
-      padding: theme.spacing(3),
-    },
-    alignCheckboxText: {
-      display: 'flex',
-      justifyContent: 'center',
-      alignItems: 'center',
-      paddingRight: 20,
-    },
-  })
-)
-
 interface CompetitionParams {
-  id: string
+  competitionId: string
 }
 
 const PresentationEditorPage: React.FC = () => {
-  const classes = useStyles()
-  const { id }: CompetitionParams = useParams()
+  const { competitionId }: CompetitionParams = useParams()
   const dispatch = useAppDispatch()
   const activeSlideId = useAppSelector((state) => state.editor.activeSlideId)
+  const activeViewTypeId = useAppSelector((state) => state.editor.activeViewTypeId)
   const competition = useAppSelector((state) => state.editor.competition)
   const competitionLoading = useAppSelector((state) => state.editor.loading)
   useEffect(() => {
-    dispatch(getEditorCompetition(id))
-    dispatch(getCities())
     dispatch(getTypes())
+    dispatch(getEditorCompetition(competitionId))
+    dispatch(getCities())
   }, [])
 
   const setActiveSlideId = (id: number) => {
@@ -104,8 +65,8 @@ const PresentationEditorPage: React.FC = () => {
   }
 
   const createNewSlide = async () => {
-    await axios.post(`/api/competitions/${id}/slides`, { title: 'new slide' })
-    dispatch(getEditorCompetition(id))
+    await axios.post(`/api/competitions/${competitionId}/slides`, { title: 'Ny sida' })
+    dispatch(getEditorCompetition(competitionId))
   }
 
   const [contextState, setContextState] = React.useState<{
@@ -128,84 +89,63 @@ const PresentationEditorPage: React.FC = () => {
   }
 
   const handleRemoveSlide = async () => {
-    await axios.delete(`/api/competitions/${id}/slides/${contextState.slideId}`)
-    dispatch(getEditorCompetition(id))
+    await axios.delete(`/api/competitions/${competitionId}/slides/${contextState.slideId}`)
+    dispatch(getEditorCompetition(competitionId))
     setContextState(initialState)
   }
 
   const handleDuplicateSlide = async () => {
-    await axios.post(`/api/competitions/${id}/slides/${contextState.slideId}/copy`)
-    dispatch(getEditorCompetition(id))
+    await axios.post(`/api/competitions/${competitionId}/slides/${contextState.slideId}/copy`)
+    dispatch(getEditorCompetition(competitionId))
     setContextState(initialState)
   }
 
-  const renderSlideIcon = (slide: RichSlide) => {
-    if (slide.questions && slide.questions[0] && slide.questions[0].type_id) {
-      switch (slide.questions[0].type_id) {
-        case 1:
-          return <CreateOutlinedIcon /> // text question
-        case 2:
-          return <BuildOutlinedIcon /> // practical qustion
-        case 3:
-          return <DnsOutlinedIcon /> // multiple choice question
-      }
-    } else {
-      return <InfoOutlinedIcon /> // information slide
+  const viewTypes = useAppSelector((state) => state.types.viewTypes)
+  const [activeViewTypeName, setActiveViewTypeName] = useState('')
+  const changeView = (clickedViewTypeName: string) => {
+    setActiveViewTypeName(clickedViewTypeName)
+    const clickedViewTypeId = viewTypes.find((viewType) => viewType.name === clickedViewTypeName)?.id
+    if (clickedViewTypeId) {
+      dispatch(setEditorViewId(clickedViewTypeId))
     }
   }
 
-  const GreenCheckbox = withStyles({
-    root: {
-      color: '#FFFFFF',
-      '&$checked': {
-        color: '#FFFFFF',
-      },
-    },
-    checked: {},
-  })((props: CheckboxProps) => <Checkbox color="default" {...props} />)
-  const [checkbox, setCheckbox] = useState(false)
-
   return (
     <PresentationEditorContainer>
       <CssBaseline />
-      <AppBar position="fixed" className={classes.appBar}>
+      <AppBarEditor leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth} position="fixed">
         <ToolBarContainer>
           <Button component={Link} to="/admin/tävlingshanterare" style={{ padding: 0 }}>
             <HomeIcon src="/t8.png" />
           </Button>
-          <Typography variant="h6" noWrap>
+          <CompetitionName variant="h5" noWrap>
             {competition.name}
-          </Typography>
+          </CompetitionName>
 
           <ViewButtonGroup>
-            <GreenCheckbox checked={checkbox} onChange={(event) => setCheckbox(event.target.checked)} />
-            <Typography className={classes.alignCheckboxText} variant="button">
-              Applicera ändringar på samtliga vyer
-            </Typography>
-            <ViewButton variant="contained" color="secondary">
+            <ViewButton
+              $activeView={activeViewTypeName === 'Audience'}
+              variant="contained"
+              color="secondary"
+              onClick={() => changeView('Audience')}
+            >
               Åskådarvy
             </ViewButton>
-            <ViewButton variant="contained" color="secondary">
+            <ViewButton
+              $activeView={activeViewTypeName === 'Team'}
+              variant="contained"
+              color="secondary"
+              onClick={() => changeView('Team')}
+            >
               Deltagarvy
             </ViewButton>
-            <ViewButton variant="contained" color="secondary">
-              Domarvy
-            </ViewButton>
           </ViewButtonGroup>
         </ToolBarContainer>
-      </AppBar>
-      <Drawer
-        className={classes.leftDrawer}
-        variant="permanent"
-        classes={{
-          paper: classes.leftDrawerPaper,
-        }}
-        anchor="left"
-      >
-        <div className={classes.toolbar} />
-        <Divider />
-        <SlideList>
-          <div>
+      </AppBarEditor>
+      <LeftDrawer leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={undefined} variant="permanent" anchor="left">
+        <FillLeftContainer leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={undefined}>
+          <ToolbarMargin />
+          <SlideList>
             {competition.slides &&
               competition.slides.map((slide) => (
                 <SlideListItem
@@ -220,36 +160,35 @@ const PresentationEditorPage: React.FC = () => {
                   <ListItemText primary={`Sida ${slide.order + 1}`} />
                 </SlideListItem>
               ))}
-          </div>
-          <div>
+          </SlideList>
+          <PositionBottom>
             <Divider />
             <SlideListItem divider button onClick={() => createNewSlide()}>
               <ListItemText primary="Ny sida" />
               <AddOutlinedIcon />
             </SlideListItem>
-          </div>
-        </SlideList>
-      </Drawer>
-      <div className={classes.toolbar} />
-      <Drawer
-        className={classes.rightDrawer}
-        variant="permanent"
-        classes={{
-          paper: classes.rightDrawerPaper,
-        }}
-        anchor="right"
-      >
-        {!competitionLoading ? (
-          <SettingsPanel />
-        ) : (
-          <CenteredSpinnerContainer>
-            <CircularProgress />
-          </CenteredSpinnerContainer>
-        )}
-      </Drawer>
+          </PositionBottom>
+        </FillLeftContainer>
+      </LeftDrawer>
+      <ToolbarMargin />
+      <RightDrawer leftDrawerWidth={undefined} rightDrawerWidth={rightDrawerWidth} variant="permanent" anchor="right">
+        <FillRightContainer leftDrawerWidth={undefined} rightDrawerWidth={rightDrawerWidth}>
+          <RightPanelScroll>
+            {!competitionLoading ? (
+              <SettingsPanel />
+            ) : (
+              <CenteredSpinnerContainer>
+                <CircularProgress />
+              </CenteredSpinnerContainer>
+            )}
+          </RightPanelScroll>
+        </FillRightContainer>
+      </RightDrawer>
 
       <Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}>
-        <SlideEditor />
+        <InnerContent>
+          <SlideDisplay variant="editor" activeViewTypeId={activeViewTypeId} />
+        </InnerContent>
       </Content>
       <Menu
         keepMounted
diff --git a/client/src/pages/presentationEditor/components/BackgroundImageSelect.tsx b/client/src/pages/presentationEditor/components/BackgroundImageSelect.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..14dbaa63ed2cc267a080264ebe3d7abd243220ba
--- /dev/null
+++ b/client/src/pages/presentationEditor/components/BackgroundImageSelect.tsx
@@ -0,0 +1,126 @@
+import { ListItem, ListItemText, Typography } from '@material-ui/core'
+import React, { useState } from 'react'
+import { useAppDispatch, useAppSelector } from '../../../hooks'
+import {
+  AddButton,
+  AddBackgroundButton,
+  Center,
+  HiddenInput,
+  ImportedImage,
+  SettingsList,
+  ImageNameText,
+  ImageTextContainer,
+} from './styled'
+import CloseIcon from '@material-ui/icons/Close'
+import axios from 'axios'
+import { Media } from '../../../interfaces/ApiModels'
+import { getEditorCompetition } from '../../../actions/editor'
+import { uploadFile } from '../../../utils/uploadImage'
+
+type BackgroundImageSelectProps = {
+  variant: 'competition' | 'slide'
+}
+
+const BackgroundImageSelect = ({ variant }: BackgroundImageSelectProps) => {
+  const activeSlideId = useAppSelector((state) => state.editor.activeSlideId)
+  const backgroundImage = useAppSelector((state) => {
+    if (variant === 'competition') return state.editor.competition.background_image
+    else return state.editor.competition.slides.find((slide) => slide.id === activeSlideId)?.background_image
+  })
+  const competitionId = useAppSelector((state) => state.editor.competition.id)
+  const dispatch = useAppDispatch()
+
+  const updateBackgroundImage = async (mediaId: number) => {
+    // Creates a new image component on the database using API call.
+    if (variant === 'competition') {
+      await axios
+        .put(`/api/competitions/${competitionId}`, { background_image_id: mediaId })
+        .then(() => {
+          dispatch(getEditorCompetition(competitionId.toString()))
+        })
+        .catch(console.log)
+    } else {
+      await axios
+        .put(`/api/competitions/${competitionId}/slides/${activeSlideId}`, { background_image_id: mediaId })
+        .then(() => {
+          dispatch(getEditorCompetition(competitionId.toString()))
+        })
+        .catch(console.log)
+    }
+  }
+
+  const removeBackgroundImage = async () => {
+    // Removes background image media and from competition using API calls.
+    await axios.delete(`/api/media/images/${backgroundImage?.id}`).catch(console.log)
+    if (variant === 'competition') {
+      await axios
+        .put(`/api/competitions/${competitionId}`, { background_image_id: null })
+        .then(() => {
+          dispatch(getEditorCompetition(competitionId.toString()))
+        })
+        .catch(console.log)
+    } else {
+      await axios
+        .put(`/api/competitions/${competitionId}/slides/${activeSlideId}`, { background_image_id: null })
+        .then(() => {
+          dispatch(getEditorCompetition(competitionId.toString()))
+        })
+        .catch(console.log)
+    }
+  }
+
+  const handleFileSelected = async (e: React.ChangeEvent<HTMLInputElement>) => {
+    // Reads the selected image file and uploads it to the server.
+    // Creates a new image component containing the file.
+    if (e.target.files !== null && e.target.files[0]) {
+      const files = Array.from(e.target.files)
+      const file = files[0]
+      const formData = new FormData()
+      formData.append('image', file)
+      const media = await uploadFile(formData, competitionId.toString())
+      if (media) {
+        updateBackgroundImage(media.id)
+      }
+    }
+  }
+
+  return (
+    <SettingsList>
+      {!backgroundImage && (
+        <ListItem button style={{ padding: 0 }}>
+          <HiddenInput
+            accept="image/*"
+            id="background-button-file"
+            multiple
+            type="file"
+            onChange={handleFileSelected}
+          />
+          <AddBackgroundButton htmlFor="background-button-file">
+            <Center>
+              <AddButton variant="button">Välj bakgrundsbild...</AddButton>
+            </Center>
+          </AddBackgroundButton>
+        </ListItem>
+      )}
+      {backgroundImage && (
+        <>
+          <ListItem divider>
+            <ImageTextContainer>
+              <ListItemText>Bakgrundsbild</ListItemText>
+              <Typography variant="body2">(Bilden bör ha sidförhållande 16:9)</Typography>
+            </ImageTextContainer>
+          </ListItem>
+          <ListItem divider button>
+            <ImportedImage src={`/static/images/thumbnail_${backgroundImage.filename}`} />
+            <Center>
+              <ImageNameText primary={backgroundImage.filename} />
+            </Center>
+            <CloseIcon onClick={removeBackgroundImage} />
+          </ListItem>
+        </>
+      )}
+    </SettingsList>
+  )
+}
+
+export default BackgroundImageSelect
diff --git a/client/src/pages/presentationEditor/components/CompetitionSettings.tsx b/client/src/pages/presentationEditor/components/CompetitionSettings.tsx
index 53c2ecdb4dd23cc81df0140bd2bede66dfe18b88..9482b660550e729f2a5a4c443d0f935193679b1f 100644
--- a/client/src/pages/presentationEditor/components/CompetitionSettings.tsx
+++ b/client/src/pages/presentationEditor/components/CompetitionSettings.tsx
@@ -1,14 +1,7 @@
 import {
-  Button,
-  Dialog,
-  DialogActions,
-  DialogContent,
-  DialogContentText,
-  DialogTitle,
   Divider,
   FormControl,
   InputLabel,
-  List,
   ListItem,
   ListItemText,
   MenuItem,
@@ -16,84 +9,45 @@ import {
   TextField,
   Typography,
 } from '@material-ui/core'
-import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
-import CloseIcon from '@material-ui/icons/Close'
+import { Center, ImportedImage, SettingsList, PanelContainer, FirstItem, AddButton } from './styled'
 import axios from 'axios'
 import React, { useState } from 'react'
 import { useParams } from 'react-router-dom'
 import { getEditorCompetition } from '../../../actions/editor'
 import { useAppDispatch, useAppSelector } from '../../../hooks'
 import { City } from '../../../interfaces/ApiModels'
-
-const useStyles = makeStyles((theme: Theme) =>
-  createStyles({
-    textInputContainer: {
-      '& > *': {
-        margin: theme.spacing(1),
-        width: '100%',
-        background: 'white',
-      },
-    },
-    textInput: {
-      margin: theme.spacing(2),
-      width: '87%',
-      background: 'white',
-    },
-    textCenter: {
-      textAlign: 'center',
-    },
-    center: {
-      display: 'flex',
-      justifyContent: 'center',
-      background: 'white',
-    },
-    importedImage: {
-      width: 70,
-      height: 50,
-      background: 'white',
-    },
-    dropDown: {
-      margin: theme.spacing(2),
-      width: '87%',
-      background: 'white',
-    },
-    addButtons: {
-      padding: 5,
-    },
-    panelList: {
-      padding: 0,
-    },
-  })
-)
+import Teams from './Teams'
+import BackgroundImageSelect from './BackgroundImageSelect'
 
 interface CompetitionParams {
-  id: string
+  competitionId: string
 }
 
 const CompetitionSettings: React.FC = () => {
-  const classes = useStyles()
-  const { id }: CompetitionParams = useParams()
+  const { competitionId }: CompetitionParams = useParams()
   const dispatch = useAppDispatch()
   const competition = useAppSelector((state) => state.editor.competition)
+  const cities = useAppSelector((state) => state.cities.cities)
+
   const updateCompetitionName = async (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
     await axios
-      .put(`/api/competitions/${id}`, { name: event.target.value })
+      .put(`/api/competitions/${competitionId}`, { name: event.target.value })
       .then(() => {
-        dispatch(getEditorCompetition(id))
+        dispatch(getEditorCompetition(competitionId))
       })
       .catch(console.log)
   }
 
-  const cities = useAppSelector((state) => state.cities.cities)
   const updateCompetitionCity = async (city: City) => {
     await axios
-      .put(`/api/competitions/${id}`, { city_id: city.id })
+      .put(`/api/competitions/${competitionId}`, { city_id: city.id })
       .then(() => {
-        dispatch(getEditorCompetition(id))
+        dispatch(getEditorCompetition(competitionId))
       })
       .catch(console.log)
   }
 
+  /* Finds the right city object from a city name */
   const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
     cities.forEach((city) => {
       if (event.target.value === city.name) {
@@ -102,109 +56,45 @@ const CompetitionSettings: React.FC = () => {
     })
   }
 
-  const removeTeam = async (tid: number) => {
-    await axios
-      .delete(`/api/competitions/${id}/teams/${tid}`)
-      .then(() => {
-        dispatch(getEditorCompetition(id))
-      })
-      .catch(console.log)
-  }
-  const addTeam = async () => {
-    setAddTeamOpen(false)
-    await axios
-      .post(`/api/competitions/${id}/teams`, { name: selectedTeamName })
-      .then(() => {
-        dispatch(getEditorCompetition(id))
-      })
-      .catch(console.log)
-  }
-  // For "add team" dialog
-  const [addTeamOpen, setAddTeamOpen] = useState(false)
-  const openAddTeam = () => {
-    setAddTeamOpen(true)
-  }
-  const closeAddTeam = () => {
-    setAddTeamOpen(false)
-  }
-  let selectedTeamName = ''
-  const updateSelectedTeamName = (event: React.ChangeEvent<{ value: string }>) => {
-    selectedTeamName = event.target.value
-  }
-
   return (
-    <div className={classes.textInputContainer}>
-      <form noValidate autoComplete="off">
-        <TextField
-          className={classes.textInput}
-          id="outlined-basic"
-          label={'Tävlingsnamn'}
-          defaultValue={competition.name}
-          onChange={updateCompetitionName}
-          variant="outlined"
-        />
+    <PanelContainer>
+      <SettingsList>
+        <FirstItem>
+          <ListItem>
+            <TextField
+              id="outlined-basic"
+              label={'Tävlingsnamn'}
+              defaultValue={competition.name}
+              onChange={updateCompetitionName}
+              variant="outlined"
+              fullWidth={true}
+            />
+          </ListItem>
+        </FirstItem>
         <Divider />
-        <FormControl variant="outlined" className={classes.dropDown}>
-          <InputLabel>Region</InputLabel>
-          <Select
-            value={cities.find((city) => city.id === competition.city_id)?.name || ''}
-            label="Region"
-            onChange={handleChange}
-          >
-            {cities.map((city) => (
-              <MenuItem value={city.name} key={city.name}>
-                <Button>{city.name}</Button>
-              </MenuItem>
-            ))}
-          </Select>
-        </FormControl>
-      </form>
 
-      <List className={classes.panelList}>
         <ListItem>
-          <ListItemText className={classes.textCenter} primary="Lag" />
+          <FormControl fullWidth variant="outlined">
+            <InputLabel>Region</InputLabel>
+            <Select
+              value={cities.find((city) => city.id === competition.city_id)?.name || ''}
+              label="Region"
+              onChange={handleChange}
+            >
+              {cities.map((city) => (
+                <MenuItem value={city.name} key={city.name}>
+                  <Typography variant="button">{city.name}</Typography>
+                </MenuItem>
+              ))}
+            </Select>
+          </FormControl>
         </ListItem>
-        {competition.teams &&
-          competition.teams.map((team) => (
-            <div key={team.id}>
-              <ListItem divider button>
-                <ListItemText primary={team.name} />
-                <CloseIcon onClick={() => removeTeam(team.id)} />
-              </ListItem>
-            </div>
-          ))}
+      </SettingsList>
 
-        <ListItem className={classes.center} button onClick={openAddTeam}>
-          <Typography className={classes.addButtons} variant="button">
-            Lägg till lag
-          </Typography>
-        </ListItem>
-        <Dialog open={addTeamOpen} onClose={closeAddTeam}>
-          <DialogTitle className={classes.center}>Lägg till lag</DialogTitle>
-          <DialogContent>
-            <DialogContentText>Skriv namnet på laget och klicka sedan på bekräfta.</DialogContentText>
-            <TextField autoFocus margin="dense" label="Lagnamn" fullWidth onChange={updateSelectedTeamName} />
-          </DialogContent>
-          <DialogActions>
-            <Button onClick={closeAddTeam} color="secondary">
-              Avbryt
-            </Button>
-            <Button onClick={addTeam} color="primary">
-              Bekräfta
-            </Button>
-          </DialogActions>
-        </Dialog>
-      </List>
+      <Teams competitionId={competitionId} />
 
-      <ListItem button>
-        <img
-          id="temp source, todo: add image source to elements of pictureList"
-          src="https://i1.wp.com/stickoutmedia.se/wp-content/uploads/2021/01/placeholder-3.png?ssl=1"
-          className={classes.importedImage}
-        />
-        <ListItemText className={classes.textCenter}>Välj bakgrundsbild ...</ListItemText>
-      </ListItem>
-    </div>
+      <BackgroundImageSelect variant="competition" />
+    </PanelContainer>
   )
 }
 
diff --git a/client/src/pages/presentationEditor/components/ImageComponentDisplay.test.tsx b/client/src/pages/presentationEditor/components/ImageComponentDisplay.test.tsx
index 9e78f8e13fd94cf36434ef63ed7504695208de1c..eb823693c7f192489f4e0cc8e15f5f1af3f6a470 100644
--- a/client/src/pages/presentationEditor/components/ImageComponentDisplay.test.tsx
+++ b/client/src/pages/presentationEditor/components/ImageComponentDisplay.test.tsx
@@ -5,7 +5,15 @@ import ImageComponentDisplay from './ImageComponentDisplay'
 it('renders competition settings', () => {
   render(
     <ImageComponentDisplay
-      component={{ id: 0, x: 0, y: 0, w: 0, h: 0, data: { media_id: 0, filename: '' }, type_id: 2 }}
+      component={{
+        id: 0,
+        x: 0,
+        y: 0,
+        w: 0,
+        h: 0,
+        media: { id: 0, mediatype_id: 0, user_id: 0, filename: '' },
+        type_id: 2,
+      }}
       width={0}
       height={0}
     />
diff --git a/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx b/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
index 5e409d57f0e6f8a7503745760dd9078440fa732c..e783bdb22e743c345611b92bfc11d161fc2fe957 100644
--- a/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
+++ b/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
@@ -10,7 +10,7 @@ type ImageComponentProps = {
 const ImageComponentDisplay = ({ component, width, height }: ImageComponentProps) => {
   return (
     <img
-      src={`http://localhost:5000/static/images/${component.filename}`}
+      src={`http://localhost:5000/static/images/${component.media?.filename}`}
       height={height}
       width={width}
       draggable={false}
diff --git a/client/src/pages/presentationEditor/components/RndComponent.tsx b/client/src/pages/presentationEditor/components/RndComponent.tsx
index c7b5933abb8144722d4348a81de59f1fd669002e..77ed5de337f5ed620e77994cea2e512166d7b7d9 100644
--- a/client/src/pages/presentationEditor/components/RndComponent.tsx
+++ b/client/src/pages/presentationEditor/components/RndComponent.tsx
@@ -10,22 +10,25 @@ import CheckboxComponent from './CheckboxComponent'
 import ImageComponentDisplay from './ImageComponentDisplay'
 import { HoverContainer } from './styled'
 import FormatAlignCenterIcon from '@material-ui/icons/FormatAlignCenter'
+import TextComponentDisplay from './TextComponentDisplay'
 
-type ImageComponentProps = {
+type RndComponentProps = {
   component: Component
   width: number
   height: number
+  scale: number
 }
 
-const RndComponent = ({ component, width, height }: ImageComponentProps) => {
-  //Makes scale close to 1, 800 height is approxemately for a 1920 by 1080 monitor
-  const scale = height / 800
+const RndComponent = ({ component, width, height, scale }: RndComponentProps) => {
   const [hover, setHover] = useState(false)
   const [currentPos, setCurrentPos] = useState<Position>({ x: component.x, y: component.y })
   const [currentSize, setCurrentSize] = useState<Size>({ w: component.w, h: component.h })
   const competitionId = useAppSelector((state) => state.editor.competition.id)
   const slideId = useAppSelector((state) => state.editor.activeSlideId)
   const [shiftPressed, setShiftPressed] = useState(false)
+  const typeName = useAppSelector(
+    (state) => state.types.componentTypes.find((componentType) => componentType.id === component.type_id)?.name
+  )
   const handleUpdatePos = (pos: Position) => {
     axios.put(`/api/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
       x: pos.x,
@@ -68,35 +71,18 @@ const RndComponent = ({ component, width, height }: ImageComponentProps) => {
   const renderInnerComponent = () => {
     switch (component.type_id) {
       case ComponentTypes.Text:
-        return (
-          <HoverContainer
-            hover={hover}
-            dangerouslySetInnerHTML={{
-              __html: `<div style="font-size: ${Math.round(24 * scale)}px;">${(component as TextComponent).text}</div>`,
-            }}
-          />
-        )
-      case ComponentTypes.Image:
         return (
           <HoverContainer hover={hover}>
-            <img
-              key={component.id}
-              src={`/static/images/${(component as ImageComponent).filename}`}
-              height={currentSize.h * scale}
-              width={currentSize.w * scale}
-              draggable={false}
-            />
+            <TextComponentDisplay component={component as TextComponent} scale={scale} />
           </HoverContainer>
         )
       case ComponentTypes.Image:
         return (
           <HoverContainer hover={hover}>
-            <img
-              key={component.id}
-              src={`/static/images/${(component as ImageComponent).filename}`}
+            <ImageComponentDisplay
               height={currentSize.h * scale}
               width={currentSize.w * scale}
-              draggable={false}
+              component={component as ImageComponent}
             />
           </HoverContainer>
         )
@@ -115,6 +101,8 @@ const RndComponent = ({ component, width, height }: ImageComponentProps) => {
         setCurrentPos({ x: d.x / scale, y: d.y / scale })
         handleUpdatePos({ x: d.x / scale, y: d.y / scale })
       }}
+      //Makes text appear on images
+      style={{ zIndex: typeName === 'Text' ? 2 : 1 }}
       lockAspectRatio={shiftPressed}
       onMouseEnter={() => setHover(true)}
       onMouseLeave={() => setHover(false)}
@@ -131,6 +119,7 @@ const RndComponent = ({ component, width, height }: ImageComponentProps) => {
           w: ref.offsetWidth / scale,
           h: ref.offsetHeight / scale,
         })
+        setCurrentPos({ x: position.x / scale, y: position.y / scale })
       }}
     >
       {hover && (
diff --git a/client/src/pages/presentationEditor/components/SlideDisplay.tsx b/client/src/pages/presentationEditor/components/SlideDisplay.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..aef4ca7c48d29bbfc47cac8f29b214ec6866f360
--- /dev/null
+++ b/client/src/pages/presentationEditor/components/SlideDisplay.tsx
@@ -0,0 +1,96 @@
+import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'
+import { getTypes } from '../../../actions/typesAction'
+import { useAppDispatch, useAppSelector } from '../../../hooks'
+import PresentationComponent from '../../views/components/PresentationComponent'
+import RndComponent from './RndComponent'
+import { SlideEditorContainer, SlideEditorContainerRatio, SlideEditorPaper } from './styled'
+
+type SlideDisplayProps = {
+  //Prop to distinguish between editor and active competition
+  variant: 'editor' | 'presentation'
+  activeViewTypeId: number
+}
+
+const SlideDisplay = ({ variant, activeViewTypeId }: SlideDisplayProps) => {
+  const components = useAppSelector((state) => {
+    if (variant === 'editor')
+      return state.editor.competition.slides.find((slide) => slide.id === state.editor.activeSlideId)?.components
+    return state.presentation.competition.slides.find((slide) => slide.id === state.presentation.slide?.id)?.components
+  })
+  const competitionBackgroundImage = useAppSelector((state) => {
+    if (variant === 'editor') return state.editor.competition.background_image
+    return state.presentation.competition.background_image
+  })
+
+  const slideBackgroundImage = useAppSelector((state) => {
+    if (variant === 'editor')
+      return state.editor.competition.slides.find((slide) => slide.id === state.editor.activeSlideId)?.background_image
+    return state.presentation.competition.slides.find((slide) => slide.id === state.presentation.slide.id)
+      ?.background_image
+  })
+  const dispatch = useAppDispatch()
+  const editorPaperRef = useRef<HTMLDivElement>(null)
+  const [width, setWidth] = useState(0)
+  const [height, setHeight] = useState(0)
+  //Makes scale close to 1, 800 height is approxemately for a 1920 by 1080 monitor
+  const scale = height / 800
+  useEffect(() => {
+    dispatch(getTypes())
+  }, [])
+
+  useLayoutEffect(() => {
+    const updateScale = () => {
+      if (editorPaperRef.current) {
+        setWidth(editorPaperRef.current.clientWidth)
+        setHeight(editorPaperRef.current.clientHeight)
+      }
+    }
+    window.addEventListener('resize', updateScale)
+    updateScale()
+    return () => window.removeEventListener('resize', updateScale)
+  }, [])
+  return (
+    <SlideEditorContainer>
+      <SlideEditorContainerRatio>
+        <SlideEditorPaper ref={editorPaperRef}>
+          {(competitionBackgroundImage || slideBackgroundImage) && (
+            <img
+              src={`/static/images/${
+                slideBackgroundImage ? slideBackgroundImage.filename : competitionBackgroundImage?.filename
+              }`}
+              height={height}
+              width={width}
+              draggable={false}
+            />
+          )}
+          {components &&
+            components
+              .filter((component) => component.view_type_id === activeViewTypeId)
+              .map((component) => {
+                if (variant === 'editor')
+                  return (
+                    <RndComponent
+                      height={height}
+                      width={width}
+                      key={component.id}
+                      component={component}
+                      scale={scale}
+                    />
+                  )
+                return (
+                  <PresentationComponent
+                    height={height}
+                    width={width}
+                    key={component.id}
+                    component={component}
+                    scale={scale}
+                  />
+                )
+              })}
+        </SlideEditorPaper>
+      </SlideEditorContainerRatio>
+    </SlideEditorContainer>
+  )
+}
+
+export default SlideDisplay
diff --git a/client/src/pages/presentationEditor/components/SlideEditor.tsx b/client/src/pages/presentationEditor/components/SlideEditor.tsx
deleted file mode 100644
index b385bb2058fe73987f1a649456c6fe0adad10c56..0000000000000000000000000000000000000000
--- a/client/src/pages/presentationEditor/components/SlideEditor.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'
-import { getTypes } from '../../../actions/typesAction'
-import { useAppDispatch, useAppSelector } from '../../../hooks'
-import RndComponent from './RndComponent'
-import { SlideEditorContainer, SlideEditorContainerRatio, SlideEditorPaper } from './styled'
-
-const SlideEditor: React.FC = () => {
-  const components = useAppSelector(
-    (state) =>
-      state.editor.competition.slides.find((slide) => slide && slide.id === state.editor.activeSlideId)?.components
-  )
-  const dispatch = useAppDispatch()
-  const editorPaperRef = useRef<HTMLDivElement>(null)
-  const [width, setWidth] = useState(0)
-  const [height, setHeight] = useState(0)
-  useEffect(() => {
-    dispatch(getTypes())
-  }, [])
-
-  useLayoutEffect(() => {
-    const updateScale = () => {
-      if (editorPaperRef.current) {
-        setWidth(editorPaperRef.current.clientWidth)
-        setHeight(editorPaperRef.current.clientHeight)
-      }
-    }
-    window.addEventListener('resize', updateScale)
-    updateScale()
-    return () => window.removeEventListener('resize', updateScale)
-  }, [])
-  return (
-    <SlideEditorContainer>
-      <SlideEditorContainerRatio>
-        <SlideEditorPaper ref={editorPaperRef}>
-          {components &&
-            components.map((component) => (
-              <RndComponent height={height} width={width} key={component.id} component={component} />
-            ))}
-        </SlideEditorPaper>
-      </SlideEditorContainerRatio>
-    </SlideEditorContainer>
-  )
-}
-
-export default SlideEditor
diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx
index d0b72e39b4c7b83398350ac87be391c19681460c..d48b4565712d043d52d3b1e93e3633ec6b927d38 100644
--- a/client/src/pages/presentationEditor/components/SlideSettings.tsx
+++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx
@@ -4,51 +4,61 @@ import { Divider, List, ListItem, ListItemText, TextField, Typography } from '@m
 import React, { useState } from 'react'
 import { useParams } from 'react-router-dom'
 import { useAppSelector } from '../../../hooks'
-import Alternatives from './Alternatives'
-import SlideType from './SlideType'
-import { Center, ImportedImage, SettingsList, SlidePanel } from './styled'
-import Timer from './Timer'
-import Images from './Images'
-import Texts from './Texts'
+import Instructions from './slideSettingsComponents/Instructions'
+import MultipleChoiceAlternatives from './slideSettingsComponents/MultipleChoiceAlternatives'
+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 QuestionSettings from './slideSettingsComponents/QuestionSettings'
+import BackgroundImageSelect from './BackgroundImageSelect'
 
 interface CompetitionParams {
-  id: string
+  competitionId: string
 }
 
 const SlideSettings: React.FC = () => {
-  const { id }: CompetitionParams = useParams()
+  const { competitionId }: CompetitionParams = useParams()
 
   const activeSlide = useAppSelector((state) =>
     // Gets the slide with id=activeSlideId from the database.
     state.editor.competition.slides.find((slide) => slide && slide.id === state.editor.activeSlideId)
   )
+  const activeViewTypeId = useAppSelector((state) => state.editor.activeViewTypeId)
 
   return (
-    <SlidePanel>
+    <PanelContainer>
       <SettingsList>
-        {activeSlide && <SlideType activeSlide={activeSlide} competitionId={id} />}
+        {activeSlide && <SlideType activeSlide={activeSlide} competitionId={competitionId} />}
         <Divider />
-        {activeSlide && <Timer activeSlide={activeSlide} competitionId={id} />}
+        {activeSlide && <Timer activeSlide={activeSlide} competitionId={competitionId} />}
       </SettingsList>
 
-      {activeSlide && <Alternatives activeSlide={activeSlide} competitionId={id} />}
+      {activeSlide?.questions[0] && <QuestionSettings activeSlide={activeSlide} competitionId={competitionId} />}
+      {
+        // Choose answer alternatives depending on the slide type
+      }
+      {activeSlide?.questions[0]?.type_id === 1 && (
+        <Instructions activeSlide={activeSlide} competitionId={competitionId} />
+      )}
+      {activeSlide?.questions[0]?.type_id === 2 && (
+        <Instructions activeSlide={activeSlide} competitionId={competitionId} />
+      )}
+      {activeSlide?.questions[0]?.type_id === 3 && (
+        <MultipleChoiceAlternatives activeSlide={activeSlide} competitionId={competitionId} />
+      )}
 
-      {activeSlide && <Texts activeSlide={activeSlide} competitionId={id} />}
+      {activeSlide && (
+        <Texts activeViewTypeId={activeViewTypeId} activeSlide={activeSlide} competitionId={competitionId} />
+      )}
 
-      {activeSlide && <Images activeSlide={activeSlide} competitionId={id} />}
+      {activeSlide && (
+        <Images activeViewTypeId={activeViewTypeId} activeSlide={activeSlide} competitionId={competitionId} />
+      )}
 
-      <SettingsList>
-        <ListItem button>
-          <ImportedImage
-            id="temp source, todo: add image source to elements of pictureList"
-            src="https://i1.wp.com/stickoutmedia.se/wp-content/uploads/2021/01/placeholder-3.png?ssl=1"
-          />
-          <Center>
-            <ListItemText>Välj bakgrundsbild ...</ListItemText>
-          </Center>
-        </ListItem>
-      </SettingsList>
-    </SlidePanel>
+      <BackgroundImageSelect variant="slide" />
+    </PanelContainer>
   )
 }
 
diff --git a/client/src/pages/presentationEditor/components/Teams.tsx b/client/src/pages/presentationEditor/components/Teams.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..564dbc847638f1d16e6d09b7ae96019e02856a07
--- /dev/null
+++ b/client/src/pages/presentationEditor/components/Teams.tsx
@@ -0,0 +1,100 @@
+import {
+  Button,
+  Dialog,
+  DialogActions,
+  DialogContent,
+  DialogContentText,
+  DialogTitle,
+  ListItem,
+  ListItemText,
+  TextField,
+} from '@material-ui/core'
+import axios from 'axios'
+import React, { useState } from 'react'
+import { getEditorCompetition } from '../../../actions/editor'
+import { useAppDispatch, useAppSelector } from '../../../hooks'
+import { Center, Clickable } from './styled'
+import { AddButton, SettingsList } from './styled'
+import CloseIcon from '@material-ui/icons/Close'
+
+type TeamsProps = {
+  competitionId: string
+}
+
+const Teams = ({ competitionId }: TeamsProps) => {
+  const dispatch = useAppDispatch()
+  const competition = useAppSelector((state) => state.editor.competition)
+  const addTeam = async () => {
+    setAddTeamOpen(false)
+    await axios
+      .post(`/api/competitions/${competitionId}/teams`, { name: selectedTeamName })
+      .then(() => {
+        dispatch(getEditorCompetition(competitionId))
+      })
+      .catch(console.log)
+  }
+  // For "add team" dialog
+  const [addTeamOpen, setAddTeamOpen] = useState(false)
+  const openAddTeam = () => {
+    setAddTeamOpen(true)
+  }
+  const closeAddTeam = () => {
+    setAddTeamOpen(false)
+  }
+  let selectedTeamName = ''
+  const updateSelectedTeamName = (event: React.ChangeEvent<{ value: string }>) => {
+    selectedTeamName = event.target.value
+  }
+
+  const removeTeam = async (tid: number) => {
+    await axios
+      .delete(`/api/competitions/${competitionId}/teams/${tid}`)
+      .then(() => {
+        dispatch(getEditorCompetition(competitionId))
+      })
+      .catch(console.log)
+  }
+
+  return (
+    <SettingsList>
+      <ListItem divider>
+        <Center>
+          <ListItemText primary="Lag" />
+        </Center>
+      </ListItem>
+      {competition.teams &&
+        competition.teams.map((team) => (
+          <div key={team.id}>
+            <ListItem divider>
+              <ListItemText primary={team.name} />
+              <Clickable>
+                <CloseIcon onClick={() => removeTeam(team.id)} />
+              </Clickable>
+            </ListItem>
+          </div>
+        ))}
+      <ListItem button onClick={openAddTeam}>
+        <Center>
+          <AddButton variant="button">Lägg till lag</AddButton>
+        </Center>
+      </ListItem>
+      <Dialog open={addTeamOpen} onClose={closeAddTeam}>
+        <DialogTitle>Lägg till lag</DialogTitle>
+        <DialogContent>
+          <DialogContentText>Skriv namnet på laget och klicka sedan på bekräfta.</DialogContentText>
+          <TextField autoFocus margin="dense" label="Lagnamn" fullWidth onChange={updateSelectedTeamName} />
+        </DialogContent>
+        <DialogActions>
+          <Button onClick={closeAddTeam} color="secondary">
+            Avbryt
+          </Button>
+          <Button onClick={addTeam} color="primary">
+            Bekräfta
+          </Button>
+        </DialogActions>
+      </Dialog>
+    </SettingsList>
+  )
+}
+
+export default Teams
diff --git a/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx b/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b2e11200e4cd14789ebd7945dd7a10b83107d740
--- /dev/null
+++ b/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx
@@ -0,0 +1,19 @@
+import React from 'react'
+import { ImageComponent, TextComponent } from '../../../interfaces/ApiModels'
+
+type TextComponentDisplayProps = {
+  component: TextComponent
+  scale: number
+}
+
+const ImageComponentDisplay = ({ component, scale }: TextComponentDisplayProps) => {
+  return (
+    <div
+      dangerouslySetInnerHTML={{
+        __html: `<div style="font-size: ${Math.round(24 * scale)}px;">${component.text}</div>`,
+      }}
+    />
+  )
+}
+
+export default ImageComponentDisplay
diff --git a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
index f50e547a8d1e7f5eadadca1ad1d531a46cc0410e..f6cda5760ec8aff5bce211a8caf7d2435a8d5c61 100644
--- a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
+++ b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
@@ -12,23 +12,23 @@ type ImageComponentProps = {
 }
 
 interface CompetitionParams {
-  id: string
+  competitionId: string
 }
 
 const TextComponentEdit = ({ component }: ImageComponentProps) => {
-  const { id }: CompetitionParams = useParams()
-  const competitionId = useAppSelector((state) => state.editor.competition.id)
+  const { competitionId }: CompetitionParams = useParams()
   const [content, setContent] = useState('')
   const [timerHandle, setTimerHandle] = React.useState<number | undefined>(undefined)
   const activeSlideId = useAppSelector((state) => state.editor.activeSlideId)
+  const activeViewTypeId = useAppSelector((state) => state.editor.activeViewTypeId)
   const dispatch = useAppDispatch()
 
   useEffect(() => {
     setContent(component.text)
   }, [])
 
-  const handleSaveText = async (a: string) => {
-    setContent(a)
+  const handleSaveText = async (newText: string) => {
+    setContent(newText)
     if (timerHandle) {
       clearTimeout(timerHandle)
       setTimerHandle(undefined)
@@ -38,16 +38,16 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => {
       window.setTimeout(async () => {
         console.log('Content was updated on server. id: ', component.id)
         await axios.put(`/api/competitions/${competitionId}/slides/${activeSlideId}/components/${component.id}`, {
-          data: { ...component, text: a },
+          text: newText,
         })
-        dispatch(getEditorCompetition(id))
+        dispatch(getEditorCompetition(competitionId))
       }, 250)
     )
   }
 
   const handleDeleteText = async (componentId: number) => {
-    await axios.delete(`/api/competitions/${id}/slides/${activeSlideId}/components/${componentId}`)
-    dispatch(getEditorCompetition(id))
+    await axios.delete(`/api/competitions/${competitionId}/slides/${activeSlideId}/components/${componentId}`)
+    dispatch(getEditorCompetition(competitionId))
   }
 
   return (
@@ -57,8 +57,16 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => {
         init={{
           height: '300px',
           menubar: false,
+          font_formats:
+            ' Andale Mono=andale mono,times; Arial=arial,helvetica,sans-serif;\
+             Arial Black=arial black,avant garde; Book Antiqua=book antiqua,palatino; Calibri=calibri;\
+             Comic Sans MS=comic sans ms,sans-serif; Courier New=courier new,courier;\
+              Georgia=georgia,palatino; Helvetica=helvetica; Impact=impact,chicago; Symbol=symbol;\
+               Tahoma=tahoma,arial,helvetica,sans-serif; Terminal=terminal,monaco;\
+                Times New Roman=times new roman,times; Trebuchet MS=trebuchet ms,geneva;\
+                Verdana=verdana,geneva; Webdings=webdings; Wingdings=wingdings,zapf dingbats',
           fontsize_formats: '8pt 9pt 10pt 11pt 12pt 14pt 18pt 24pt 30pt 36pt 48pt 60pt 72pt 96pt 120pt 144pt',
-          content_style: 'body {font-size: 24pt;}',
+          content_style: 'body {font-size: 24pt; font-family: Calibri;}',
           plugins: [
             'advlist autolink lists link image charmap print preview anchor',
             'searchreplace visualblocks code fullscreen',
diff --git a/client/src/pages/presentationEditor/components/Images.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/Images.tsx
similarity index 67%
rename from client/src/pages/presentationEditor/components/Images.tsx
rename to client/src/pages/presentationEditor/components/slideSettingsComponents/Images.tsx
index b715ba2c4482d6d074f53409fbb1f4abab996409..49f41e7f87fa6c53dac59704540ca3c186ef708b 100644
--- a/client/src/pages/presentationEditor/components/Images.tsx
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/Images.tsx
@@ -2,32 +2,22 @@
  */
 import { ListItem, ListItemText, Typography } from '@material-ui/core'
 import CloseIcon from '@material-ui/icons/Close'
-import React, { useState } from 'react'
-import { useDispatch } from 'react-redux'
-import {
-  Center,
-  HiddenInput,
-  SettingsList,
-  AddImageButton,
-  ImportedImage,
-  WhiteBackground,
-  AddButton,
-  Clickable,
-  NoPadding,
-} from './styled'
+import React from 'react'
+import { Center, HiddenInput, SettingsList, AddImageButton, ImportedImage, AddButton } from '../styled'
 import axios from 'axios'
-import { getEditorCompetition } from '../../../actions/editor'
-import { RichSlide } from '../../../interfaces/ApiRichModels'
-import { ImageComponent, Media } from '../../../interfaces/ApiModels'
-import { useAppSelector } from '../../../hooks'
+import { getEditorCompetition } from '../../../../actions/editor'
+import { RichSlide } from '../../../../interfaces/ApiRichModels'
+import { ImageComponent, Media } from '../../../../interfaces/ApiModels'
+import { useAppDispatch, useAppSelector } from '../../../../hooks'
 
 type ImagesProps = {
+  activeViewTypeId: number
   activeSlide: RichSlide
   competitionId: string
 }
 
-const Images = ({ activeSlide, competitionId }: ImagesProps) => {
-  const dispatch = useDispatch()
+const Images = ({ activeViewTypeId, activeSlide, competitionId }: ImagesProps) => {
+  const dispatch = useAppDispatch()
 
   const uploadFile = async (formData: FormData) => {
     // Uploads the file to the server and creates a Media object in database.
@@ -48,6 +38,7 @@ const Images = ({ activeSlide, competitionId }: ImagesProps) => {
       y: 0,
       media_id: media.id,
       type_id: 2,
+      view_type_id: activeViewTypeId,
     }
     await axios
       .post(`/api/competitions/${competitionId}/slides/${activeSlide?.id}/components`, imageData)
@@ -67,7 +58,7 @@ const Images = ({ activeSlide, competitionId }: ImagesProps) => {
       formData.append('image', file)
       const response = await uploadFile(formData)
       if (response) {
-        const newComponent = createImageComponent(response)
+        createImageComponent(response)
       }
     }
   }
@@ -75,7 +66,7 @@ const Images = ({ activeSlide, competitionId }: ImagesProps) => {
   const handleCloseimageClick = async (image: ImageComponent) => {
     // Removes selected image component and deletes its file from the server.
     await axios
-      .delete(`/api/media/images/${image.media_id}`)
+      .delete(`/api/media/images/${image.media?.id}`)
       .then(() => {
         dispatch(getEditorCompetition(competitionId))
       })
@@ -99,32 +90,32 @@ const Images = ({ activeSlide, competitionId }: ImagesProps) => {
 
   return (
     <SettingsList>
-      <WhiteBackground>
-        <ListItem divider>
-          <Center>
-            <ListItemText primary="Bilder" />
-          </Center>
-        </ListItem>
-        {images &&
-          images.map((image) => (
+      <ListItem divider>
+        <Center>
+          <ListItemText primary="Bilder" />
+        </Center>
+      </ListItem>
+      {images &&
+        images
+          .filter((image) => image.view_type_id === activeViewTypeId)
+          .map((image) => (
             <div key={image.id}>
               <ListItem divider button>
-                <ImportedImage src={`http://localhost:5000/static/images/thumbnail_${image.filename}`} />
+                <ImportedImage src={`http://localhost:5000/static/images/thumbnail_${image.media?.filename}`} />
                 <Center>
-                  <ListItemText primary={image.filename} />
+                  <ListItemText primary={image.media?.filename} />
                 </Center>
                 <CloseIcon onClick={() => handleCloseimageClick(image)} />
               </ListItem>
             </div>
           ))}
 
-        <ListItem button>
-          <HiddenInput accept="image/*" id="contained-button-file" multiple type="file" onChange={handleFileSelected} />
-          <AddImageButton htmlFor="contained-button-file">
-            <AddButton variant="button">Lägg till bild</AddButton>
-          </AddImageButton>
-        </ListItem>
-      </WhiteBackground>
+      <ListItem button style={{ padding: 0 }}>
+        <HiddenInput accept="image/*" id="contained-button-file" multiple type="file" onChange={handleFileSelected} />
+        <AddImageButton htmlFor="contained-button-file">
+          <AddButton variant="button">Lägg till bild</AddButton>
+        </AddImageButton>
+      </ListItem>
     </SettingsList>
   )
 }
diff --git a/client/src/pages/presentationEditor/components/slideSettingsComponents/Instructions.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/Instructions.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..858dd75e65dc554cc6e1c2083f46873a925335a1
--- /dev/null
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/Instructions.tsx
@@ -0,0 +1,70 @@
+import { ListItem, ListItemText, TextField, withStyles } from '@material-ui/core'
+import axios from 'axios'
+import React from 'react'
+import { getEditorCompetition } from '../../../../actions/editor'
+import { useAppDispatch } from '../../../../hooks'
+import { RichSlide } from '../../../../interfaces/ApiRichModels'
+import { Center, SettingsList } from '../styled'
+
+type InstructionsProps = {
+  activeSlide: RichSlide
+  competitionId: string
+}
+
+const Instructions = ({ activeSlide, competitionId }: InstructionsProps) => {
+  const dispatch = useAppDispatch()
+  const [timerHandle, setTimerHandle] = React.useState<number | undefined>(undefined)
+
+  const updateInstructionsText = async (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
+    if (timerHandle) {
+      clearTimeout(timerHandle)
+      setTimerHandle(undefined)
+    }
+    //Only updates 250ms after last input was made to not spam
+    setTimerHandle(
+      window.setTimeout(async () => {
+        console.log('Content was updated on server. id: ', activeSlide.questions[0].id)
+        if (activeSlide && activeSlide.questions[0]) {
+          await axios
+            // TODO: Implement instructions field in question and add put API
+            .put(
+              `/api/competitions/${competitionId}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}`,
+              {
+                instructions: event.target.value,
+              }
+            )
+            .then(() => {
+              dispatch(getEditorCompetition(competitionId))
+            })
+            .catch(console.log)
+        }
+      }, 250)
+    )
+  }
+
+  return (
+    <SettingsList>
+      <ListItem divider>
+        <Center>
+          <ListItemText
+            primary="Rättningsinstruktioner"
+            secondary="Den här texten kommer endast att visas för domarna."
+          />
+        </Center>
+      </ListItem>
+      <ListItem divider>
+        <Center>
+          <TextField
+            id="outlined-basic"
+            defaultValue={''}
+            onChange={updateInstructionsText}
+            variant="outlined"
+            fullWidth={true}
+          />
+        </Center>
+      </ListItem>
+    </SettingsList>
+  )
+}
+
+export default Instructions
diff --git a/client/src/pages/presentationEditor/components/Alternatives.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/MultipleChoiceAlternatives.tsx
similarity index 59%
rename from client/src/pages/presentationEditor/components/Alternatives.tsx
rename to client/src/pages/presentationEditor/components/slideSettingsComponents/MultipleChoiceAlternatives.tsx
index e699d003f7f3433e2caf25a26185ad5141c6f694..58053995856730b22a16d4401af17da5628ab87a 100644
--- a/client/src/pages/presentationEditor/components/Alternatives.tsx
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/MultipleChoiceAlternatives.tsx
@@ -4,20 +4,19 @@ import { green, grey } from '@material-ui/core/colors'
 import CloseIcon from '@material-ui/icons/Close'
 import axios from 'axios'
 import React from 'react'
-import { getEditorCompetition } from '../../../actions/editor'
-import { useAppDispatch, useAppSelector } from '../../../hooks'
-import { QuestionAlternative } from '../../../interfaces/ApiModels'
-import { RichSlide } from '../../../interfaces/ApiRichModels'
-import { AddButton, Center, Clickable, SettingsList, TextInput, WhiteBackground } from './styled'
+import { getEditorCompetition } from '../../../../actions/editor'
+import { useAppDispatch, useAppSelector } from '../../../../hooks'
+import { QuestionAlternative } from '../../../../interfaces/ApiModels'
+import { RichSlide } from '../../../../interfaces/ApiRichModels'
+import { AddButton, AlternativeTextField, Center, Clickable, SettingsList } from '../styled'
 
-type AlternativeProps = {
+type MultipleChoiceAlternativeProps = {
   activeSlide: RichSlide
   competitionId: string
 }
 
-const Alternatives = ({ activeSlide, competitionId }: AlternativeProps) => {
+const MultipleChoiceAlternatives = ({ activeSlide, competitionId }: MultipleChoiceAlternativeProps) => {
   const dispatch = useAppDispatch()
-  const competition = useAppSelector((state) => state.editor.competition)
   const activeSlideId = useAppSelector((state) => state.editor.activeSlideId)
   const GreenCheckbox = withStyles({
     root: {
@@ -95,42 +94,40 @@ const Alternatives = ({ activeSlide, competitionId }: AlternativeProps) => {
 
   return (
     <SettingsList>
-      <WhiteBackground>
-        <ListItem divider>
-          <Center>
-            <ListItemText
-              primary="Svarsalternativ"
-              secondary="(Fyll i rutan höger om textfältet för att markera korrekt svar)"
-            />
-          </Center>
-        </ListItem>
-        {activeSlide &&
-          activeSlide.questions[0] &&
-          activeSlide.questions[0].alternatives &&
-          activeSlide.questions[0].alternatives.map((alt) => (
-            <div key={alt.id}>
-              <ListItem divider>
-                <TextInput
-                  id="outlined-basic"
-                  defaultValue={alt.text}
-                  onChange={(event) => updateAlternativeText(alt.id, event.target.value)}
-                  variant="outlined"
-                />
-                <GreenCheckbox checked={numberToBool(alt.value)} onChange={() => updateAlternativeValue(alt)} />
-                <Clickable>
-                  <CloseIcon onClick={() => handleCloseAnswerClick(alt.id)} />
-                </Clickable>
-              </ListItem>
-            </div>
-          ))}
-        <ListItem button onClick={addAlternative}>
-          <Center>
-            <AddButton variant="button">Lägg till svarsalternativ</AddButton>
-          </Center>
-        </ListItem>
-      </WhiteBackground>
+      <ListItem divider>
+        <Center>
+          <ListItemText
+            primary="Svarsalternativ"
+            secondary="(Fyll i rutan höger om textfältet för att markera korrekt svar)"
+          />
+        </Center>
+      </ListItem>
+      {activeSlide &&
+        activeSlide.questions[0] &&
+        activeSlide.questions[0].alternatives &&
+        activeSlide.questions[0].alternatives.map((alt) => (
+          <div key={alt.id}>
+            <ListItem divider>
+              <AlternativeTextField
+                id="outlined-basic"
+                defaultValue={alt.text}
+                onChange={(event) => updateAlternativeText(alt.id, event.target.value)}
+                variant="outlined"
+              />
+              <GreenCheckbox checked={numberToBool(alt.value)} onChange={() => updateAlternativeValue(alt)} />
+              <Clickable>
+                <CloseIcon onClick={() => handleCloseAnswerClick(alt.id)} />
+              </Clickable>
+            </ListItem>
+          </div>
+        ))}
+      <ListItem button onClick={addAlternative}>
+        <Center>
+          <AddButton variant="button">Lägg till svarsalternativ</AddButton>
+        </Center>
+      </ListItem>
     </SettingsList>
   )
 }
 
-export default Alternatives
+export default MultipleChoiceAlternatives
diff --git a/client/src/pages/presentationEditor/components/slideSettingsComponents/QuestionSettings.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/QuestionSettings.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2bc10b3588133245d54767dbbc7d8381e4134b59
--- /dev/null
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/QuestionSettings.tsx
@@ -0,0 +1,87 @@
+import { ListItem, ListItemText, TextField } from '@material-ui/core'
+import axios from 'axios'
+import React, { useEffect, useState } from 'react'
+import { getEditorCompetition } from '../../../../actions/editor'
+import { useAppDispatch } from '../../../../hooks'
+import { RichSlide } from '../../../../interfaces/ApiRichModels'
+import { Center, SettingsList } from '../styled'
+
+type QuestionSettingsProps = {
+  activeSlide: RichSlide
+  competitionId: string
+}
+
+const QuestionSettings = ({ activeSlide, competitionId }: QuestionSettingsProps) => {
+  const dispatch = useAppDispatch()
+
+  const updateQuestion = async (
+    updateTitle: boolean,
+    event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
+  ) => {
+    console.log('Content was updated on server. id: ', activeSlide.questions[0].id)
+    if (activeSlide && activeSlide.questions[0]) {
+      if (updateTitle) {
+        await axios
+          .put(`/api/competitions/${competitionId}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}`, {
+            name: event.target.value,
+          })
+          .then(() => {
+            dispatch(getEditorCompetition(competitionId))
+          })
+          .catch(console.log)
+      } else {
+        setScore(+event.target.value)
+        await axios
+          .put(`/api/competitions/${competitionId}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}`, {
+            total_score: event.target.value,
+          })
+          .then(() => {
+            dispatch(getEditorCompetition(competitionId))
+          })
+          .catch(console.log)
+      }
+    }
+  }
+
+  const [score, setScore] = useState<number | undefined>(0)
+  useEffect(() => {
+    setScore(activeSlide?.questions[0]?.total_score)
+  }, [activeSlide])
+
+  return (
+    <SettingsList>
+      <ListItem divider>
+        <Center>
+          <ListItemText primary="Frågeinställningar" secondary="" />
+        </Center>
+      </ListItem>
+      <ListItem divider>
+        <TextField
+          id="outlined-basic"
+          defaultValue={''}
+          label="Frågans titel"
+          onChange={(event) => updateQuestion(true, event)}
+          variant="outlined"
+          fullWidth={true}
+        />
+      </ListItem>
+      <ListItem>
+        <Center>
+          <TextField
+            fullWidth={true}
+            variant="outlined"
+            placeholder="Antal poäng"
+            helperText="Välj hur många poäng frågan ska ge för rätt svar."
+            label="Poäng"
+            type="number"
+            InputProps={{ inputProps: { min: 0 } }}
+            value={score}
+            onChange={(event) => updateQuestion(false, event)}
+          />
+        </Center>
+      </ListItem>
+    </SettingsList>
+  )
+}
+
+export default QuestionSettings
diff --git a/client/src/pages/presentationEditor/components/SlideType.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/SlideType.tsx
similarity index 55%
rename from client/src/pages/presentationEditor/components/SlideType.tsx
rename to client/src/pages/presentationEditor/components/slideSettingsComponents/SlideType.tsx
index ba104fe6ce7088d6aec076359d9f3f1361de3262..bc251b91e092c05457db1b64bb8bbe566d76dd55 100644
--- a/client/src/pages/presentationEditor/components/SlideType.tsx
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/SlideType.tsx
@@ -5,6 +5,7 @@ import {
   DialogContent,
   DialogContentText,
   DialogTitle,
+  FormControl,
   InputLabel,
   ListItem,
   MenuItem,
@@ -13,10 +14,10 @@ import {
 } from '@material-ui/core'
 import axios from 'axios'
 import React, { useState } from 'react'
-import { getEditorCompetition } from '../../../actions/editor'
-import { useAppDispatch } from '../../../hooks'
-import { RichSlide } from '../../../interfaces/ApiRichModels'
-import { Center, FormControlDropdown, SlideTypeInputLabel, WhiteBackground } from './styled'
+import { getEditorCompetition } from '../../../../actions/editor'
+import { useAppDispatch } from '../../../../hooks'
+import { RichSlide } from '../../../../interfaces/ApiRichModels'
+import { Center, FirstItem } from '../styled'
 
 type SlideTypeProps = {
   activeSlide: RichSlide
@@ -85,52 +86,55 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => {
     }
   }
   return (
-    <WhiteBackground>
-      <FormControlDropdown variant="outlined">
-        <SlideTypeInputLabel>Sidtyp</SlideTypeInputLabel>
-        <Select fullWidth={true} value={activeSlide?.questions[0]?.type_id || 0} label="Sidtyp">
-          <MenuItem value={0}>
-            <Typography variant="button" onClick={() => openSlideTypeDialog(0)}>
-              Informationssida
-            </Typography>
-          </MenuItem>
-          <MenuItem value={1}>
-            <Typography variant="button" onClick={() => openSlideTypeDialog(1)}>
-              Skriftlig fråga
-            </Typography>
-          </MenuItem>
-          <MenuItem value={2}>
-            <Typography variant="button" onClick={() => openSlideTypeDialog(2)}>
-              Praktisk fråga
-            </Typography>
-          </MenuItem>
-          <MenuItem value={3}>
-            <Typography variant="button" onClick={() => openSlideTypeDialog(3)}>
-              Flervalsfråga
-            </Typography>
-          </MenuItem>
-        </Select>
-      </FormControlDropdown>
-      <Dialog open={slideTypeDialog} onClose={closeSlideTypeDialog}>
-        <Center>
-          <DialogTitle color="secondary">Varning!</DialogTitle>
-        </Center>
-        <DialogContent>
-          <DialogContentText>
-            Om du ändrar sidtypen kommer eventuella frågeinställningar gå förlorade. Det inkluderar: frågans namn, poäng
-            och svarsalternativ.{' '}
-          </DialogContentText>
-        </DialogContent>
-        <DialogActions>
-          <Button onClick={closeSlideTypeDialog} color="secondary">
-            Avbryt
-          </Button>
-          <Button onClick={updateSlideType} color="primary">
-            Bekräfta
-          </Button>
-        </DialogActions>
-      </Dialog>
-    </WhiteBackground>
+    <FirstItem>
+      <ListItem>
+        <FormControl fullWidth variant="outlined">
+          <InputLabel>Sidtyp</InputLabel>
+          <Select fullWidth={true} value={activeSlide?.questions[0]?.type_id || 0} label="Sidtyp">
+            <MenuItem value={0}>
+              <Typography variant="button" onClick={() => openSlideTypeDialog(0)}>
+                Informationssida
+              </Typography>
+            </MenuItem>
+            <MenuItem value={1}>
+              <Typography variant="button" onClick={() => openSlideTypeDialog(1)}>
+                Skriftlig fråga
+              </Typography>
+            </MenuItem>
+            <MenuItem value={2}>
+              <Typography variant="button" onClick={() => openSlideTypeDialog(2)}>
+                Praktisk fråga
+              </Typography>
+            </MenuItem>
+            <MenuItem value={3}>
+              <Typography variant="button" onClick={() => openSlideTypeDialog(3)}>
+                Flervalsfråga
+              </Typography>
+            </MenuItem>
+          </Select>
+        </FormControl>
+
+        <Dialog open={slideTypeDialog} onClose={closeSlideTypeDialog}>
+          <Center>
+            <DialogTitle color="secondary">Varning!</DialogTitle>
+          </Center>
+          <DialogContent>
+            <DialogContentText>
+              Om du ändrar sidtypen kommer eventuella frågeinställningar gå förlorade. Det inkluderar: frågans namn,
+              poäng och svarsalternativ.{' '}
+            </DialogContentText>
+          </DialogContent>
+          <DialogActions>
+            <Button onClick={closeSlideTypeDialog} color="secondary">
+              Avbryt
+            </Button>
+            <Button onClick={updateSlideType} color="primary">
+              Bekräfta
+            </Button>
+          </DialogActions>
+        </Dialog>
+      </ListItem>
+    </FirstItem>
   )
 }
 
diff --git a/client/src/pages/presentationEditor/components/Texts.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/Texts.tsx
similarity index 57%
rename from client/src/pages/presentationEditor/components/Texts.tsx
rename to client/src/pages/presentationEditor/components/slideSettingsComponents/Texts.tsx
index 31ecf57cd29325972328647290fc74de7c9827bd..03656614e3076e6048984e70b7ddd0ae711a492d 100644
--- a/client/src/pages/presentationEditor/components/Texts.tsx
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/Texts.tsx
@@ -1,20 +1,20 @@
 import { Divider, ListItem, ListItemText, Typography } from '@material-ui/core'
 import React from 'react'
-import { useAppSelector } from '../../../hooks'
-import { TextComponent } from '../../../interfaces/ApiModels'
-import { RichSlide } from '../../../interfaces/ApiRichModels'
-import { AddButton, Center, SettingsList, TextCard } from './styled'
-import TextComponentEdit from './TextComponentEdit'
+import { useAppDispatch, useAppSelector } from '../../../../hooks'
+import { TextComponent } from '../../../../interfaces/ApiModels'
+import { RichSlide } from '../../../../interfaces/ApiRichModels'
+import { AddButton, Center, SettingsList, TextCard } from '../styled'
+import TextComponentEdit from '../TextComponentEdit'
 import axios from 'axios'
-import { getEditorCompetition } from '../../../actions/editor'
-import { useDispatch } from 'react-redux'
+import { getEditorCompetition } from '../../../../actions/editor'
 
 type TextsProps = {
+  activeViewTypeId: number
   activeSlide: RichSlide
   competitionId: string
 }
 
-const Texts = ({ activeSlide, competitionId }: TextsProps) => {
+const Texts = ({ activeViewTypeId, activeSlide, competitionId }: TextsProps) => {
   const texts = useAppSelector(
     (state) =>
       state.editor.competition.slides
@@ -22,7 +22,7 @@ const Texts = ({ activeSlide, competitionId }: TextsProps) => {
         ?.components.filter((component) => component.type_id === 1) as TextComponent[]
   )
 
-  const dispatch = useDispatch()
+  const dispatch = useAppDispatch()
   const handleAddText = async () => {
     if (activeSlide) {
       await axios.post(`/api/competitions/${competitionId}/slides/${activeSlide?.id}/components`, {
@@ -30,6 +30,7 @@ const Texts = ({ activeSlide, competitionId }: TextsProps) => {
         text: 'Ny text',
         w: 315,
         h: 50,
+        view_type_id: activeViewTypeId,
       })
       dispatch(getEditorCompetition(competitionId))
     }
@@ -43,12 +44,14 @@ const Texts = ({ activeSlide, competitionId }: TextsProps) => {
         </Center>
       </ListItem>
       {texts &&
-        texts.map((text) => (
-          <TextCard elevation={4} key={text.id}>
-            <TextComponentEdit component={text} />
-            <Divider />
-          </TextCard>
-        ))}
+        texts
+          .filter((text) => text.view_type_id === activeViewTypeId)
+          .map((text) => (
+            <TextCard elevation={4} key={text.id}>
+              <TextComponentEdit component={text} />
+              <Divider />
+            </TextCard>
+          ))}
       <ListItem button onClick={handleAddText}>
         <Center>
           <AddButton variant="button">Lägg till text</AddButton>
diff --git a/client/src/pages/presentationEditor/components/Timer.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/Timer.tsx
similarity index 50%
rename from client/src/pages/presentationEditor/components/Timer.tsx
rename to client/src/pages/presentationEditor/components/slideSettingsComponents/Timer.tsx
index 124635f423df9d0bfcca20a9a02309c646cb3dca..91825662dbc65ec851c093ddf2aa48ddb98ce404 100644
--- a/client/src/pages/presentationEditor/components/Timer.tsx
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/Timer.tsx
@@ -1,10 +1,10 @@
 import { ListItem, TextField } from '@material-ui/core'
 import axios from 'axios'
 import React, { useEffect, useState } from 'react'
-import { getEditorCompetition } from '../../../actions/editor'
-import { useAppDispatch } from '../../../hooks'
-import { RichSlide } from '../../../interfaces/ApiRichModels'
-import { Center, WhiteBackground } from './styled'
+import { getEditorCompetition } from '../../../../actions/editor'
+import { useAppDispatch } from '../../../../hooks'
+import { RichSlide } from '../../../../interfaces/ApiRichModels'
+import { Center } from '../styled'
 
 type TimerProps = {
   activeSlide: RichSlide
@@ -24,29 +24,26 @@ const Timer = ({ activeSlide, competitionId }: TimerProps) => {
         .catch(console.log)
     }
   }
-  const [timer, setTimer] = useState<number | undefined>(0)
+  const [timer, setTimer] = useState<number | undefined>(activeSlide?.timer)
   useEffect(() => {
     setTimer(activeSlide?.timer)
   }, [activeSlide])
   return (
-    <WhiteBackground>
-      <ListItem>
-        <Center>
-          <TextField
-            id="standard-number"
-            fullWidth={true}
-            variant="outlined"
-            placeholder="Antal sekunder"
-            helperText="Lämna blank för att inte använda timerfunktionen"
-            label="Timer"
-            type="number"
-            defaultValue={activeSlide?.timer || 0}
-            onChange={updateTimer}
-            value={timer}
-          />
-        </Center>
-      </ListItem>
-    </WhiteBackground>
+    <ListItem>
+      <Center>
+        <TextField
+          id="standard-number"
+          fullWidth={true}
+          variant="outlined"
+          placeholder="Antal sekunder"
+          helperText="Lämna blank för att inte använda timerfunktionen"
+          label="Timer"
+          type="number"
+          onChange={updateTimer}
+          value={timer}
+        />
+      </Center>
+    </ListItem>
   )
 }
 
diff --git a/client/src/pages/presentationEditor/components/styled.tsx b/client/src/pages/presentationEditor/components/styled.tsx
index 2a3959c7edee69c190215af53b91c4ae7f2aebf9..605972d2c4ee798388f9437f373cbabeab777d3c 100644
--- a/client/src/pages/presentationEditor/components/styled.tsx
+++ b/client/src/pages/presentationEditor/components/styled.tsx
@@ -9,6 +9,7 @@ import {
   ListItem,
   Select,
   InputLabel,
+  ListItemText,
 } from '@material-ui/core'
 import styled from 'styled-components'
 
@@ -18,17 +19,15 @@ export const SettingsTab = styled(Tab)`
 `
 
 export const SlideEditorContainer = styled.div`
+  overflow: hidden;
   height: 100%;
   display: flex;
   align-items: center;
   justify-content: center;
-  background-color: rgba(0, 0, 0, 0.08);
 `
 
 export const SlideEditorContainerRatio = styled.div`
-  padding-top: 56.25%;
   width: 100%;
-  height: 0;
   overflow: hidden;
   padding-top: 56.25%;
   position: relative;
@@ -56,30 +55,15 @@ export const ToolbarPadding = styled.div`
   padding-top: 55px;
 `
 
-export const FormControlDropdown = styled(FormControl)`
-  width: 100%;
-  margin-top: 10px;
-  padding: 8px;
-  padding-left: 16px;
-  padding-right: 16px;
-`
-
-export const SlideTypeInputLabel = styled(InputLabel)`
+export const FirstItem = styled.div`
   width: 100%;
-  padding: 10px;
-  padding-left: 22px;
+  padding-top: 10px;
 `
 
-export const TextInput = styled(TextField)`
+export const AlternativeTextField = styled(TextField)`
   width: 87%;
 `
 
-export const NoPadding = styled.div`
-  padding: 0;
-  height: 100%;
-  width: 100%;
-`
-
 export const Center = styled.div`
   display: flex;
   justify-content: center;
@@ -88,20 +72,21 @@ export const Center = styled.div`
   width: 100%;
 `
 
-export const SlidePanel = styled.div`
-  padding: 10px;
+export const ImageTextContainer = styled.div`
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
   width: 100%;
 `
 
-export const WhiteBackground = styled.div`
-  background: white;
+export const PanelContainer = styled.div`
+  padding: 10px;
+  width: 100%;
 `
 
 export const AddButton = styled(Typography)`
-  padding-left: 8px;
-  padding-right: 8px;
-  padding-top: 7px;
-  padding-bottom: 7px;
+  padding: 7px 8px 7px 8px;
 `
 
 export const ImportedImage = styled.img`
@@ -114,8 +99,17 @@ export const Clickable = styled.div`
 `
 
 export const AddImageButton = styled.label`
-  padding: 0;
-  cursor: 'pointer';
+  padding: 8px 13px 8px 13px;
+  display: flex;
+  justify-content: center;
+  text-align: center;
+  height: 100%;
+  width: 100%;
+  cursor: pointer;
+`
+
+export const AddBackgroundButton = styled.label`
+  padding: 16px 29px 16px 29px;
   display: flex;
   justify-content: center;
   text-align: center;
@@ -150,3 +144,7 @@ export const HoverContainer = styled.div<HoverContainerProps>`
   padding: ${(props) => (props.hover ? 0 : 1)}px;
   border: solid ${(props) => (props.hover ? 1 : 0)}px;
 `
+
+export const ImageNameText = styled(ListItemText)`
+  word-break: break-all;
+`
diff --git a/client/src/pages/presentationEditor/styled.tsx b/client/src/pages/presentationEditor/styled.tsx
index d1f05d6bf18de2eccc6b486fb6e94701acce3b5c..c02054090f9b597fc32f1cacc03d145f3b77d9fe 100644
--- a/client/src/pages/presentationEditor/styled.tsx
+++ b/client/src/pages/presentationEditor/styled.tsx
@@ -1,14 +1,32 @@
-import { Button, List, ListItem, Toolbar } from '@material-ui/core'
+import { AppBar, Button, Drawer, List, ListItem, Toolbar, Typography } from '@material-ui/core'
 import styled from 'styled-components'
 
+interface ViewButtonProps {
+  $activeView: boolean
+}
+
+interface DrawerSizeProps {
+  leftDrawerWidth: number | undefined
+  rightDrawerWidth: number | undefined
+}
+
+const AppBarHeight = 64
+const SlideListHeight = 60
+
 export const ToolBarContainer = styled(Toolbar)`
   display: flex;
   justify-content: space-between;
   padding-left: 0;
 `
 
-export const ViewButton = styled(Button)`
+export const ViewButton = styled(Button)<ViewButtonProps>`
+  margin-right: 8px;
+  background: ${(props) => (props.$activeView ? '#5a0017' : undefined)};
+`
+
+export const ViewButtonClicked = styled(Button)`
   margin-right: 8px;
+  background: #5a0017;
 `
 
 export const ViewButtonGroup = styled.div`
@@ -17,16 +35,19 @@ export const ViewButtonGroup = styled.div`
 `
 
 export const SlideList = styled(List)`
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-  justify-content: space-between;
+  height: calc(100% - ${SlideListHeight}px);
+  padding: 0px;
+  overflow-y: auto;
+`
+
+export const RightPanelScroll = styled(List)`
   padding: 0px;
+  overflow-y: auto;
 `
 
 export const SlideListItem = styled(ListItem)`
   text-align: center;
-  height: 60px;
+  height: ${SlideListHeight}px;
 `
 
 export const PresentationEditorContainer = styled.div`
@@ -41,5 +62,54 @@ export const CenteredSpinnerContainer = styled.div`
 `
 
 export const HomeIcon = styled.img`
-  height: 64px;
+  height: ${AppBarHeight}px;
+`
+
+export const LeftDrawer = styled(Drawer)<DrawerSizeProps>`
+  width: ${(props) => (props ? props.leftDrawerWidth : 0)}px;
+  flex-shrink: 0;
+  position: relative;
+  z-index: 1;
+`
+
+export const RightDrawer = styled(Drawer)<DrawerSizeProps>`
+  width: ${(props) => (props ? props.rightDrawerWidth : 0)}px;
+  flex-shrink: 0;
+`
+
+export const AppBarEditor = styled(AppBar)<DrawerSizeProps>`
+  width: calc(100% - ${(props) => (props ? props.rightDrawerWidth : 0)}px);
+  left: 0;
+  margin-left: leftDrawerWidth;
+  margin-right: rightDrawerWidth;
+`
+
+// Necessary for content to be below app bar
+export const ToolbarMargin = styled.div`
+  padding-top: ${AppBarHeight}px;
+`
+
+export const FillLeftContainer = styled.div<DrawerSizeProps>`
+  width: ${(props) => (props ? props.leftDrawerWidth : 0)}px;
+  height: calc(100% - ${SlideListHeight}px);
+  overflow: hidden;
+`
+
+export const FillRightContainer = styled.div<DrawerSizeProps>`
+  width: ${(props) => (props ? props.rightDrawerWidth : 0)}px;
+  height: 100%;
+  overflow-y: auto;
+  background: #e9e9e9;
+`
+
+export const PositionBottom = styled.div`
+  position: absolute;
+  bottom: 0;
+  width: 100%;
+`
+
+export const CompetitionName = styled(Typography)`
+  text-decoration: none;
+  position: absolute;
+  left: 180px;
 `
diff --git a/client/src/pages/views/AudienceViewPage.tsx b/client/src/pages/views/AudienceViewPage.tsx
index 00a821f35a4ad05354954fdb50694012cb2bda46..d03f3367499b9820ad35d646feedda3821928dcc 100644
--- a/client/src/pages/views/AudienceViewPage.tsx
+++ b/client/src/pages/views/AudienceViewPage.tsx
@@ -1,8 +1,15 @@
+import { Typography } from '@material-ui/core'
 import React from 'react'
-import SlideDisplay from './components/SlideDisplay'
+import { useAppSelector } from '../../hooks'
+import SlideDisplay from '../presentationEditor/components/SlideDisplay'
 
 const AudienceViewPage: React.FC = () => {
-  return <SlideDisplay />
+  const viewTypes = useAppSelector((state) => state.types.viewTypes)
+  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Audience')?.id
+  if (activeViewTypeId) {
+    return <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />
+  }
+  return <Typography>Error: Åskådarvyn kunde inte laddas</Typography>
 }
 
 export default AudienceViewPage
diff --git a/client/src/pages/views/JudgeViewPage.test.tsx b/client/src/pages/views/JudgeViewPage.test.tsx
index 537dae4c570f3b7ed50dadaa9f60bae1be8823a2..2e15f0904705726d364672b87af478ddaf922fbb 100644
--- a/client/src/pages/views/JudgeViewPage.test.tsx
+++ b/client/src/pages/views/JudgeViewPage.test.tsx
@@ -9,7 +9,7 @@ import JudgeViewPage from './JudgeViewPage'
 it('renders judge view page', () => {
   const compRes: any = {
     data: {
-      slides: [{ id: 0, title: '' }],
+      slides: [{ id: 0, title: '', questions: [{ id: 0 }] }],
     },
   }
   const teamsRes: any = {
@@ -36,7 +36,7 @@ it('renders judge view page', () => {
   render(
     <BrowserRouter>
       <Provider store={store}>
-        <JudgeViewPage />
+        <JudgeViewPage code={''} competitionId={0} />
       </Provider>
     </BrowserRouter>
   )
diff --git a/client/src/pages/views/JudgeViewPage.tsx b/client/src/pages/views/JudgeViewPage.tsx
index 66450f3a1f8ac98f14423d7040e215c0c96881c6..677a080d04ab34e8232120daae018b6d1a2b29c4 100644
--- a/client/src/pages/views/JudgeViewPage.tsx
+++ b/client/src/pages/views/JudgeViewPage.tsx
@@ -1,32 +1,33 @@
-import { Divider, List, ListItemText, Typography } from '@material-ui/core'
+import { Card, Divider, List, ListItem, ListItemText, Paper, 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'
-import {
-  getPresentationCompetition,
-  getPresentationTeams,
-  setCurrentSlide,
-  setPresentationCode,
-} from '../../actions/presentation'
+import { getPresentationCompetition, setCurrentSlide, setPresentationCode } 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'
+import PresentationComponent from './components/PresentationComponent'
 import { useHistory } from 'react-router-dom'
 import {
   Content,
+  InnerContent,
   JudgeAnswersLabel,
   JudgeAppBar,
   JudgeQuestionsLabel,
   JudgeToolbar,
   LeftDrawer,
   RightDrawer,
+  ScoreHeaderPadding,
+  ScoreHeaderPaper,
+  ScoreFooterPadding,
 } from './styled'
+import SlideDisplay from '../presentationEditor/components/SlideDisplay'
+import JudgeScoringInstructions from './components/JudgeScoringInstructions'
+import { renderSlideIcon } from '../../utils/renderSlideIcon'
 
 const leftDrawerWidth = 150
-const rightDrawerWidth = 390
+const rightDrawerWidth = 700
 
 const useStyles = makeStyles((theme: Theme) =>
   createStyles({
@@ -39,30 +40,36 @@ const useStyles = makeStyles((theme: Theme) =>
     toolbar: theme.mixins.toolbar,
   })
 )
+type JudgeViewPageProps = {
+  //Prop to distinguish between editor and active competition
+  competitionId: number
+  code: string
+}
 
-const JudgeViewPage: React.FC = () => {
+const JudgeViewPage = ({ competitionId, code }: JudgeViewPageProps) => {
   const classes = useStyles()
   const history = useHistory()
-  const { id, code }: ViewParams = useParams()
   const dispatch = useAppDispatch()
   const [activeSlideIndex, setActiveSlideIndex] = useState<number>(0)
-  const teams = useAppSelector((state) => state.presentation.teams)
+  const viewTypes = useAppSelector((state) => state.types.viewTypes)
+  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Judge')?.id
+  const teams = useAppSelector((state) => state.presentation.competition.teams)
   const slides = useAppSelector((state) => state.presentation.competition.slides)
+  const currentQuestion = slides[activeSlideIndex]?.questions[0]
   const handleSelectSlide = (index: number) => {
     setActiveSlideIndex(index)
     dispatch(setCurrentSlide(slides[index]))
   }
   useEffect(() => {
     socket_connect()
-    dispatch(getPresentationCompetition(id))
-    dispatch(getPresentationTeams(id))
+    dispatch(getPresentationCompetition(competitionId.toString()))
     dispatch(setPresentationCode(code))
     //hides the url so people can't sneak peak
     history.push('judge')
   }, [])
 
   return (
-    <div>
+    <div style={{ height: '100%' }}>
       <JudgeAppBar position="fixed">
         <JudgeToolbar>
           <JudgeQuestionsLabel variant="h5">Frågor</JudgeQuestionsLabel>
@@ -87,8 +94,8 @@ const JudgeViewPage: React.FC = () => {
               button
               key={slide.id}
             >
-              <Typography variant="h6">Slide ID: {slide.id} </Typography>
-              <ListItemText primary={slide.title} />
+              {renderSlideIcon(slide)}
+              <ListItemText primary={`Sida ${slide.order + 1}`} />
             </SlideListItem>
           ))}
         </List>
@@ -102,18 +109,29 @@ const JudgeViewPage: React.FC = () => {
         anchor="right"
       >
         <div className={classes.toolbar} />
-        <List>
-          {teams.map((answer, index) => (
-            <div key={answer.name}>
-              <JudgeScoreDisplay teamIndex={index} />
-              <Divider />
-            </div>
-          ))}
+        {currentQuestion && (
+          <ScoreHeaderPaper $rightDrawerWidth={rightDrawerWidth} elevation={4}>
+            <Typography variant="h4">{`${currentQuestion.name} (${currentQuestion.total_score}p)`}</Typography>
+          </ScoreHeaderPaper>
+        )}
+        <ScoreHeaderPadding />
+        <List style={{ overflowY: 'scroll', overflowX: 'hidden' }}>
+          {teams &&
+            teams.map((answer, index) => (
+              <div key={answer.name}>
+                <JudgeScoreDisplay teamIndex={index} />
+                <Divider />
+              </div>
+            ))}
         </List>
+        <ScoreFooterPadding />
+        <JudgeScoringInstructions question={currentQuestion} />
       </RightDrawer>
+      <div className={classes.toolbar} />
       <Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}>
-        <div className={classes.toolbar} />
-        <SlideDisplay />
+        <InnerContent>
+          {activeViewTypeId && <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />}
+        </InnerContent>
       </Content>
     </div>
   )
diff --git a/client/src/pages/views/PresenterViewPage.test.tsx b/client/src/pages/views/OperatorViewPage.test.tsx
similarity index 91%
rename from client/src/pages/views/PresenterViewPage.test.tsx
rename to client/src/pages/views/OperatorViewPage.test.tsx
index fd7b0a9692e08354330b3e4db98c046649c0d10a..e658fe3b386899b9ab2972243c5d4ceeefc6dec4 100644
--- a/client/src/pages/views/PresenterViewPage.test.tsx
+++ b/client/src/pages/views/OperatorViewPage.test.tsx
@@ -4,7 +4,7 @@ import React from 'react'
 import { Provider } from 'react-redux'
 import { BrowserRouter } from 'react-router-dom'
 import store from '../../store'
-import PresenterViewPage from './PresenterViewPage'
+import OperatorViewPage from './OperatorViewPage'
 
 it('renders presenter view page', () => {
   const compRes: any = {
@@ -36,7 +36,7 @@ it('renders presenter view page', () => {
   render(
     <BrowserRouter>
       <Provider store={store}>
-        <PresenterViewPage />
+        <OperatorViewPage />
       </Provider>
     </BrowserRouter>
   )
diff --git a/client/src/pages/views/OperatorViewPage.tsx b/client/src/pages/views/OperatorViewPage.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e965a3ec9f60b77b1919525f75ba2a925a81e84b
--- /dev/null
+++ b/client/src/pages/views/OperatorViewPage.tsx
@@ -0,0 +1,317 @@
+import {
+  Button,
+  Dialog,
+  DialogActions,
+  DialogContent,
+  DialogContentText,
+  DialogTitle,
+  List,
+  ListItem,
+  ListItemText,
+  Popover,
+  Tooltip,
+  Typography,
+  useMediaQuery,
+  useTheme,
+} from '@material-ui/core'
+import AssignmentIcon from '@material-ui/icons/Assignment'
+import FileCopyIcon from '@material-ui/icons/FileCopy'
+import SupervisorAccountIcon from '@material-ui/icons/SupervisorAccount'
+import BackspaceIcon from '@material-ui/icons/Backspace'
+import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'
+import ChevronRightIcon from '@material-ui/icons/ChevronRight'
+import TimerIcon from '@material-ui/icons/Timer'
+import React, { useEffect, useState } from 'react'
+import { useHistory, useParams } from 'react-router-dom'
+import { getPresentationCompetition, 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 '../presentationEditor/components/SlideDisplay'
+import PresentationComponent from './components/PresentationComponent'
+import Timer from './components/Timer'
+import {
+  OperatorButton,
+  OperatorContainer,
+  OperatorFooter,
+  OperatorHeader,
+  OperatorContent,
+  OperatorInnerContent,
+  SlideCounter,
+  ToolBarContainer,
+} from './styled'
+
+/**
+ *  Description:
+ *
+ *  Presentation is an active competition
+ *
+ *
+ *  ===========================================
+ *  TODO:
+ *  - Instead of copying code for others to join the competition, copy URL.
+ *
+ *  - Make code popup less code by using .map instead
+ *
+ *  - Fix scoreboard
+ *
+ *  - When two userers are connected to the same Localhost:5000 and updates/starts/end competition it
+ *    creates a bug where the competition can't be started.
+ * ===========================================
+ */
+
+const OperatorViewPage: React.FC = () => {
+  // for dialog alert
+  const [openAlert, setOpen] = React.useState(false)
+  const [openAlertCode, setOpenCode] = React.useState(true)
+  const theme = useTheme()
+  const fullScreen = useMediaQuery(theme.breakpoints.down('sm'))
+  const teams = useAppSelector((state) => state.presentation.competition.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()
+  const viewTypes = useAppSelector((state) => state.types.viewTypes)
+  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Operator')?.id
+
+  useEffect(() => {
+    dispatch(getPresentationCompetition(id))
+    dispatch(setPresentationCode(code))
+    socket_connect()
+    socketSetSlide // Behövs denna?
+    setTimeout(startCompetition, 1000) // Ghetto, wait for everything to load
+    // console.log(id)
+  }, [])
+
+  window.onpopstate = () => {
+    //Handle browser back arrow
+    alert('Tävlingen avslutas för alla')
+    endCompetition()
+  }
+
+  const handleOpenPopover = (event: React.MouseEvent<HTMLButtonElement>) => {
+    setAnchorEl(event.currentTarget)
+  }
+
+  const handleClose = () => {
+    setOpen(false)
+    setOpenCode(false)
+    setAnchorEl(null)
+  }
+
+  const startCompetition = () => {
+    socketStartPresentation()
+    console.log('started competition for')
+    console.log(id)
+  }
+
+  const handleVerifyExit = () => {
+    setOpen(true)
+  }
+
+  const handleOpenCodes = () => {
+    setOpenCode(true)
+  }
+
+  const handleCopy = () => {
+    console.log('copied code to clipboard')
+  }
+
+  const endCompetition = () => {
+    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 (
+    <OperatorContainer>
+      <Dialog
+        fullScreen={fullScreen}
+        open={openAlertCode}
+        onClose={handleClose}
+        aria-labelledby="responsive-dialog-title"
+      >
+        <DialogTitle id="responsive-dialog-title">{'Koder för tävlingen'}</DialogTitle>
+        <DialogContent>
+          <ListItem>
+            <ListItemText primary={`Domare: ${presentation.code}`} />
+            <Tooltip title="Kopiera kod" arrow>
+              <Button
+                onClick={() => {
+                  navigator.clipboard.writeText(presentation.code)
+                }}
+              >
+                <FileCopyIcon fontSize="small" />
+              </Button>
+            </Tooltip>
+          </ListItem>
+          <ListItem>
+            <ListItemText primary={`Tävlande: ${presentation.code}`} />
+            <Tooltip title="Kopiera kod" arrow>
+              <Button
+                onClick={() => {
+                  navigator.clipboard.writeText(presentation.code)
+                }}
+              >
+                <FileCopyIcon fontSize="small" />
+              </Button>
+            </Tooltip>
+          </ListItem>
+          <ListItem>
+            <ListItemText primary={`Publik: ${presentation.code}`} />
+            <Tooltip title="Kopiera kod" arrow>
+              <Button
+                onClick={() => {
+                  navigator.clipboard.writeText(presentation.code)
+                }}
+              >
+                <FileCopyIcon fontSize="small" />
+              </Button>
+            </Tooltip>
+          </ListItem>
+        </DialogContent>
+        <DialogActions>
+          <Button autoFocus onClick={handleClose} color="primary">
+            Ok
+          </Button>
+        </DialogActions>
+      </Dialog>
+
+      <OperatorHeader>
+        <Tooltip title="Avsluta tävling" arrow>
+          <OperatorButton onClick={handleVerifyExit} variant="contained" color="secondary">
+            <BackspaceIcon fontSize="large" />
+          </OperatorButton>
+        </Tooltip>
+
+        <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>
+      </OperatorHeader>
+      <div style={{ height: 0, paddingTop: 120 }} />
+      <OperatorContent>
+        <OperatorInnerContent>
+          {activeViewTypeId && <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />}
+        </OperatorInnerContent>
+      </OperatorContent>
+      <div style={{ height: 0, paddingTop: 140 }} />
+      <OperatorFooter>
+        <ToolBarContainer>
+          <Tooltip title="Föregående" arrow>
+            <OperatorButton onClick={socketSetSlidePrev} variant="contained">
+              <ChevronLeftIcon fontSize="large" />
+            </OperatorButton>
+          </Tooltip>
+
+          {/* 
+          // Manual start button
+          <Tooltip title="Start Presentation" arrow>
+            <OperatorButton onClick={startCompetition} variant="contained">
+              start
+            </OperatorButton>
+          </Tooltip>
+
+          
+          // This creates a join button, but Operator should not join others, others should join Operator
+          <Tooltip title="Join Presentation" arrow>
+            <OperatorButton onClick={socketJoinPresentation} variant="contained">
+              <GroupAddIcon fontSize="large" />
+            </OperatorButton>
+          </Tooltip>
+          
+
+          // This creates another end button, it might not be needed since we already have one
+          <Tooltip title="End Presentation" arrow>
+            <OperatorButton onClick={socketEndPresentation} variant="contained">
+              <CancelIcon fontSize="large" />
+            </OperatorButton>
+          </Tooltip>
+          */}
+
+          <Tooltip title="Starta Timer" arrow>
+            <OperatorButton onClick={socketStartTimer} variant="contained">
+              <TimerIcon fontSize="large" />
+              <Timer></Timer>
+            </OperatorButton>
+          </Tooltip>
+
+          <Tooltip title="Ställning" arrow>
+            <OperatorButton onClick={handleOpenPopover} variant="contained">
+              <AssignmentIcon fontSize="large" />
+            </OperatorButton>
+          </Tooltip>
+
+          <Tooltip title="Koder" arrow>
+            <OperatorButton onClick={handleOpenCodes} variant="contained">
+              <SupervisorAccountIcon fontSize="large" />
+            </OperatorButton>
+          </Tooltip>
+
+          <Tooltip title="Nästa" arrow>
+            <OperatorButton onClick={socketSetSlideNext} variant="contained">
+              <ChevronRightIcon fontSize="large" />
+            </OperatorButton>
+          </Tooltip>
+        </ToolBarContainer>
+      </OperatorFooter>
+      <Popover
+        open={Boolean(anchorEl)}
+        anchorEl={anchorEl}
+        onClose={handleClose}
+        anchorOrigin={{
+          vertical: 'bottom',
+          horizontal: 'center',
+        }}
+        transformOrigin={{
+          vertical: 'top',
+          horizontal: 'center',
+        }}
+      >
+        <List>
+          {teams &&
+            teams.map((team) => (
+              <ListItem key={team.id}>
+                {team.name} score: {team.question_answers}{' '}
+              </ListItem>
+            ))}
+        </List>
+      </Popover>
+    </OperatorContainer>
+  )
+}
+
+export default OperatorViewPage
diff --git a/client/src/pages/views/ParticipantViewPage.tsx b/client/src/pages/views/ParticipantViewPage.tsx
deleted file mode 100644
index f531ad76db15bc5ba8578b4015e2d5feed8ea1e8..0000000000000000000000000000000000000000
--- a/client/src/pages/views/ParticipantViewPage.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import React, { useEffect } from 'react'
-import SlideDisplay from './components/SlideDisplay'
-import { useHistory } from 'react-router-dom'
-
-const ParticipantViewPage: React.FC = () => {
-  const history = useHistory()
-  useEffect(() => {
-    //hides the url so people can't sneak peak
-    history.push('participant')
-  }, [])
-  return <SlideDisplay />
-}
-
-export default ParticipantViewPage
diff --git a/client/src/pages/views/PresenterViewPage.tsx b/client/src/pages/views/PresenterViewPage.tsx
deleted file mode 100644
index 1abeee92c519c3aae977d667f68470ca78cafe2d..0000000000000000000000000000000000000000
--- a/client/src/pages/views/PresenterViewPage.tsx
+++ /dev/null
@@ -1,218 +0,0 @@
-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 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 Timer from './components/Timer'
-import {
-  PresenterButton,
-  PresenterContainer,
-  PresenterFooter,
-  PresenterHeader,
-  SlideCounter,
-  ToolBarContainer,
-} from './styled'
-
-/**
- *  Presentation is an active competition
- */
-
-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 [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(() => {
-    dispatch(getPresentationCompetition(id))
-    dispatch(getPresentationTeams(id))
-    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>) => {
-    setAnchorEl(event.currentTarget)
-  }
-
-  const handleClose = () => {
-    setOpen(false)
-    setAnchorEl(null)
-  }
-
-  const startCompetition = () => {
-    socketStartPresentation()
-    console.log('started competition for')
-    console.log(id)
-  }
-
-  const handleVerifyExit = () => {
-    setOpen(true)
-  }
-
-  const endCompetition = () => {
-    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 (
-    <PresenterContainer>
-      <PresenterHeader>
-        <Tooltip title="Avsluta tävling" arrow>
-          <PresenterButton onClick={handleVerifyExit} variant="contained" color="secondary">
-            <BackspaceIcon fontSize="large" />
-          </PresenterButton>
-        </Tooltip>
-
-        <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>
-      <SlideDisplay />
-      <PresenterFooter>
-        <ToolBarContainer>
-          <Tooltip title="Previous Slide" arrow>
-            <PresenterButton onClick={socketSetSlidePrev} variant="contained">
-              <ChevronLeftIcon fontSize="large" />
-            </PresenterButton>
-          </Tooltip>
-
-          {/* 
-          // 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>
-      <Popover
-        open={Boolean(anchorEl)}
-        anchorEl={anchorEl}
-        onClose={handleClose}
-        anchorOrigin={{
-          vertical: 'bottom',
-          horizontal: 'center',
-        }}
-        transformOrigin={{
-          vertical: 'top',
-          horizontal: 'center',
-        }}
-      >
-        <List>
-          {/**  TODO:
-           *    Fix scoreboard
-           */}
-          {teams.map((team) => (
-            <ListItem key={team.id}>{team.name} score: 20</ListItem>
-          ))}
-        </List>
-      </Popover>
-    </PresenterContainer>
-  )
-}
-
-export default PresenterViewPage
-function componentDidMount() {
-  throw new Error('Function not implemented.')
-}
diff --git a/client/src/pages/views/ParticipantViewPage.test.tsx b/client/src/pages/views/TeamViewPage.test.tsx
similarity index 59%
rename from client/src/pages/views/ParticipantViewPage.test.tsx
rename to client/src/pages/views/TeamViewPage.test.tsx
index c0950b3c6d3dfeaf1b1ce2d1293829c10651fe33..10574f7e51df7dabf9f07754e9d8595d1c489559 100644
--- a/client/src/pages/views/ParticipantViewPage.test.tsx
+++ b/client/src/pages/views/TeamViewPage.test.tsx
@@ -3,13 +3,20 @@ import React from 'react'
 import { Provider } from 'react-redux'
 import { BrowserRouter } from 'react-router-dom'
 import store from '../../store'
-import ParticipantViewPage from './ParticipantViewPage'
+import TeamViewPage from './TeamViewPage'
+import mockedAxios from 'axios'
 
 it('renders participant view page', () => {
+  const res = {
+    data: {},
+  }
+  ;(mockedAxios.get as jest.Mock).mockImplementation(() => {
+    return Promise.resolve(res)
+  })
   render(
     <BrowserRouter>
       <Provider store={store}>
-        <ParticipantViewPage />
+        <TeamViewPage />
       </Provider>
     </BrowserRouter>
   )
diff --git a/client/src/pages/views/TeamViewPage.tsx b/client/src/pages/views/TeamViewPage.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..32eef28b9f7acd350195571719ea0449aeb19610
--- /dev/null
+++ b/client/src/pages/views/TeamViewPage.tsx
@@ -0,0 +1,29 @@
+import React, { useEffect } from 'react'
+import PresentationComponent from './components/PresentationComponent'
+import { useHistory } from 'react-router-dom'
+import SlideDisplay from '../presentationEditor/components/SlideDisplay'
+import { TeamContainer } from './styled'
+import { socketJoinPresentation, socket_connect } from '../../sockets'
+import { useAppSelector } from '../../hooks'
+
+const TeamViewPage: React.FC = () => {
+  const history = useHistory()
+  const code = useAppSelector((state) => state.presentation.code)
+  const viewTypes = useAppSelector((state) => state.types.viewTypes)
+  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Team')?.id
+  useEffect(() => {
+    //hides the url so people can't sneak peak
+    history.push('team')
+    if (code && code !== '') {
+      socket_connect()
+      socketJoinPresentation()
+    }
+  }, [])
+  return (
+    <TeamContainer>
+      {activeViewTypeId && <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />}
+    </TeamContainer>
+  )
+}
+
+export default TeamViewPage
diff --git a/client/src/pages/views/ViewSelectPage.test.tsx b/client/src/pages/views/ViewSelectPage.test.tsx
index 83b71db05a13abc23b629003877c5698d99b4481..1843a1863990fe7e9fc54dd77971ed6dc7ef08ae 100644
--- a/client/src/pages/views/ViewSelectPage.test.tsx
+++ b/client/src/pages/views/ViewSelectPage.test.tsx
@@ -12,9 +12,18 @@ it('renders view select page', async () => {
     const res = {
       data: {},
     }
+    const compRes = {
+      data: {
+        id: 2,
+        slides: [{ id: 4 }],
+      },
+    }
     ;(mockedAxios.post as jest.Mock).mockImplementation(() => {
       return Promise.resolve(res)
     })
+    ;(mockedAxios.get as jest.Mock).mockImplementation(() => {
+      return Promise.resolve(compRes)
+    })
     render(
       <BrowserRouter>
         <Provider store={store}>
diff --git a/client/src/pages/views/ViewSelectPage.tsx b/client/src/pages/views/ViewSelectPage.tsx
index 3c3599edeaf3d9d46ff6c462506d196d79d1a9f7..845ba21c3edc1e5f499f6aae817e195af4d9bc21 100644
--- a/client/src/pages/views/ViewSelectPage.tsx
+++ b/client/src/pages/views/ViewSelectPage.tsx
@@ -4,17 +4,19 @@ import { Link, useRouteMatch } from 'react-router-dom'
 import { ViewSelectButtonGroup, ViewSelectContainer } from './styled'
 import { useParams } from 'react-router-dom'
 import { CircularProgress, Typography } from '@material-ui/core'
-import ParticipantViewPage from './ParticipantViewPage'
+import TeamViewPage from './TeamViewPage'
 import axios from 'axios'
-import PresenterViewPage from './PresenterViewPage'
+import OperatorViewPage from './OperatorViewPage'
 import JudgeViewPage from './JudgeViewPage'
 import AudienceViewPage from './AudienceViewPage'
-import { useAppSelector } from '../../hooks'
+import { useAppDispatch, useAppSelector } from '../../hooks'
+import { getPresentationCompetition, setPresentationCode } from '../../actions/presentation'
 
 interface ViewSelectParams {
   code: string
 }
 const ViewSelectPage: React.FC = () => {
+  const dispatch = useAppDispatch()
   const [loading, setLoading] = useState(true)
   const [error, setError] = useState(false)
   const [viewTypeId, setViewTypeId] = useState(undefined)
@@ -27,9 +29,9 @@ const ViewSelectPage: React.FC = () => {
     if (competitionId) {
       switch (viewType) {
         case 'Team':
-          return <ParticipantViewPage />
+          return <TeamViewPage />
         case 'Judge':
-          return <JudgeViewPage />
+          return <JudgeViewPage code={code} competitionId={competitionId} />
         case 'Audience':
           return <AudienceViewPage />
         default:
@@ -43,8 +45,10 @@ const ViewSelectPage: React.FC = () => {
       .post('/api/auth/login/code', { code })
       .then((response) => {
         setLoading(false)
-        setViewTypeId(response.data[0].view_type_id)
-        setCompetitionId(response.data[0].competition_id)
+        setViewTypeId(response.data.view_type_id)
+        setCompetitionId(response.data.competition_id)
+        dispatch(getPresentationCompetition(response.data.competition_id))
+        dispatch(setPresentationCode(code))
       })
       .catch(() => {
         setLoading(false)
@@ -53,13 +57,17 @@ const ViewSelectPage: React.FC = () => {
   }, [])
 
   return (
-    <ViewSelectContainer>
-      <ViewSelectButtonGroup>
-        {loading && <CircularProgress />}
-        {!loading && renderView(viewTypeId)}
-        {error && <Typography>Något gick fel, dubbelkolla koden och försök igen</Typography>}
-      </ViewSelectButtonGroup>
-    </ViewSelectContainer>
+    <>
+      {!loading && renderView(viewTypeId)}
+      {(loading || error) && (
+        <ViewSelectContainer>
+          <ViewSelectButtonGroup>
+            {loading && <CircularProgress />}
+            {error && <Typography>Något gick fel, dubbelkolla koden och försök igen</Typography>}
+          </ViewSelectButtonGroup>
+        </ViewSelectContainer>
+      )}
+    </>
   )
 }
 
diff --git a/client/src/pages/views/components/JudgeScoreDisplay.tsx b/client/src/pages/views/components/JudgeScoreDisplay.tsx
index ae4d8ab3e7e95a44357565093711d9f4722be0a3..2745e12970f3d5a20d2057adbc76ecc0ce2e6d6f 100644
--- a/client/src/pages/views/components/JudgeScoreDisplay.tsx
+++ b/client/src/pages/views/components/JudgeScoreDisplay.tsx
@@ -1,15 +1,35 @@
 import { Box, Typography } from '@material-ui/core'
+import axios from 'axios'
 import React from 'react'
-import { useAppSelector } from '../../../hooks'
+import { getPresentationCompetition } from '../../../actions/presentation'
+import { useAppDispatch, useAppSelector } from '../../../hooks'
 import { AnswerContainer, ScoreDisplayContainer, ScoreDisplayHeader, ScoreInput } from './styled'
 
 type ScoreDisplayProps = {
   teamIndex: number
 }
-const questionMaxScore = 5
 
 const JudgeScoreDisplay = ({ teamIndex }: ScoreDisplayProps) => {
-  const currentTeam = useAppSelector((state) => state.presentation.teams[teamIndex])
+  const dispatch = useAppDispatch()
+  const currentTeam = useAppSelector((state) => state.presentation.competition.teams[teamIndex])
+  const currentCompetititonId = useAppSelector((state) => state.presentation.competition.id)
+  const activeQuestion = useAppSelector(
+    (state) =>
+      state.presentation.competition.slides.find((slide) => slide.id === state.presentation.slide?.id)?.questions[0]
+  )
+  const scores = currentTeam.question_answers.map((questionAnswer) => questionAnswer.score)
+  const questionMaxScore = activeQuestion?.total_score
+  const activeAnswer = currentTeam.question_answers.find(
+    (questionAnswer) => questionAnswer.question_id === activeQuestion?.id
+  )
+  const handleEditScore = async (newScore: number, answerId: number) => {
+    await axios
+      .put(`/api/competitions/${currentCompetititonId}/teams/${currentTeam.id}/answers/${answerId}`, {
+        score: newScore,
+      })
+      .then(() => dispatch(getPresentationCompetition(currentCompetititonId.toString())))
+  }
+
   return (
     <ScoreDisplayContainer>
       <ScoreDisplayHeader>
@@ -17,21 +37,25 @@ const JudgeScoreDisplay = ({ teamIndex }: ScoreDisplayProps) => {
           <Box fontWeight="fontWeightBold">{currentTeam.name}</Box>
         </Typography>
 
-        <ScoreInput
-          label="Poäng"
-          defaultValue={0}
-          inputProps={{ style: { fontSize: 20 } }}
-          InputProps={{ disableUnderline: true, inputProps: { min: 0, max: questionMaxScore } }}
-          type="number"
-        ></ScoreInput>
+        {activeAnswer && (
+          <ScoreInput
+            label="Poäng"
+            defaultValue={activeAnswer?.score}
+            inputProps={{ style: { fontSize: 20 } }}
+            InputProps={{ disableUnderline: true, inputProps: { min: 0, max: questionMaxScore } }}
+            type="number"
+            onChange={(event) => handleEditScore(+event.target.value, activeAnswer.id)}
+          />
+        )}
       </ScoreDisplayHeader>
-      <Typography variant="h6">Alla poäng: 2 0 0 0 0 0 0 0 0</Typography>
-      <Typography variant="h6">Total poäng: 9</Typography>
-      <AnswerContainer>
-        <Typography variant="body1">
-          Svar: blablablablablablablablablabla blablablablabla blablablablabla blablablablablablablablablabla{' '}
-        </Typography>
-      </AnswerContainer>
+      <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>
+      {activeAnswer && (
+        <AnswerContainer>
+          <Typography variant="body1">{activeAnswer.answer}</Typography>
+        </AnswerContainer>
+      )}
+      {!activeAnswer && <Typography variant="body1">Inget svar</Typography>}
     </ScoreDisplayContainer>
   )
 }
diff --git a/client/src/pages/views/components/JudgeScoringInstructions.tsx b/client/src/pages/views/components/JudgeScoringInstructions.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3cc803194a9754bea8a9b23b3f2b073d6547c05c
--- /dev/null
+++ b/client/src/pages/views/components/JudgeScoringInstructions.tsx
@@ -0,0 +1,28 @@
+import { Box, Card, Typography } from '@material-ui/core'
+import axios from 'axios'
+import React from 'react'
+import { getPresentationCompetition } from '../../../actions/presentation'
+import { useAppDispatch, useAppSelector } from '../../../hooks'
+import { RichQuestion } from '../../../interfaces/ApiRichModels'
+import {
+  AnswerContainer,
+  JudgeScoringInstructionsContainer,
+  ScoreDisplayContainer,
+  ScoreDisplayHeader,
+  ScoreInput,
+} from './styled'
+
+type JudgeScoringInstructionsProps = {
+  question: RichQuestion
+}
+
+const JudgeScoringInstructions = ({ question }: JudgeScoringInstructionsProps) => {
+  return (
+    <JudgeScoringInstructionsContainer elevation={3}>
+      <Typography variant="h4">Rättningsinstruktioner</Typography>
+      <Typography variant="body1">Såhär rättar du denhär frågan</Typography>
+    </JudgeScoringInstructionsContainer>
+  )
+}
+
+export default JudgeScoringInstructions
diff --git a/client/src/pages/views/components/PresentationComponent.tsx b/client/src/pages/views/components/PresentationComponent.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a41f7912469256a6e946522790f4f8203f8da60f
--- /dev/null
+++ b/client/src/pages/views/components/PresentationComponent.tsx
@@ -0,0 +1,51 @@
+import { Typography } from '@material-ui/core'
+import React from 'react'
+import { Rnd } from 'react-rnd'
+import { ComponentTypes } from '../../../enum/ComponentTypes'
+import { useAppSelector } from '../../../hooks'
+import { Component, ImageComponent, TextComponent } from '../../../interfaces/ApiModels'
+import ImageComponentDisplay from '../../presentationEditor/components/ImageComponentDisplay'
+import TextComponentDisplay from '../../presentationEditor/components/TextComponentDisplay'
+import { SlideContainer } from './styled'
+
+type PresentationComponentProps = {
+  component: Component
+  width: number
+  height: number
+  scale: number
+}
+
+const PresentationComponent = ({ component, width, height, scale }: PresentationComponentProps) => {
+  const renderInnerComponent = () => {
+    switch (component.type_id) {
+      case ComponentTypes.Text:
+        return <TextComponentDisplay component={component as TextComponent} scale={scale} />
+      case ComponentTypes.Image:
+        return (
+          <ImageComponentDisplay
+            height={component.h * scale}
+            width={component.w * scale}
+            component={component as ImageComponent}
+          />
+        )
+      default:
+        break
+    }
+  }
+  return (
+    <Rnd
+      minWidth={75 * scale}
+      minHeight={75 * scale}
+      disableDragging={true}
+      enableResizing={false}
+      bounds="parent"
+      //Multiply by scale to show components correctly for current screen size
+      size={{ width: component.w * scale, height: component.h * scale }}
+      position={{ x: component.x * scale, y: component.y * scale }}
+    >
+      {renderInnerComponent()}
+    </Rnd>
+  )
+}
+
+export default PresentationComponent
diff --git a/client/src/pages/views/components/SlideDisplay.test.tsx b/client/src/pages/views/components/SlideDisplay.test.tsx
deleted file mode 100644
index 1a661d3340d503c71e149393db3cb00f1e2406c4..0000000000000000000000000000000000000000
--- a/client/src/pages/views/components/SlideDisplay.test.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { render } from '@testing-library/react'
-import React from 'react'
-import { Provider } from 'react-redux'
-import store from '../../../store'
-import SlideDisplay from './SlideDisplay'
-
-it('renders slide display', () => {
-  render(
-    <Provider store={store}>
-      <SlideDisplay />
-    </Provider>
-  )
-})
diff --git a/client/src/pages/views/components/SlideDisplay.tsx b/client/src/pages/views/components/SlideDisplay.tsx
deleted file mode 100644
index 7ecffac50a735eade79ebcad046040045e37dd93..0000000000000000000000000000000000000000
--- a/client/src/pages/views/components/SlideDisplay.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Typography } from '@material-ui/core'
-import React from 'react'
-import { useAppSelector } from '../../../hooks'
-import { SlideContainer } from './styled'
-
-const SlideDisplay: React.FC = () => {
-  const currentSlide = useAppSelector((state) => state.presentation.slide)
-
-  return (
-    <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>
-  )
-}
-
-export default SlideDisplay
diff --git a/client/src/pages/views/components/Timer.tsx b/client/src/pages/views/components/Timer.tsx
index 0cbd1fdfdbf0d63dc6ef5e687539400510af770e..8d3b9f70594ac416610bbd06b9d09cbee3ece0a4 100644
--- a/client/src/pages/views/components/Timer.tsx
+++ b/client/src/pages/views/components/Timer.tsx
@@ -36,11 +36,7 @@ const Timer: React.FC = (props: any) => {
     }
   }, [props.timer.enabled])
 
-  return (
-    <>
-      <div>{props.timer.value}</div>
-    </>
-  )
+  return <div>{props.timer.value}</div>
 }
 
 export default connect(mapStateToProps, mapDispatchToProps)(Timer)
diff --git a/client/src/pages/views/components/styled.tsx b/client/src/pages/views/components/styled.tsx
index b522b20d3548b4f11864373d5a91088424c45a58..fef186f792dafe8ee259faa693cf6707975d06ba 100644
--- a/client/src/pages/views/components/styled.tsx
+++ b/client/src/pages/views/components/styled.tsx
@@ -1,4 +1,4 @@
-import { TextField } from '@material-ui/core'
+import { Card, Paper, TextField } from '@material-ui/core'
 import styled from 'styled-components'
 
 export const SlideContainer = styled.div`
@@ -32,3 +32,13 @@ export const AnswerContainer = styled.div`
   display: flex;
   flex-wrap: wrap;
 `
+
+export const JudgeScoringInstructionsContainer = styled(Paper)`
+  position: absolute;
+  bottom: 0;
+  height: 250px;
+  width: 100%;
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+`
diff --git a/client/src/pages/views/styled.tsx b/client/src/pages/views/styled.tsx
index 1f3a61c61789964b96346f8734422ca8c83518b7..9699902727b2352fec07b8781a9d8c43ad20663e 100644
--- a/client/src/pages/views/styled.tsx
+++ b/client/src/pages/views/styled.tsx
@@ -1,4 +1,4 @@
-import { AppBar, Button, Drawer, Toolbar, Typography } from '@material-ui/core'
+import { AppBar, Button, Card, Drawer, Paper, Toolbar, Typography } from '@material-ui/core'
 import styled from 'styled-components'
 
 export const JudgeAppBar = styled(AppBar)`
@@ -15,7 +15,7 @@ export const JudgeQuestionsLabel = styled(Typography)`
 `
 
 export const JudgeAnswersLabel = styled(Typography)`
-  margin-right: 160px;
+  margin-right: 304px;
 `
 
 export const ViewSelectContainer = styled.div`
@@ -35,19 +35,24 @@ export const ViewSelectButtonGroup = styled.div`
   margin-right: auto;
 `
 
-export const PresenterHeader = styled.div`
+export const OperatorHeader = styled.div`
   display: flex;
   justify-content: space-between;
-  position: fixed;
+  height: 120px;
   width: 100%;
+  position: absolute;
 `
 
-export const PresenterFooter = styled.div`
+export const OperatorFooter = styled.div`
   display: flex;
   justify-content: space-between;
+  height: 140px;
+  position: absolute;
+  bottom: 0;
+  width: 100%;
 `
 
-export const PresenterButton = styled(Button)`
+export const OperatorButton = styled(Button)`
   width: 100px;
   height: 100px;
   margin-left: 16px;
@@ -61,10 +66,11 @@ export const SlideCounter = styled(Button)`
   margin-top: 16px;
 `
 
-export const PresenterContainer = styled.div`
+export const OperatorContainer = styled.div`
   display: flex;
   flex-direction: column;
   justify-content: space-between;
+  align-items: center;
   height: 100%;
 `
 
@@ -103,8 +109,63 @@ interface ContentProps {
 }
 
 export const Content = styled.div<ContentProps>`
+  width: 100%;
+  height: 100%;
+  max-width: calc(100% - ${(props) => (props ? props.leftDrawerWidth + props.rightDrawerWidth : 0)}px);
+  max-height: calc(100% - 64px);
   margin-left: ${(props) => (props ? props.leftDrawerWidth : 0)}px;
   margin-right: ${(props) => (props ? props.rightDrawerWidth : 0)}px;
-  width: calc(100% - ${(props) => (props ? props.leftDrawerWidth + props.rightDrawerWidth : 0)}px);
-  height: calc(100% - 64px);
+  display: flex;
+  justify-content: center;
+  background-color: rgba(0, 0, 0, 0.08);
+`
+
+export const InnerContent = styled.div`
+  width: 100%;
+  /* Makes sure width is not bigger than where a 16:9 display can fit 
+  without overlapping with header */
+  max-width: calc(((100vh - 64px) / 9) * 16);
+`
+
+export const OperatorContent = styled.div`
+  height: 100%;
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  background-color: rgba(0, 0, 0, 0.08);
+`
+
+export const OperatorInnerContent = styled.div`
+  height: 100%;
+  width: 100%;
+  /* Makes sure width is not bigger than where a 16:9 display can fit 
+  without overlapping with header and footer */
+  max-width: calc(((100vh - 260px) / 9) * 16);
+`
+
+export const TeamContainer = styled.div`
+  max-width: calc((100vh / 9) * 16);
+`
+
+interface ScoreHeaderPaperProps {
+  $rightDrawerWidth: number
+}
+
+export const ScoreHeaderPaper = styled(Card)<ScoreHeaderPaperProps>`
+  position: absolute;
+  top: 66px;
+  width: ${(props) => (props ? props.$rightDrawerWidth : 0)}px;
+  height: 71px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 10;
+`
+
+export const ScoreHeaderPadding = styled.div`
+  min-height: 71px;
+`
+
+export const ScoreFooterPadding = styled.div`
+  min-height: 250px;
 `
diff --git a/client/src/reducers/editorReducer.ts b/client/src/reducers/editorReducer.ts
index 20d0b42885b4d3ebeffbca9ead97b1eb0ddc2fad..4f245d1d05638cc059cf8d26921642d8239c169a 100644
--- a/client/src/reducers/editorReducer.ts
+++ b/client/src/reducers/editorReducer.ts
@@ -5,6 +5,7 @@ import { RichCompetition } from '../interfaces/ApiRichModels'
 interface EditorState {
   competition: RichCompetition
   activeSlideId: number
+  activeViewTypeId: number
   loading: boolean
 }
 
@@ -16,8 +17,10 @@ const initialState: EditorState = {
     city_id: 1,
     slides: [],
     teams: [],
+    background_image: undefined,
   },
   activeSlideId: -1,
+  activeViewTypeId: -1,
   loading: true,
 }
 
@@ -34,6 +37,11 @@ export default function (state = initialState, action: AnyAction) {
         ...state,
         activeSlideId: action.payload as number,
       }
+    case Types.SET_EDITOR_VIEW_ID:
+      return {
+        ...state,
+        activeViewTypeId: action.payload as number,
+      }
     default:
       return state
   }
diff --git a/client/src/reducers/presentationReducer.test.ts b/client/src/reducers/presentationReducer.test.ts
index a155eab5c915e82c64f16c0093dd2fc296b10d7a..202d9406853628a32c77b9e42e1bcc502299889d 100644
--- a/client/src/reducers/presentationReducer.test.ts
+++ b/client/src/reducers/presentationReducer.test.ts
@@ -1,12 +1,13 @@
 import Types from '../actions/types'
 import { RichSlide } from '../interfaces/ApiRichModels'
-import { Slide } from '../interfaces/Slide'
+import { Slide } from '../interfaces/ApiModels'
 import presentationReducer from './presentationReducer'
 
 const initialState = {
   competition: {
     name: '',
-    id: 0,
+    id: 1,
+    background_image: undefined,
     city_id: 0,
     slides: [],
     year: 0,
@@ -14,12 +15,12 @@ const initialState = {
   },
   slide: {
     competition_id: 0,
-    id: 0,
+    background_image: undefined,
+    id: -1,
     order: 0,
     timer: 0,
     title: '',
   },
-  teams: [],
   code: '',
   timer: {
     enabled: false,
@@ -41,7 +42,6 @@ it('should handle SET_PRESENTATION_COMPETITION', () => {
     },
     slides: [{ id: 20 }],
     year: 1999,
-    teams: [],
   }
   expect(
     presentationReducer(initialState, {
@@ -50,33 +50,7 @@ it('should handle SET_PRESENTATION_COMPETITION', () => {
     })
   ).toEqual({
     competition: testCompetition,
-    slide: testCompetition.slides[0],
-    teams: initialState.teams,
-    code: initialState.code,
-    timer: initialState.timer,
-  })
-})
-
-it('should handle SET_PRESENTATION_TEAMS', () => {
-  const testTeams = [
-    {
-      name: 'testTeamName1',
-      id: 3,
-    },
-    {
-      name: 'testTeamName2',
-      id: 5,
-    },
-  ]
-  expect(
-    presentationReducer(initialState, {
-      type: Types.SET_PRESENTATION_TEAMS,
-      payload: testTeams,
-    })
-  ).toEqual({
-    competition: initialState.competition,
     slide: initialState.slide,
-    teams: testTeams,
     code: initialState.code,
     timer: initialState.timer,
   })
@@ -100,7 +74,6 @@ it('should handle SET_PRESENTATION_SLIDE', () => {
   ).toEqual({
     competition: initialState.competition,
     slide: testSlide,
-    teams: initialState.teams,
     code: initialState.code,
     timer: initialState.timer,
   })
@@ -115,8 +88,8 @@ describe('should handle SET_PRESENTATION_SLIDE_PREVIOUS', () => {
           { competition_id: 0, order: 0 },
           { competition_id: 0, order: 1 },
         ] as RichSlide[],
+        teams: [],
       },
-      teams: initialState.teams,
       slide: { competition_id: 0, order: 1 } as Slide,
       code: initialState.code,
       timer: initialState.timer,
@@ -128,7 +101,7 @@ describe('should handle SET_PRESENTATION_SLIDE_PREVIOUS', () => {
     ).toEqual({
       competition: testPresentationState.competition,
       slide: testPresentationState.competition.slides[0],
-      teams: testPresentationState.teams,
+
       code: initialState.code,
       timer: initialState.timer,
     })
@@ -141,8 +114,8 @@ describe('should handle SET_PRESENTATION_SLIDE_PREVIOUS', () => {
           { competition_id: 0, order: 0 },
           { competition_id: 0, order: 1 },
         ] as RichSlide[],
+        teams: [],
       },
-      teams: initialState.teams,
       slide: { competition_id: 0, order: 0 } as Slide,
       code: initialState.code,
       timer: initialState.timer,
@@ -154,7 +127,6 @@ describe('should handle SET_PRESENTATION_SLIDE_PREVIOUS', () => {
     ).toEqual({
       competition: testPresentationState.competition,
       slide: testPresentationState.competition.slides[0],
-      teams: testPresentationState.teams,
       code: initialState.code,
       timer: initialState.timer,
     })
@@ -170,9 +142,11 @@ describe('should handle SET_PRESENTATION_SLIDE_NEXT', () => {
           { competition_id: 0, order: 0 },
           { competition_id: 0, order: 1 },
         ] as RichSlide[],
+        teams: [],
       },
-      teams: initialState.teams,
       slide: { competition_id: 0, order: 0 } as Slide,
+      code: initialState.code,
+      timer: initialState.timer,
     }
     expect(
       presentationReducer(testPresentationState, {
@@ -181,7 +155,8 @@ describe('should handle SET_PRESENTATION_SLIDE_NEXT', () => {
     ).toEqual({
       competition: testPresentationState.competition,
       slide: testPresentationState.competition.slides[1],
-      teams: testPresentationState.teams,
+      code: initialState.code,
+      timer: initialState.timer,
     })
   })
   it('by not changing slide if there is no next one', () => {
@@ -192,8 +167,8 @@ describe('should handle SET_PRESENTATION_SLIDE_NEXT', () => {
           { competition_id: 0, order: 0 },
           { competition_id: 0, order: 1 },
         ] as RichSlide[],
+        teams: [],
       },
-      teams: initialState.teams,
       slide: { competition_id: 0, order: 1 } as Slide,
     }
     expect(
@@ -203,7 +178,6 @@ describe('should handle SET_PRESENTATION_SLIDE_NEXT', () => {
     ).toEqual({
       competition: testPresentationState.competition,
       slide: testPresentationState.competition.slides[1],
-      teams: testPresentationState.teams,
     })
   })
 })
diff --git a/client/src/reducers/presentationReducer.ts b/client/src/reducers/presentationReducer.ts
index 5ba3ac5b196cb65fc3209b87fcb4636e22cac2ba..b0c4791e9ab1f0ef05c931caa1fc4e9fe8268917 100644
--- a/client/src/reducers/presentationReducer.ts
+++ b/client/src/reducers/presentationReducer.ts
@@ -7,7 +7,6 @@ import { RichCompetition } from './../interfaces/ApiRichModels'
 interface PresentationState {
   competition: RichCompetition
   slide: Slide
-  teams: Team[]
   code: string
   timer: Timer
 }
@@ -15,20 +14,21 @@ interface PresentationState {
 const initialState: PresentationState = {
   competition: {
     name: '',
-    id: 0,
+    id: 1,
     city_id: 0,
     slides: [],
     year: 0,
     teams: [],
+    background_image: undefined,
   },
   slide: {
     competition_id: 0,
-    id: 0,
+    id: -1,
     order: 0,
     timer: 0,
     title: '',
+    background_image: undefined,
   },
-  teams: [],
   code: '',
   timer: {
     enabled: false,
@@ -41,14 +41,8 @@ export default function (state = initialState, action: AnyAction) {
     case Types.SET_PRESENTATION_COMPETITION:
       return {
         ...state,
-        slide: action.payload.slides[0] as Slide,
         competition: action.payload as RichCompetition,
       }
-    case Types.SET_PRESENTATION_TEAMS:
-      return {
-        ...state,
-        teams: action.payload as Team[],
-      }
     case Types.SET_PRESENTATION_CODE:
       return {
         ...state,
diff --git a/client/src/sockets.ts b/client/src/sockets.ts
index 54f84c39840acec184fa023545c1a15332eecdbc..4392021df3c89d3d981096e38e1576d155c89b21 100644
--- a/client/src/sockets.ts
+++ b/client/src/sockets.ts
@@ -38,32 +38,26 @@ 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: 'CO0ART' }) // TODO: Send code gotten from auth/login/<code> api call
-  console.log('JOIN PRESENTATION')
+  socket.emit('join_presentation', { code: store.getState().presentation.code }) // TODO: Send code gotten from auth/login/<code> api call
 }
 
 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) => {
   if (slide_order < 0 || store.getState().presentation.competition.slides.length <= slide_order) {
-    console.log('CANT CHANGE TO NON EXISTENT SLIDE')
     return
   }
 
@@ -74,7 +68,6 @@ 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,
@@ -82,6 +75,5 @@ export const socketSetTimer = (timer: Timer) => {
 }
 
 export const socketStartTimer = () => {
-  console.log('START TIMER')
   socketSetTimer({ enabled: true, value: store.getState().presentation.timer.value })
 }
diff --git a/client/src/utils/renderSlideIcon.tsx b/client/src/utils/renderSlideIcon.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ba1eafcb8052469dd8b627b95d2eee518bfd5fe8
--- /dev/null
+++ b/client/src/utils/renderSlideIcon.tsx
@@ -0,0 +1,21 @@
+import { RichSlide } from '../interfaces/ApiRichModels'
+import React from 'react'
+import BuildOutlinedIcon from '@material-ui/icons/BuildOutlined'
+import CreateOutlinedIcon from '@material-ui/icons/CreateOutlined'
+import DnsOutlinedIcon from '@material-ui/icons/DnsOutlined'
+import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined'
+
+export const renderSlideIcon = (slide: RichSlide) => {
+  if (slide.questions && slide.questions[0] && slide.questions[0].type_id) {
+    switch (slide.questions[0].type_id) {
+      case 1:
+        return <CreateOutlinedIcon /> // text question
+      case 2:
+        return <BuildOutlinedIcon /> // practical qustion
+      case 3:
+        return <DnsOutlinedIcon /> // multiple choice question
+    }
+  } else {
+    return <InfoOutlinedIcon /> // information slide
+  }
+}
diff --git a/client/src/utils/uploadImage.ts b/client/src/utils/uploadImage.ts
new file mode 100644
index 0000000000000000000000000000000000000000..151a34a400f2d2e632b1f18b8f249206c043a02f
--- /dev/null
+++ b/client/src/utils/uploadImage.ts
@@ -0,0 +1,16 @@
+import axios from 'axios'
+import { getEditorCompetition } from '../actions/editor'
+import { Media } from '../interfaces/ApiModels'
+import store from '../store'
+
+export const uploadFile = async (formData: FormData, competitionId: string) => {
+  // Uploads the file to the server and creates a Media object in database.
+  // Returns media object data.
+  return await axios
+    .post(`/api/media/images`, formData)
+    .then((response) => {
+      getEditorCompetition(competitionId)(store.dispatch, store.getState)
+      return response.data as Media
+    })
+    .catch(console.log)
+}
diff --git a/server/app/apis/__init__.py b/server/app/apis/__init__.py
index 5eab3f829ae81e17ecc269d98f568eeacc45a68c..e5ec3d2ca1e1d9de6d4c5cca5ec1e3fba40f33ee 100644
--- a/server/app/apis/__init__.py
+++ b/server/app/apis/__init__.py
@@ -13,24 +13,62 @@ def validate_editor(db_item, *views):
         abort(http_codes.UNAUTHORIZED)
 
 
-def check_jwt(editor=False, *views):
-    def wrapper(fn):
-        @wraps(fn)
-        def decorator(*args, **kwargs):
+def _is_allowed(allowed, actual):
+    return actual and "*" in allowed or actual in allowed
+
+
+def _has_access(in_claim, in_route):
+    in_route = int(in_route) if in_route else None
+    return not in_route or in_claim and in_claim == in_route
+
+
+def protect_route(allowed_roles=None, allowed_views=None):
+    def wrapper(func):
+        def inner(*args, **kwargs):
             verify_jwt_in_request()
             claims = get_jwt_claims()
+
+            # Authorize request if roles has access to the route #
+
+            nonlocal allowed_roles
+            allowed_roles = allowed_roles or []
             role = claims.get("role")
+            if _is_allowed(allowed_roles, role):
+                return func(*args, **kwargs)
+
+            # Authorize request if view has access and is trying to access the
+            # competition its in. Also check team if client is a team.
+            # Allow request if route doesn't belong to any competition.
+
+            nonlocal allowed_views
+            allowed_views = allowed_views or []
             view = claims.get("view")
-            if role == "Admin":
-                return fn(*args, **kwargs)
-            elif editor and role == "Editor":
-                return fn(*args, **kwargs)
-            elif view in views:
-                return fn(*args, **kwargs)
-            else:
-                abort(http_codes.UNAUTHORIZED)
-
-        return decorator
+            if not _is_allowed(allowed_views, view):
+                abort(
+                    http_codes.UNAUTHORIZED,
+                    f"Client with view '{view}' is not allowed to access route with allowed views {allowed_views}.",
+                )
+
+            claim_competition_id = claims.get("competition_id")
+            route_competition_id = kwargs.get("competition_id")
+            if not _has_access(claim_competition_id, route_competition_id):
+                abort(
+                    http_codes.UNAUTHORIZED,
+                    f"Client in competition '{claim_competition_id}' is not allowed to access competition '{route_competition_id}'.",
+                )
+
+            if view == "Team":
+                claim_team_id = claims.get("team_id")
+                route_team_id = kwargs.get("team_id")
+                if not _has_access(claim_team_id, route_team_id):
+                    abort(
+                        http_codes.UNAUTHORIZED,
+                        f"Client in team '{claim_team_id}' is not allowed to access team '{route_team_id}'.",
+                    )
+
+            return func(*args, **kwargs)
+
+        return inner
 
     return wrapper
 
diff --git a/server/app/apis/alternatives.py b/server/app/apis/alternatives.py
index ce7b4e7d3bb8a550aa52cbc0b2492c1118999876..94f6d6b1725106a3cf5994570e4d3d5829ba2dbe 100644
--- a/server/app/apis/alternatives.py
+++ b/server/app/apis/alternatives.py
@@ -1,29 +1,35 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
-from app.core.dto import QuestionAlternativeDTO, QuestionDTO
-from app.core.parsers import question_alternative_parser
-from app.core.schemas import QuestionAlternativeSchema
-from app.database.models import Question, QuestionAlternative
-from flask_jwt_extended import jwt_required
+from app.apis import item_response, list_response, protect_route
+from app.core.dto import QuestionAlternativeDTO
 from flask_restx import Resource
+from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = QuestionAlternativeDTO.api
 schema = QuestionAlternativeDTO.schema
 list_schema = QuestionAlternativeDTO.list_schema
 
+alternative_parser_add = reqparse.RequestParser()
+alternative_parser_add.add_argument("text", type=str, required=True, location="json")
+alternative_parser_add.add_argument("value", type=int, required=True, location="json")
+
+alternative_parser_edit = reqparse.RequestParser()
+alternative_parser_edit.add_argument("text", type=str, default=sentinel, location="json")
+alternative_parser_edit.add_argument("value", type=int, default=sentinel, location="json")
+
 
 @api.route("")
 @api.param("competition_id, slide_id, question_id")
 class QuestionAlternativeList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id, question_id):
         items = dbc.get.question_alternative_list(competition_id, slide_id, question_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id, question_id):
-        args = question_alternative_parser.parse_args(strict=True)
+        args = alternative_parser_add.parse_args(strict=True)
         item = dbc.add.question_alternative(**args, question_id=question_id)
         return item_response(schema.dump(item))
 
@@ -31,19 +37,19 @@ class QuestionAlternativeList(Resource):
 @api.route("/<alternative_id>")
 @api.param("competition_id, slide_id, question_id, alternative_id")
 class QuestionAlternatives(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id, question_id, alternative_id):
         items = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id)
         return item_response(schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, question_id, alternative_id):
-        args = question_alternative_parser.parse_args(strict=True)
+        args = alternative_parser_edit.parse_args(strict=True)
         item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id)
         item = dbc.edit.default(item, **args)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id, question_id, alternative_id):
         item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id)
         dbc.delete.default(item)
diff --git a/server/app/apis/answers.py b/server/app/apis/answers.py
index 0ef3003931d52d304d66cff1cedb36b0c8cfa777..3990e4e1d7b75fa35d13cc592906e4c4aa921024 100644
--- a/server/app/apis/answers.py
+++ b/server/app/apis/answers.py
@@ -1,29 +1,36 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import QuestionAnswerDTO
-from app.core.parsers import question_answer_edit_parser, question_answer_parser
-from app.core.schemas import QuestionAlternativeSchema
-from app.database.models import Question, QuestionAlternative, QuestionAnswer
-from flask_jwt_extended import jwt_required
 from flask_restx import Resource
+from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = QuestionAnswerDTO.api
 schema = QuestionAnswerDTO.schema
 list_schema = QuestionAnswerDTO.list_schema
 
+answer_parser_add = reqparse.RequestParser()
+answer_parser_add.add_argument("answer", type=str, required=True, location="json")
+answer_parser_add.add_argument("score", type=int, required=True, location="json")
+answer_parser_add.add_argument("question_id", type=int, required=True, location="json")
+
+answer_parser_edit = reqparse.RequestParser()
+answer_parser_edit.add_argument("answer", type=str, default=sentinel, location="json")
+answer_parser_edit.add_argument("score", type=int, default=sentinel, location="json")
+
 
 @api.route("")
 @api.param("competition_id, team_id")
 class QuestionAnswerList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, team_id):
         items = dbc.get.question_answer_list(competition_id, team_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def post(self, competition_id, team_id):
-        args = question_answer_parser.parse_args(strict=True)
+        args = answer_parser_add.parse_args(strict=True)
         item = dbc.add.question_answer(**args, team_id=team_id)
         return item_response(schema.dump(item))
 
@@ -31,14 +38,14 @@ class QuestionAnswerList(Resource):
 @api.route("/<answer_id>")
 @api.param("competition_id, team_id, answer_id")
 class QuestionAnswers(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, team_id, answer_id):
         item = dbc.get.question_answer(competition_id, team_id, answer_id)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def put(self, competition_id, team_id, answer_id):
-        args = question_answer_edit_parser.parse_args(strict=True)
+        args = answer_parser_edit.parse_args(strict=True)
         item = dbc.get.question_answer(competition_id, team_id, answer_id)
         item = dbc.edit.default(item, **args)
         return item_response(schema.dump(item))
diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py
index 87d7f1d19041760131560db52de5dada55dc34fe..c09b04950433e94ed55da08a64e4fb51671df91a 100644
--- a/server/app/apis/auth.py
+++ b/server/app/apis/auth.py
@@ -1,32 +1,54 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, text_response
+from app.apis import item_response, protect_route, text_response
 from app.core.codes import verify_code
 from app.core.dto import AuthDTO, CodeDTO
-from app.core.parsers import create_user_parser, login_code_parser, login_parser
-from app.database.models import User
 from flask_jwt_extended import (
     create_access_token,
     create_refresh_token,
     get_jwt_identity,
     get_raw_jwt,
     jwt_refresh_token_required,
-    jwt_required,
 )
-from flask_restx import Namespace, Resource, cors
+from flask_restx import Resource
+from flask_restx import inputs, reqparse
+from datetime import timedelta
+from app.core import sockets
 
 api = AuthDTO.api
 schema = AuthDTO.schema
 list_schema = AuthDTO.list_schema
 
+login_parser = reqparse.RequestParser()
+login_parser.add_argument("email", type=inputs.email(), required=True, location="json")
+login_parser.add_argument("password", type=str, required=True, location="json")
+
+create_user_parser = login_parser.copy()
+create_user_parser.add_argument("city_id", type=int, required=True, location="json")
+create_user_parser.add_argument("role_id", type=int, required=True, location="json")
+
+login_code_parser = reqparse.RequestParser()
+login_code_parser.add_argument("code", type=str, required=True, location="json")
+
 
 def get_user_claims(item_user):
     return {"role": item_user.role.name, "city_id": item_user.city_id}
 
 
+def get_code_claims(item_code):
+    return {"view": item_code.view_type.name, "competition_id": item_code.competition_id, "team_id": item_code.team_id}
+
+
+@api.route("/test")
+class AuthSignup(Resource):
+    @protect_route(allowed_roles=["Admin"], allowed_views=["*"])
+    def get(self):
+        return "ok"
+
+
 @api.route("/signup")
 class AuthSignup(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def post(self):
         args = create_user_parser.parse_args(strict=True)
         email = args.get("email")
@@ -41,7 +63,7 @@ class AuthSignup(Resource):
 @api.route("/delete/<ID>")
 @api.param("ID")
 class AuthDelete(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def delete(self, ID):
         item_user = dbc.get.user(ID)
 
@@ -77,24 +99,38 @@ class AuthLoginCode(Resource):
         code = args["code"]
 
         if not verify_code(code):
-            api.abort(codes.BAD_REQUEST, "Invalid code")
+            api.abort(codes.UNAUTHORIZED, "Invalid code")
 
         item_code = dbc.get.code_by_code(code)
-        return item_response(CodeDTO.schema.dump(item_code)), codes.OK
+
+        if item_code.competition_id not in sockets.presentations:
+            api.abort(codes.UNAUTHORIZED, "Competition not active")
+
+        access_token = create_access_token(
+            item_code.id, user_claims=get_code_claims(item_code), expires_delta=timedelta(hours=8)
+        )
+
+        response = {
+            "competition_id": item_code.competition_id,
+            "view_type_id": item_code.view_type_id,
+            "team_id": item_code.team_id,
+            "access_token": access_token,
+        }
+        return response
 
 
 @api.route("/logout")
 class AuthLogout(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def post(self):
         jti = get_raw_jwt()["jti"]
         dbc.add.blacklist(jti)
-        return text_response("User logout")
+        return text_response("Logout")
 
 
 @api.route("/refresh")
 class AuthRefresh(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     @jwt_refresh_token_required
     def post(self):
         old_jti = get_raw_jwt()["jti"]
diff --git a/server/app/apis/codes.py b/server/app/apis/codes.py
index 2a2eea5c55c6e46cf516ff35dd79ed3d96f58679..d07e17435aed31417254acfd83ed9315f1477ba3 100644
--- a/server/app/apis/codes.py
+++ b/server/app/apis/codes.py
@@ -1,10 +1,8 @@
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core import http_codes as codes
 from app.core.dto import CodeDTO
-from app.core.parsers import code_parser
-from app.database.models import Code, Competition
-from flask_jwt_extended import jwt_required
+from app.database.models import Code
 from flask_restx import Resource
 
 api = CodeDTO.api
@@ -15,18 +13,18 @@ list_schema = CodeDTO.list_schema
 @api.route("")
 @api.param("competition_id")
 class CodesList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.code_list(competition_id)
-        return list_response(list_schema.dump(items), len(items)), codes.OK
+        return list_response(list_schema.dump(items), len(items))
 
 
 @api.route("/<code_id>")
 @api.param("competition_id, code_id")
 class CodesById(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, code_id):
         item = dbc.get.one(Code, code_id)
         item.code = dbc.utils.generate_unique_code()
         dbc.utils.commit_and_refresh(item)
-        return item_response(schema.dump(item)), codes.OK
+        return item_response(schema.dump(item))
diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py
index 4a6bf79d6bf17d132e3a5fed6a403b21762bbc13..c5ff37c9131a7a9ec4bc1f394a9e1f5b5727460e 100644
--- a/server/app/apis/competitions.py
+++ b/server/app/apis/competitions.py
@@ -1,25 +1,40 @@
 import time
 
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
-from app.core import rich_schemas
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import CompetitionDTO
-from app.core.parsers import competition_parser, competition_search_parser
 from app.database.models import Competition
-from flask_jwt_extended import jwt_required
 from flask_restx import Resource
+from flask_restx import reqparse
+from app.core.parsers import search_parser, sentinel
 
 api = CompetitionDTO.api
 schema = CompetitionDTO.schema
 rich_schema = CompetitionDTO.rich_schema
 list_schema = CompetitionDTO.list_schema
 
+competition_parser_add = reqparse.RequestParser()
+competition_parser_add.add_argument("name", type=str, required=True, location="json")
+competition_parser_add.add_argument("year", type=int, required=True, location="json")
+competition_parser_add.add_argument("city_id", type=int, required=True, location="json")
+
+competition_parser_edit = reqparse.RequestParser()
+competition_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
+competition_parser_edit.add_argument("year", type=int, default=sentinel, location="json")
+competition_parser_edit.add_argument("city_id", type=int, default=sentinel, location="json")
+competition_parser_edit.add_argument("background_image_id", default=sentinel, type=int, location="json")
+
+competition_parser_search = search_parser.copy()
+competition_parser_search.add_argument("name", type=str, default=sentinel, location="args")
+competition_parser_search.add_argument("year", type=int, default=sentinel, location="args")
+competition_parser_search.add_argument("city_id", type=int, default=sentinel, location="args")
+
 
 @api.route("")
 class CompetitionsList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self):
-        args = competition_parser.parse_args(strict=True)
+        args = competition_parser_add.parse_args(strict=True)
 
         # Add competition
         item = dbc.add.competition(**args)
@@ -32,21 +47,21 @@ class CompetitionsList(Resource):
 @api.route("/<competition_id>")
 @api.param("competition_id")
 class Competitions(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id):
         item = dbc.get.competition(competition_id)
 
         return item_response(rich_schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id):
-        args = competition_parser.parse_args(strict=True)
+        args = competition_parser_edit.parse_args(strict=True)
         item = dbc.get.one(Competition, competition_id)
         item = dbc.edit.default(item, **args)
 
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id):
         item = dbc.get.one(Competition, competition_id)
         dbc.delete.competition(item)
@@ -56,9 +71,9 @@ class Competitions(Resource):
 
 @api.route("/search")
 class CompetitionSearch(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
-        args = competition_search_parser.parse_args(strict=True)
+        args = competition_parser_search.parse_args(strict=True)
         items, total = dbc.search.competition(**args)
         return list_response(list_schema.dump(items), total)
 
@@ -66,7 +81,7 @@ class CompetitionSearch(Resource):
 @api.route("/<competition_id>/copy")
 @api.param("competition_id")
 class SlidesOrder(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id):
         item_competition = dbc.get.competition(competition_id)
 
diff --git a/server/app/apis/components.py b/server/app/apis/components.py
index 23d250256120f4bf600b10cd010b5de9aa67e0dc..a1982763a5ec37b8bb02bb8e6a73e164355417a8 100644
--- a/server/app/apis/components.py
+++ b/server/app/apis/components.py
@@ -1,34 +1,53 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import ComponentDTO
-from app.core.parsers import component_create_parser, component_parser
-from app.database.models import Competition, Component
-from flask.globals import request
-from flask_jwt_extended import jwt_required
 from flask_restx import Resource
+from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = ComponentDTO.api
 schema = ComponentDTO.schema
 list_schema = ComponentDTO.list_schema
 
+component_parser_add = reqparse.RequestParser()
+component_parser_add.add_argument("x", type=str, default=0, location="json")
+component_parser_add.add_argument("y", type=int, default=0, location="json")
+component_parser_add.add_argument("w", type=int, default=1, location="json")
+component_parser_add.add_argument("h", type=int, default=1, location="json")
+component_parser_add.add_argument("type_id", type=int, required=True, location="json")
+component_parser_add.add_argument("view_type_id", type=int, required=True, location="json")
+component_parser_add.add_argument("text", type=str, default=None, location="json")
+component_parser_add.add_argument("media_id", type=int, default=None, location="json")
+component_parser_add.add_argument("question_id", type=int, default=None, location="json")
+
+component_parser_edit = reqparse.RequestParser()
+component_parser_edit.add_argument("x", type=str, default=sentinel, location="json")
+component_parser_edit.add_argument("y", type=int, default=sentinel, location="json")
+component_parser_edit.add_argument("w", type=int, default=sentinel, location="json")
+component_parser_edit.add_argument("h", type=int, default=sentinel, location="json")
+component_parser_edit.add_argument("text", type=str, default=sentinel, location="json")
+component_parser_edit.add_argument("media_id", type=int, default=sentinel, location="json")
+component_parser_edit.add_argument("question_id", type=int, default=sentinel, location="json")
+
 
 @api.route("/<component_id>")
 @api.param("competition_id, slide_id, component_id")
 class ComponentByID(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id, component_id):
         item = dbc.get.component(competition_id, slide_id, component_id)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, component_id):
-        args = component_parser.parse_args()
+        args = component_parser_edit.parse_args(strict=True)
         item = dbc.get.component(competition_id, slide_id, component_id)
-        item = dbc.edit.default(item, **args)
+        args_without_sentinel = {key: value for key, value in args.items() if value is not sentinel}
+        item = dbc.edit.default(item, **args_without_sentinel)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id, component_id):
         item = dbc.get.component(competition_id, slide_id, component_id)
         dbc.delete.component(item)
@@ -38,13 +57,13 @@ class ComponentByID(Resource):
 @api.route("")
 @api.param("competition_id, slide_id")
 class ComponentList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id):
         items = dbc.get.component_list(competition_id, slide_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id):
-        args = component_create_parser.parse_args()
+        args = component_parser_add.parse_args()
         item = dbc.add.component(slide_id=slide_id, **args)
         return item_response(schema.dump(item))
diff --git a/server/app/apis/media.py b/server/app/apis/media.py
index 8f0b28f7b332184f043f3f79562c02b6a001f7ca..49d20608840e320ef3d73d9df848748e6da33617 100644
--- a/server/app/apis/media.py
+++ b/server/app/apis/media.py
@@ -1,31 +1,35 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import MediaDTO
-from app.core.parsers import media_parser_search
-from app.database.models import City, Media, MediaType, QuestionType, Role
+from app.core.parsers import search_parser
+from app.database.models import Media
 from flask import request
-from flask_jwt_extended import get_jwt_identity, jwt_required
-from flask_restx import Resource, reqparse
+from flask_jwt_extended import get_jwt_identity
+from flask_restx import Resource
 from flask_uploads import UploadNotAllowed
 from sqlalchemy import exc
 import app.core.files as files
+from app.core.parsers import sentinel
 
 api = MediaDTO.api
 image_set = MediaDTO.image_set
 schema = MediaDTO.schema
 list_schema = MediaDTO.list_schema
 
+media_parser_search = search_parser.copy()
+media_parser_search.add_argument("filename", type=str, default=sentinel, location="args")
+
 
 @api.route("/images")
 class ImageList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         args = media_parser_search.parse_args(strict=True)
         items, total = dbc.search.image(**args)
         return list_response(list_schema.dump(items), total)
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self):
         if "image" not in request.files:
             api.abort(codes.BAD_REQUEST, "Missing image in request.files")
@@ -45,12 +49,12 @@ class ImageList(Resource):
 @api.route("/images/<ID>")
 @api.param("ID")
 class ImageList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, ID):
         item = dbc.get.one(Media, ID)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, ID):
         item = dbc.get.one(Media, ID)
         try:
diff --git a/server/app/apis/misc.py b/server/app/apis/misc.py
index 8b3bd5e57e5b877fdaed85f3e6a372b4984d8ef8..20a84e4c17c138b3a94ea6e3902e67154036cabc 100644
--- a/server/app/apis/misc.py
+++ b/server/app/apis/misc.py
@@ -1,10 +1,10 @@
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import list_response, protect_route
 from app.core import http_codes
 from app.core.dto import MiscDTO
 from app.database.models import City, Competition, ComponentType, MediaType, QuestionType, Role, User, ViewType
-from flask_jwt_extended import jwt_required
 from flask_restx import Resource, reqparse
+from flask_restx import reqparse
 
 api = MiscDTO.api
 
@@ -23,6 +23,7 @@ name_parser.add_argument("name", type=str, required=True, location="json")
 
 @api.route("/types")
 class TypesList(Resource):
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self):
         result = {}
         result["media_types"] = media_type_schema.dump(dbc.get.all(MediaType))
@@ -34,7 +35,7 @@ class TypesList(Resource):
 
 @api.route("/roles")
 class RoleList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         items = dbc.get.all(Role)
         return list_response(role_schema.dump(items))
@@ -42,12 +43,12 @@ class RoleList(Resource):
 
 @api.route("/cities")
 class CitiesList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         items = dbc.get.all(City)
         return list_response(city_schema.dump(items))
 
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def post(self):
         args = name_parser.parse_args(strict=True)
         dbc.add.city(args["name"])
@@ -58,7 +59,7 @@ class CitiesList(Resource):
 @api.route("/cities/<ID>")
 @api.param("ID")
 class Cities(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def put(self, ID):
         item = dbc.get.one(City, ID)
         args = name_parser.parse_args(strict=True)
@@ -67,7 +68,7 @@ class Cities(Resource):
         items = dbc.get.all(City)
         return list_response(city_schema.dump(items))
 
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def delete(self, ID):
         item = dbc.get.one(City, ID)
         dbc.delete.default(item)
@@ -77,7 +78,7 @@ class Cities(Resource):
 
 @api.route("/statistics")
 class Statistics(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         user_count = User.query.count()
         competition_count = Competition.query.count()
diff --git a/server/app/apis/questions.py b/server/app/apis/questions.py
index 5797872a9865bd693989eb0322d8a9818796e86b..b14849d224501e48f9cce8a108bfd24f29657e99 100644
--- a/server/app/apis/questions.py
+++ b/server/app/apis/questions.py
@@ -1,21 +1,30 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import QuestionDTO
-from app.core.parsers import question_parser
-from app.database.models import Question
-from flask_jwt_extended import jwt_required
 from flask_restx import Resource
+from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = QuestionDTO.api
 schema = QuestionDTO.schema
 list_schema = QuestionDTO.list_schema
 
+question_parser_add = reqparse.RequestParser()
+question_parser_add.add_argument("name", type=str, default=None, location="json")
+question_parser_add.add_argument("total_score", type=int, default=None, location="json")
+question_parser_add.add_argument("type_id", type=int, required=True, location="json")
+
+question_parser_edit = reqparse.RequestParser()
+question_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
+question_parser_edit.add_argument("total_score", type=int, default=sentinel, location="json")
+question_parser_edit.add_argument("type_id", type=int, default=sentinel, location="json")
+
 
 @api.route("/questions")
 @api.param("competition_id")
 class QuestionList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.question_list_for_competition(competition_id)
         return list_response(list_schema.dump(items))
@@ -24,14 +33,14 @@ class QuestionList(Resource):
 @api.route("/slides/<slide_id>/questions")
 @api.param("competition_id, slide_id")
 class QuestionListForSlide(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, slide_id):
         items = dbc.get.question_list(competition_id, slide_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id):
-        args = question_parser.parse_args(strict=True)
+        args = question_parser_add.parse_args(strict=True)
         item = dbc.add.question(slide_id=slide_id, **args)
         return item_response(schema.dump(item))
 
@@ -39,21 +48,21 @@ class QuestionListForSlide(Resource):
 @api.route("/slides/<slide_id>/questions/<question_id>")
 @api.param("competition_id, slide_id, question_id")
 class QuestionById(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, slide_id, question_id):
         item_question = dbc.get.question(competition_id, slide_id, question_id)
         return item_response(schema.dump(item_question))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, question_id):
-        args = question_parser.parse_args(strict=True)
+        args = question_parser_edit.parse_args(strict=True)
 
         item_question = dbc.get.question(competition_id, slide_id, question_id)
         item_question = dbc.edit.default(item_question, **args)
 
         return item_response(schema.dump(item_question))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id, question_id):
         item_question = dbc.get.question(competition_id, slide_id, question_id)
         dbc.delete.question(item_question)
diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py
index 9ca4a5f229488e03931ec544dd3ccf43406e5b9e..52ce4cce1d20fc277c3ee9ec46ea3566a51ec633 100644
--- a/server/app/apis/slides.py
+++ b/server/app/apis/slides.py
@@ -1,26 +1,31 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import SlideDTO
-from app.core.parsers import slide_parser
-from app.database.models import Competition, Slide
-from flask_jwt_extended import jwt_required
 from flask_restx import Resource
+from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = SlideDTO.api
 schema = SlideDTO.schema
 list_schema = SlideDTO.list_schema
 
+slide_parser_edit = reqparse.RequestParser()
+slide_parser_edit.add_argument("order", type=int, default=sentinel, location="json")
+slide_parser_edit.add_argument("title", type=str, default=sentinel, location="json")
+slide_parser_edit.add_argument("timer", type=int, default=sentinel, location="json")
+slide_parser_edit.add_argument("background_image_id", default=sentinel, type=int, location="json")
+
 
 @api.route("")
 @api.param("competition_id")
 class SlidesList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.slide_list(competition_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id):
         item_slide = dbc.add.slide(competition_id)
         return item_response(schema.dump(item_slide))
@@ -29,21 +34,21 @@ class SlidesList(Resource):
 @api.route("/<slide_id>")
 @api.param("competition_id,slide_id")
 class Slides(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, slide_id):
         item_slide = dbc.get.slide(competition_id, slide_id)
         return item_response(schema.dump(item_slide))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id):
-        args = slide_parser.parse_args(strict=True)
+        args = slide_parser_edit.parse_args(strict=True)
 
         item_slide = dbc.get.slide(competition_id, slide_id)
         item_slide = dbc.edit.default(item_slide, **args)
 
         return item_response(schema.dump(item_slide))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id):
         item_slide = dbc.get.slide(competition_id, slide_id)
 
@@ -54,9 +59,9 @@ class Slides(Resource):
 @api.route("/<slide_id>/order")
 @api.param("competition_id,slide_id")
 class SlideOrder(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id):
-        args = slide_parser.parse_args(strict=True)
+        args = slide_parser_edit.parse_args(strict=True)
         order = args.get("order")
 
         item_slide = dbc.get.slide(competition_id, slide_id)
@@ -83,7 +88,7 @@ class SlideOrder(Resource):
 @api.route("/<slide_id>/copy")
 @api.param("competition_id,slide_id")
 class SlideCopy(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id):
         item_slide = dbc.get.slide(competition_id, slide_id)
 
diff --git a/server/app/apis/teams.py b/server/app/apis/teams.py
index 514748ae27c7ff685d2b1099d1bf710610928860..71ca715d55d21de75f07db4241e5ef0bb14e1050 100644
--- a/server/app/apis/teams.py
+++ b/server/app/apis/teams.py
@@ -1,28 +1,32 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import TeamDTO
-from app.core.parsers import team_parser
-from app.database.models import Competition, Team
-from flask_jwt_extended import get_jwt_identity, jwt_required
-from flask_restx import Namespace, Resource, reqparse
+from flask_restx import Resource, reqparse
+from app.core.parsers import sentinel
 
 api = TeamDTO.api
 schema = TeamDTO.schema
 list_schema = TeamDTO.list_schema
 
+team_parser_add = reqparse.RequestParser()
+team_parser_add.add_argument("name", type=str, required=True, location="json")
+
+team_parser_edit = reqparse.RequestParser()
+team_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
+
 
 @api.route("")
 @api.param("competition_id")
 class TeamsList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.team_list(competition_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id):
-        args = team_parser.parse_args(strict=True)
+        args = team_parser_add.parse_args(strict=True)
         item_team = dbc.add.team(args["name"], competition_id)
         return item_response(schema.dump(item_team))
 
@@ -30,21 +34,21 @@ class TeamsList(Resource):
 @api.route("/<team_id>")
 @api.param("competition_id,team_id")
 class Teams(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, team_id):
         item = dbc.get.team(competition_id, team_id)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, team_id):
         item_team = dbc.get.team(competition_id, team_id)
 
         dbc.delete.team(item_team)
         return {}, codes.NO_CONTENT
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, team_id):
-        args = team_parser.parse_args(strict=True)
+        args = team_parser_edit.parse_args(strict=True)
         name = args.get("name")
 
         item_team = dbc.get.team(competition_id, team_id)
diff --git a/server/app/apis/users.py b/server/app/apis/users.py
index 1bae000bc6821ff7c7ac3dde82474bd351c2fe4d..dc26ac5b64e9a4270215374de84f3c71cae66f9e 100644
--- a/server/app/apis/users.py
+++ b/server/app/apis/users.py
@@ -1,19 +1,30 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import UserDTO
-from app.core.parsers import user_parser, user_search_parser
-from app.database.models import User
-from flask import request
-from flask_jwt_extended import get_jwt_identity, jwt_required
-from flask_restx import Namespace, Resource
+from flask_jwt_extended import get_jwt_identity
+from flask_restx import Resource
+from flask_restx import inputs, reqparse
+from app.core.parsers import search_parser, sentinel
 
 api = UserDTO.api
 schema = UserDTO.schema
 list_schema = UserDTO.list_schema
 
+user_parser_edit = reqparse.RequestParser()
+user_parser_edit.add_argument("email", type=inputs.email(), default=sentinel, location="json")
+user_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
+user_parser_edit.add_argument("city_id", type=int, default=sentinel, location="json")
+user_parser_edit.add_argument("role_id", type=int, default=sentinel, location="json")
 
-def edit_user(item_user, args):
+user_search_parser = search_parser.copy()
+user_search_parser.add_argument("name", type=str, default=sentinel, location="args")
+user_search_parser.add_argument("email", type=str, default=sentinel, location="args")
+user_search_parser.add_argument("city_id", type=int, default=sentinel, location="args")
+user_search_parser.add_argument("role_id", type=int, default=sentinel, location="args")
+
+
+def _edit_user(item_user, args):
     email = args.get("email")
     name = args.get("name")
 
@@ -29,38 +40,38 @@ def edit_user(item_user, args):
 
 @api.route("")
 class UsersList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         item = dbc.get.user(get_jwt_identity())
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self):
-        args = user_parser.parse_args(strict=True)
+        args = user_parser_edit.parse_args(strict=True)
         item = dbc.get.user(get_jwt_identity())
-        item = edit_user(item, args)
+        item = _edit_user(item, args)
         return item_response(schema.dump(item))
 
 
 @api.route("/<ID>")
 @api.param("ID")
 class Users(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, ID):
         item = dbc.get.user(ID)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def put(self, ID):
-        args = user_parser.parse_args(strict=True)
+        args = user_parser_edit.parse_args(strict=True)
         item = dbc.get.user(ID)
-        item = edit_user(item, args)
+        item = _edit_user(item, args)
         return item_response(schema.dump(item))
 
 
 @api.route("/search")
 class UserSearch(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         args = user_search_parser.parse_args(strict=True)
         items, total = dbc.search.user(**args)
diff --git a/server/app/core/parsers.py b/server/app/core/parsers.py
index 1e9813e99d9d131d42dd221ef893ff6eb99b81f6..160d67a0af7b58483202c00c1ec6b97097de0633 100644
--- a/server/app/core/parsers.py
+++ b/server/app/core/parsers.py
@@ -1,109 +1,25 @@
 from flask_restx import inputs, reqparse
 
-###SEARCH####
-search_parser = reqparse.RequestParser()
-search_parser.add_argument("page", type=int, default=0, location="args")
-search_parser.add_argument("page_size", type=int, default=15, location="args")
-search_parser.add_argument("order", type=int, default=1, location="args")
-search_parser.add_argument("order_by", type=str, default=None, location="args")
-
-###LOGIN####
-login_parser = reqparse.RequestParser()
-login_parser.add_argument("email", type=inputs.email(), required=True, location="json")
-login_parser.add_argument("password", required=True, location="json")
-
-###CREATE_USER####
-create_user_parser = login_parser.copy()
-create_user_parser.add_argument("city_id", type=int, required=True, location="json")
-create_user_parser.add_argument("role_id", type=int, required=True, location="json")
-
-###USER####
-user_parser = reqparse.RequestParser()
-user_parser.add_argument("email", type=inputs.email(), location="json")
-user_parser.add_argument("name", type=str, location="json")
-user_parser.add_argument("city_id", type=int, location="json")
-user_parser.add_argument("role_id", type=int, location="json")
-
-###SEARCH_USER####
-user_search_parser = search_parser.copy()
-user_search_parser.add_argument("name", type=str, default=None, location="args")
-user_search_parser.add_argument("email", type=str, default=None, location="args")
-user_search_parser.add_argument("city_id", type=int, default=None, location="args")
-user_search_parser.add_argument("role_id", type=int, default=None, location="args")
-
-
-###COMPETITION####
-competition_parser = reqparse.RequestParser()
-competition_parser.add_argument("name", type=str, location="json")
-competition_parser.add_argument("year", type=int, location="json")
-competition_parser.add_argument("city_id", type=int, location="json")
-
-
-###SEARCH_COMPETITION####
-competition_search_parser = search_parser.copy()
-competition_search_parser.add_argument("name", type=str, default=None, location="args")
-competition_search_parser.add_argument("year", type=int, default=None, location="args")
-competition_search_parser.add_argument("city_id", type=int, default=None, location="args")
-
 
-###SLIDER_PARSER####
-slide_parser = reqparse.RequestParser()
-slide_parser.add_argument("order", type=int, default=None, location="json")
-slide_parser.add_argument("title", type=str, default=None, location="json")
-slide_parser.add_argument("timer", type=int, default=None, location="json")
+class Sentinel:
+    """
+    Sentinel is used as default argument to parsers if it isn't necessary to
+    supply a value. This is used instead of None so that None can be supplied
+    as value.
+    """
 
+    def __repr__(self):
+        return "Sentinel"
 
-###QUESTION####
-question_parser = reqparse.RequestParser()
-question_parser.add_argument("name", type=str, default=None, location="json")
-question_parser.add_argument("total_score", type=int, default=None, location="json")
-question_parser.add_argument("type_id", type=int, default=None, location="json")
+    def __bool__(self):
+        return False
 
 
-###QUESTION ALTERNATIVES####
-question_alternative_parser = reqparse.RequestParser()
-question_alternative_parser.add_argument("text", type=str, default=None, location="json")
-question_alternative_parser.add_argument("value", type=int, default=None, location="json")
+sentinel = Sentinel()
 
-###QUESTION ANSWERS####
-question_answer_parser = reqparse.RequestParser()
-question_answer_parser.add_argument("data", type=dict, required=True, location="json")
-question_answer_parser.add_argument("score", type=int, required=True, location="json")
-question_answer_parser.add_argument("question_id", type=int, required=True, location="json")
-
-###QUESTION ANSWERS EDIT####
-question_answer_edit_parser = reqparse.RequestParser()
-question_answer_edit_parser.add_argument("data", type=dict, default=None, location="json")
-question_answer_edit_parser.add_argument("score", type=int, default=None, location="json")
-
-###CODE####
-code_parser = reqparse.RequestParser()
-code_parser.add_argument("pointer", type=str, default=None, location="json")
-code_parser.add_argument("view_type_id", type=int, default=None, location="json")
-
-
-###TEAM####
-team_parser = reqparse.RequestParser()
-team_parser.add_argument("name", type=str, location="json")
-
-###SEARCH_COMPETITION####
-media_parser_search = search_parser.copy()
-media_parser_search.add_argument("filename", type=str, default=None, location="args")
-
-
-###COMPONENT###
-component_parser = reqparse.RequestParser()
-component_parser.add_argument("x", type=str, default=None, location="json")
-component_parser.add_argument("y", type=int, default=None, location="json")
-component_parser.add_argument("w", type=int, default=None, location="json")
-component_parser.add_argument("h", type=int, default=None, location="json")
-# component_parser.add_argument("data", type=dict, default=None, location="json")
-
-component_create_parser = component_parser.copy()
-# component_create_parser.replace_argument("data", type=dict, required=True, location="json")
-component_create_parser.add_argument("type_id", type=int, required=True, location="json")
-component_create_parser.add_argument("text", type=str, required=False, location="json")
-component_create_parser.add_argument("media_id", type=str, required=False, location="json")
-
-login_code_parser = reqparse.RequestParser()
-login_code_parser.add_argument("code", type=str, location="json")
+###SEARCH####
+search_parser = reqparse.RequestParser()
+search_parser.add_argument("page", type=int, default=0, location="args")
+search_parser.add_argument("page_size", type=int, default=15, location="args")
+search_parser.add_argument("order", type=int, default=1, location="args")
+search_parser.add_argument("order_by", type=str, default=None, location="args")
diff --git a/server/app/core/rich_schemas.py b/server/app/core/rich_schemas.py
index da5f58d653ea5efb6c00648db56c1bc81a24d44b..ae1c6ab6e0bbd63ebd1369e1741672a6489c87fa 100644
--- a/server/app/core/rich_schemas.py
+++ b/server/app/core/rich_schemas.py
@@ -42,8 +42,7 @@ class SlideSchemaRich(RichSchema):
     title = ma.auto_field()
     timer = ma.auto_field()
     competition_id = ma.auto_field()
-    background_image_id = ma.auto_field()
-    background_image = ma.Function(lambda x: x.background_image.filename if x.background_image is not None else "")
+    background_image = fields.Nested(schemas.MediaSchema, many=False)
     questions = fields.Nested(QuestionSchemaRich, many=True)
     components = fields.Nested(schemas.ComponentSchema, many=True)
 
@@ -56,8 +55,7 @@ class CompetitionSchemaRich(RichSchema):
     name = ma.auto_field()
     year = ma.auto_field()
     city_id = ma.auto_field()
-    background_image_id = ma.auto_field()
-    background_image = ma.Function(lambda x: x.background_image.filename if x.background_image is not None else "")
+    background_image = fields.Nested(schemas.MediaSchema, many=False)
 
     slides = fields.Nested(
         SlideSchemaRich,
diff --git a/server/app/core/schemas.py b/server/app/core/schemas.py
index 4e440fc4b5e282201f19602a428a506e763f670a..4008b8869cc084022d4d46386d89ec29ca1765d2 100644
--- a/server/app/core/schemas.py
+++ b/server/app/core/schemas.py
@@ -1,5 +1,5 @@
 from marshmallow.decorators import pre_load
-from marshmallow.decorators import pre_dump
+from marshmallow.decorators import pre_dump, post_dump
 import app.database.models as models
 from app.core import ma
 from marshmallow_sqlalchemy import fields
@@ -65,7 +65,7 @@ class QuestionAnswerSchema(BaseSchema):
         model = models.QuestionAnswer
 
     id = ma.auto_field()
-    data = ma.Function(lambda obj: obj.data)
+    answer = ma.auto_field()
     score = ma.auto_field()
     question_id = ma.auto_field()
     team_id = ma.auto_field()
@@ -116,8 +116,7 @@ class SlideSchema(BaseSchema):
     title = ma.auto_field()
     timer = ma.auto_field()
     competition_id = ma.auto_field()
-    background_image_id = ma.auto_field()
-    background_image = ma.Function(lambda x: x.background_image.filename if x.background_image is not None else "")
+    background_image = fields.Nested(MediaSchema, many=False)
 
 
 class TeamSchema(BaseSchema):
@@ -148,8 +147,7 @@ class CompetitionSchema(BaseSchema):
     name = ma.auto_field()
     year = ma.auto_field()
     city_id = ma.auto_field()
-    background_image_id = ma.auto_field()
-    background_image = ma.Function(lambda x: x.background_image.filename if x.background_image is not None else "")
+    background_image = fields.Nested(MediaSchema, many=False)
 
 
 class ComponentSchema(BaseSchema):
@@ -163,7 +161,8 @@ class ComponentSchema(BaseSchema):
     h = ma.auto_field()
     slide_id = ma.auto_field()
     type_id = ma.auto_field()
+    view_type_id = ma.auto_field()
 
     text = fields.fields.String()
-    media_id = fields.fields.Integer()
-    filename = ma.Function(lambda x: x.media.filename if hasattr(x, "media_id") else "")
+    media = fields.Nested(MediaSchema, many=False)
+    question_id = fields.fields.Integer()
\ No newline at end of file
diff --git a/server/app/core/sockets.py b/server/app/core/sockets.py
index 04099ff2fc3268301d6a1e5cc130f30774a8bc16..b62f82c9365818ac76faa0cd05e09bc795d0c071 100644
--- a/server/app/core/sockets.py
+++ b/server/app/core/sockets.py
@@ -9,7 +9,7 @@ logger = logging.getLogger(__name__)
 logger.propagate = False
 logger.setLevel(logging.INFO)
 
-formatter = logging.Formatter('[%(levelname)s] %(funcName)s: %(message)s')
+formatter = logging.Formatter("[%(levelname)s] %(funcName)s: %(message)s")
 stream_handler = logging.StreamHandler()
 stream_handler.setFormatter(formatter)
 logger.addHandler(stream_handler)
@@ -44,7 +44,9 @@ def start_presentation(data):
     competition_id = data["competition_id"]
 
     if competition_id in presentations:
-        logger.error(f"Client '{request.sid}' failed to start competition '{competition_id}', presentation already active")
+        logger.error(
+            f"Client '{request.sid}' failed to start competition '{competition_id}', presentation already active"
+        )
         return
 
     presentations[competition_id] = {
@@ -58,16 +60,21 @@ def start_presentation(data):
 
     logger.info(f"Client '{request.sid}' started competition '{competition_id}'")
 
+
 @sio.on("end_presentation")
 def end_presentation(data):
     competition_id = data["competition_id"]
 
     if competition_id not in presentations:
-        logger.error(f"Client '{request.sid}' failed to end presentation '{competition_id}', no such presentation exists")
+        logger.error(
+            f"Client '{request.sid}' failed to end presentation '{competition_id}', no such presentation exists"
+        )
         return
 
     if request.sid not in presentations[competition_id]["clients"]:
-        logger.error(f"Client '{request.sid}' failed to end presentation '{competition_id}', client not in presentation")
+        logger.error(
+            f"Client '{request.sid}' failed to end presentation '{competition_id}', client not in presentation"
+        )
         return
 
     if presentations[competition_id]["clients"][request.sid]["view_type"] != "Operator":
@@ -93,18 +100,18 @@ def join_presentation(data):
         logger.error(f"Client '{request.sid}' failed to join presentation with code '{code}', no such code exists")
         return
 
-    competition_id = (
-        item_code.pointer
-        if item_code.view_type_id != team_view_id
-        else db.session.query(Team).filter(Team.id == item_code.pointer).one().competition_id
-    )
+    competition_id = item_code.competition_id
 
     if competition_id not in presentations:
-        logger.error(f"Client '{request.sid}' failed to join presentation '{competition_id}', no such presentation exists")
+        logger.error(
+            f"Client '{request.sid}' failed to join presentation '{competition_id}', no such presentation exists"
+        )
         return
 
     if request.sid in presentations[competition_id]["clients"]:
-        logger.error(f"Client '{request.sid}' failed to join presentation '{competition_id}', client already in presentation")
+        logger.error(
+            f"Client '{request.sid}' failed to join presentation '{competition_id}', client already in presentation"
+        )
         return
 
     # TODO: Write function in database controller to do this
@@ -124,21 +131,29 @@ def set_slide(data):
     slide_order = data["slide_order"]
 
     if competition_id not in presentations:
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', no such presentation exists")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', no such presentation exists"
+        )
         return
 
     if request.sid not in presentations[competition_id]["clients"]:
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client not in presentation")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client not in presentation"
+        )
         return
 
     if presentations[competition_id]["clients"][request.sid]["view_type"] != "Operator":
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client is not operator")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client is not operator"
+        )
         return
 
     num_slides = db.session.query(Slide).filter(Slide.competition_id == competition_id).count()
 
     if not (0 <= slide_order < num_slides):
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', slide number {slide_order} does not exist")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', slide number {slide_order} does not exist"
+        )
         return
 
     presentations[competition_id]["slide"] = slide_order
@@ -155,15 +170,21 @@ def set_timer(data):
     timer = data["timer"]
 
     if competition_id not in presentations:
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', no such presentation exists")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', no such presentation exists"
+        )
         return
 
     if request.sid not in presentations[competition_id]["clients"]:
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client not in presentation")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client not in presentation"
+        )
         return
 
     if presentations[competition_id]["clients"][request.sid]["view_type"] != "Operator":
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client is not operator")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client is not operator"
+        )
         return
 
     # TODO: Save timer in presentation, maybe?
@@ -172,4 +193,3 @@ def set_timer(data):
     logger.debug(f"Emitting event 'set_timer' to room {competition_id} including self")
 
     logger.info(f"Client '{request.sid}' set timer '{timer}' in presentation '{competition_id}'")
-
diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py
index dceccfe85851a9dffe24032c0df5cc199f056ab0..6c5ea14bcd143a4bac5d0f214fc3d14f8f73bfe0 100644
--- a/server/app/database/controller/add.py
+++ b/server/app/database/controller/add.py
@@ -20,6 +20,7 @@ from app.database.models import (
     Question,
     QuestionAlternative,
     QuestionAnswer,
+    QuestionComponent,
     QuestionType,
     Role,
     Slide,
@@ -37,6 +38,8 @@ from sqlalchemy.orm import relation
 from sqlalchemy.orm.session import sessionmaker
 from flask import current_app
 
+from app.database.types import ID_IMAGE_COMPONENT, ID_QUESTION_COMPONENT, ID_TEXT_COMPONENT
+
 
 def db_add(item):
     """
@@ -59,7 +62,7 @@ def db_add(item):
     return item
 
 
-def component(type_id, slide_id, x=0, y=0, w=0, h=0, **data):
+def component(type_id, slide_id, view_type_id, x=0, y=0, w=0, h=0, **data):
     """
     Adds a component to the slide at the specified coordinates with the
     provided size and data .
@@ -79,12 +82,15 @@ def component(type_id, slide_id, x=0, y=0, w=0, h=0, **data):
         w *= ratio
         h *= ratio
 
-    if type_id == 1:
-        item = db_add(TextComponent(slide_id, type_id, x, y, w, h))
+    if type_id == ID_TEXT_COMPONENT:
+        item = db_add(TextComponent(slide_id, type_id, view_type_id, x, y, w, h))
         item.text = data.get("text")
-    elif type_id == 2:
-        item = db_add(ImageComponent(slide_id, type_id, x, y, w, h))
+    elif type_id == ID_IMAGE_COMPONENT:
+        item = db_add(ImageComponent(slide_id, type_id, view_type_id, x, y, w, h))
         item.media_id = data.get("media_id")
+    elif type_id == ID_QUESTION_COMPONENT:
+        item = db_add(QuestionComponent(slide_id, type_id, view_type_id, x, y, w, h))
+        item.question_id = data.get("question_id")
     else:
         abort(codes.BAD_REQUEST, f"Invalid type_id{type_id}")
 
@@ -151,9 +157,13 @@ def competition(name, year, city_id):
 
     # Add code for Judge view
     code(2, item_competition.id)
+
     # Add code for Audience view
     code(3, item_competition.id)
 
+    # Add code for Operator view
+    code(4, item_competition.id)
+
     item_competition = utils.refresh(item_competition)
     return item_competition
 
@@ -246,5 +256,5 @@ def question_alternative(text, value, question_id):
     return db_add(QuestionAlternative(text, value, question_id))
 
 
-def question_answer(data, score, question_id, team_id):
-    return db_add(QuestionAnswer(data, score, question_id, team_id))
+def question_answer(answer, score, question_id, team_id):
+    return db_add(QuestionAnswer(answer, score, question_id, team_id))
diff --git a/server/app/database/controller/copy.py b/server/app/database/controller/copy.py
index 87e3f831542aac9478566e916d12118f66a7c120..a32ba1deda39c231bfa7ea2b26f91337e0ee2bec 100644
--- a/server/app/database/controller/copy.py
+++ b/server/app/database/controller/copy.py
@@ -4,6 +4,7 @@ This file contains functionality to copy and duplicate data to the database.
 
 from app.database.controller import add, get, search, utils
 from app.database.models import Question
+from app.database.types import ID_IMAGE_COMPONENT, ID_QUESTION_COMPONENT, ID_TEXT_COMPONENT
 
 
 def _alternative(item_old, question_id):
@@ -38,13 +39,16 @@ def _component(item_component, item_slide_new):
     component item to the specified slide.
     """
     data = {}
-    if item_component.type_id == 1:
+    if item_component.type_id == ID_TEXT_COMPONENT:
         data["text"] = item_component.text
-    elif item_component.type_id == 2:
+    elif item_component.type_id == ID_IMAGE_COMPONENT:
         data["media_id"] = item_component.media_id
+    elif item_component.type_id == ID_QUESTION_COMPONENT:
+        data["question_id"] = item_component.question_id
     add.component(
         item_component.type_id,
         item_slide_new.id,
+        item_component.view_type_id,
         item_component.x,
         item_component.y,
         item_component.w,
@@ -77,8 +81,7 @@ def slide_to_competition(item_slide_old, item_competition):
     item_slide_new.body = item_slide_old.body
     item_slide_new.timer = item_slide_old.timer
     item_slide_new.settings = item_slide_old.settings
-
-    # TODO: Add background image
+    item_slide_new.background_image_id = item_slide_old.background_image_id
 
     for item_component in item_slide_old.components:
         _component(item_component, item_slide_new)
@@ -109,6 +112,7 @@ def competition(item_competition_old):
         item_competition_old.font,
     )
     # TODO: Add background image
+    item_competition_new.background_image_id = item_competition_old.background_image_id
 
     for item_slide in item_competition_old.slides:
         slide_to_competition(item_slide, item_competition_new)
diff --git a/server/app/database/controller/edit.py b/server/app/database/controller/edit.py
index b1f5bc91e045e43c5f69618a685a39f86c7b081d..efb4909696cf2ae911a2451e1427b60b460f1153 100644
--- a/server/app/database/controller/edit.py
+++ b/server/app/database/controller/edit.py
@@ -3,6 +3,7 @@ This file contains functionality to get data from the database.
 """
 
 from app.core import db
+from app.core.parsers import sentinel
 
 
 def switch_order(item1, item2):
@@ -46,7 +47,7 @@ def default(item, **kwargs):
     for key, value in kwargs.items():
         if not hasattr(item, key):
             raise AttributeError(f"Item of type {type(item)} has no attribute '{key}'")
-        if value is not None:
+        if value is not sentinel:
             setattr(item, key, value)
     db.session.commit()
     db.session.refresh(item)
diff --git a/server/app/database/models.py b/server/app/database/models.py
index 1ed99d911700c64349816e937230f2cd0e90e437..f9f18438ad07edf951cebd24ad7ab92e9b854299 100644
--- a/server/app/database/models.py
+++ b/server/app/database/models.py
@@ -1,8 +1,7 @@
 from app.core import bcrypt, db
-from app.database import Dictionary
 from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property
 
-from app.database.types import ID_IMAGE_COMPONENT, ID_TEXT_COMPONENT
+from app.database.types import ID_IMAGE_COMPONENT, ID_QUESTION_COMPONENT, ID_TEXT_COMPONENT
 
 STRING_SIZE = 254
 
@@ -175,7 +174,7 @@ class QuestionAlternative(db.Model):
 class QuestionAnswer(db.Model):
     __table_args__ = (db.UniqueConstraint("question_id", "team_id"),)
     id = db.Column(db.Integer, primary_key=True)
-    data = db.Column(Dictionary(), nullable=False)
+    answer = db.Column(db.String(STRING_SIZE), nullable=False)
     score = db.Column(db.Integer, nullable=False, default=0)
 
     question_id = db.Column(db.Integer, db.ForeignKey("question.id"), nullable=False)
@@ -200,13 +199,14 @@ class Component(db.Model):
 
     __mapper_args__ = {"polymorphic_on": type_id}
 
-    def __init__(self, slide_id, type_id, x=0, y=0, w=1, h=1):
+    def __init__(self, slide_id, type_id, view_type_id, x=0, y=0, w=1, h=1):
         self.x = x
         self.y = y
         self.w = w
         self.h = h
         self.slide_id = slide_id
         self.type_id = type_id
+        self.view_type_id = view_type_id
 
 
 class TextComponent(Component):
@@ -224,6 +224,13 @@ class ImageComponent(Component):
     __mapper_args__ = {"polymorphic_identity": ID_IMAGE_COMPONENT}
 
 
+class QuestionComponent(Component):
+    question_id = db.Column(db.Integer, db.ForeignKey("question.id"), nullable=True)
+
+    # __tablename__ = None
+    __mapper_args__ = {"polymorphic_identity": ID_QUESTION_COMPONENT}
+
+
 class Code(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     code = db.Column(db.Text, unique=True)
@@ -231,6 +238,8 @@ class Code(db.Model):
     competition_id = db.Column(db.Integer, db.ForeignKey("competition.id"), nullable=False)
     team_id = db.Column(db.Integer, db.ForeignKey("team.id"), nullable=True)
 
+    view_type = db.relationship("ViewType", uselist=False)
+
     def __init__(self, code, view_type_id, competition_id=None, team_id=None):
         self.code = code
         self.view_type_id = view_type_id
@@ -241,7 +250,6 @@ class Code(db.Model):
 class ViewType(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     name = db.Column(db.String(STRING_SIZE), unique=True)
-    codes = db.relationship("Code", backref="view_type")
 
     def __init__(self, name):
         self.name = name
diff --git a/server/app/database/types.py b/server/app/database/types.py
index 236e50f5278d03684f2cbdcc05c2e7637e21a057..f53835eccf10599eb9ab81d8af1426e02a21e405 100644
--- a/server/app/database/types.py
+++ b/server/app/database/types.py
@@ -1,2 +1,3 @@
 ID_TEXT_COMPONENT = 1
 ID_IMAGE_COMPONENT = 2
+ID_QUESTION_COMPONENT = 3
\ No newline at end of file
diff --git a/server/populate.py b/server/populate.py
index f323a525bcf7c639aecc66d57bed61eaf71b1db9..92183b6cc87f8f2e5377fb934c3ff14219920a7f 100644
--- a/server/populate.py
+++ b/server/populate.py
@@ -86,7 +86,13 @@ def _add_items():
                 y = random.randrange(1, 500)
                 w = random.randrange(150, 400)
                 h = random.randrange(150, 400)
-                dbc.add.component(1, item_slide.id, x, y, w, h, text=f"hej{k}")
+                dbc.add.component(1, item_slide.id, 1, x, y, w, h, text=f"hej{k}")
+            for k in range(3):
+                x = random.randrange(1, 500)
+                y = random.randrange(1, 500)
+                w = random.randrange(150, 400)
+                h = random.randrange(150, 400)
+                dbc.add.component(1, item_slide.id, 3, x, y, w, h, text=f"hej{k}")
 
         # item_slide = dbc.add.slide(item_comp)
         # item_slide.title = f"Slide {len(item_comp.slides)}"
diff --git a/server/tests/test_app.py b/server/tests/test_app.py
index 199df387e878fdbbde759a80e6aa3d9ea486ea7d..d59428a6380b74abe8853c6c09c4df0bafc031e0 100644
--- a/server/tests/test_app.py
+++ b/server/tests/test_app.py
@@ -3,7 +3,9 @@ This file tests the api function calls.
 """
 
 import app.core.http_codes as codes
+from app.database.controller.add import competition
 from app.database.models import Slide
+from app.core import sockets
 
 from tests import app, client, db
 from tests.test_helpers import add_default_values, change_order_test, delete, get, post, put
@@ -342,7 +344,7 @@ def test_question_api(client):
     slide_order = 1
     response, body = get(client, f"/api/competitions/{CID}/questions", headers=headers)
     assert response.status_code == codes.OK
-    assert body["count"] == 1
+    assert body["count"] == 2
 
     # Get questions from another competition that should have some questions
     CID = 3
@@ -385,3 +387,67 @@ def test_question_api(client):
     response, _ = delete(client, f"/api/competitions/{CID}/slides/{NEW_slide_order}/questions/{QID}", headers=headers)
     assert response.status_code == codes.NOT_FOUND
     """
+
+
+def test_authorization(client):
+    add_default_values()
+
+    # Fake that competition 1 is active
+    sockets.presentations[1] = {}
+
+    #### TEAM ####
+    # Login in with team code
+    response, body = post(client, "/api/auth/login/code", {"code": "111111"})
+    assert response.status_code == codes.OK
+    headers = {"Authorization": "Bearer " + body["access_token"]}
+
+    competition_id = body["competition_id"]
+    team_id = body["team_id"]
+
+    # Get competition team is in
+    response, body = get(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Try to delete competition team is in
+    response, body = delete(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Try to get a different competition
+    response, body = get(client, f"/api/competitions/{competition_id+1}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Get own answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id}/answers", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Try to get another teams answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id+1}/answers", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    #### JUDGE ####
+    # Login in with judge code
+    response, body = post(client, "/api/auth/login/code", {"code": "222222"})
+    assert response.status_code == codes.OK
+    headers = {"Authorization": "Bearer " + body["access_token"]}
+
+    competition_id = body["competition_id"]
+
+    # Get competition judge is in
+    response, body = get(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Try to delete competition judge is in
+    response, body = delete(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Try to get a different competition
+    response, body = get(client, f"/api/competitions/{competition_id+1}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Get team answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id}/answers", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Also get antoher teams answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id+1}/answers", headers=headers)
+    assert response.status_code == codes.OK
\ No newline at end of file
diff --git a/server/tests/test_helpers.py b/server/tests/test_helpers.py
index b5f1e54e136b759d9924a534acf2f37e6f7b08cd..f55aa68251a5a3b9ea73411c959939acc93f1bc6 100644
--- a/server/tests/test_helpers.py
+++ b/server/tests/test_helpers.py
@@ -3,14 +3,14 @@ import json
 import app.core.http_codes as codes
 import app.database.controller as dbc
 from app.core import db
-from app.database.models import City, Role
+from app.database.models import City, Code, Role
 
 
 def add_default_values():
     media_types = ["Image", "Video"]
     question_types = ["Boolean", "Multiple", "Text"]
     component_types = ["Text", "Image"]
-    view_types = ["Team", "Judge", "Audience"]
+    view_types = ["Team", "Judge", "Audience", "Operator"]
 
     roles = ["Admin", "Editor"]
     cities = ["Linköping", "Testköping"]
@@ -40,6 +40,20 @@ def add_default_values():
 
     # Add competitions
     item_competition = dbc.add.competition("Tom tävling", 2012, item_city.id)
+
+    item_question = dbc.add.question("hej", 5, 1, item_competition.slides[0].id)
+
+    item_team1 = dbc.add.team("Hej lag 3", item_competition.id)
+    item_team2 = dbc.add.team("Hej lag 4", item_competition.id)
+
+    db.session.add(Code("111111", 1, item_competition.id, item_team1.id))  # Team
+    db.session.add(Code("222222", 2, item_competition.id))  # Judge
+
+    dbc.add.QuestionAnswer("hej", 5, item_question.id, item_team1)
+    dbc.add.QuestionAnswer("då", 5, item_question.id, item_team2)
+
+    db.session.commit()
+
     for j in range(2):
         item_comp = dbc.add.competition(f"Tävling {j}", 2012, item_city.id)
         # Add two more slides to competition
@@ -59,7 +73,7 @@ def add_default_values():
             # dbc.add.question(name=f"Q{i+1}", total_score=i + 1, type_id=1, slide_id=item_slide.id)
 
             # Add text component
-            dbc.add.component(1, item_slide.id, i, 2 * i, 3 * i, 4 * i, text="Text")
+            dbc.add.component(1, item_slide.id, 1, i, 2 * i, 3 * i, 4 * i, text="Text")
 
 
 def get_body(response):