Skip to content
Snippets Groups Projects

Compare revisions

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

Source

Select target project
No results found

Target

Select target project
  • tddd96-grupp11/teknikattan-scoring-system
1 result
Show changes
Showing
with 544 additions and 60 deletions
import { Checkbox, ListItem, ListItemText, Typography, withStyles } from '@material-ui/core'
import { CheckboxProps } from '@material-ui/core/Checkbox'
import { green, grey } from '@material-ui/core/colors'
import axios from 'axios'
import React from 'react'
import { getEditorCompetition } from '../../../../actions/editor'
import { getPresentationCompetition } from '../../../../actions/presentation'
import { useAppDispatch, useAppSelector } from '../../../../hooks'
import { QuestionAlternative } from '../../../../interfaces/ApiModels'
import { RichSlide } from '../../../../interfaces/ApiRichModels'
import { Center } from '../styled'
type AnswerMultipleProps = {
variant: 'editor' | 'presentation'
activeSlide: RichSlide | undefined
competitionId: string
}
const AnswerMultiple = ({ variant, activeSlide, competitionId }: AnswerMultipleProps) => {
const dispatch = useAppDispatch()
const teamId = useAppSelector((state) => state.competitionLogin.data?.team_id)
const team = useAppSelector((state) => state.presentation.competition.teams.find((team) => team.id === teamId))
const answer = team?.question_answers.find((answer) => answer.question_id === activeSlide?.questions[0].id)
const decideChecked = (alternative: QuestionAlternative) => {
const teamAnswer = team?.question_answers.find((answer) => answer.answer === alternative.text)?.answer
if (alternative.text === teamAnswer) return true
else return false
}
const updateAnswer = async (alternative: QuestionAlternative) => {
// TODO: fix. Make list of alternatives and delete & post instead of put to allow multiple boxes checked.
if (activeSlide) {
if (team?.question_answers.find((answer) => answer.question_id === activeSlide.questions[0].id)) {
if (answer?.answer === alternative.text) {
// Uncheck checkbox
deleteAnswer()
} else {
// Check another box
// TODO
}
} else {
// Check first checkbox
await axios
.post(`/api/competitions/${competitionId}/teams/${teamId}/answers`, {
answer: alternative.text,
score: 0,
question_id: activeSlide.questions[0].id,
})
.then(() => {
if (variant === 'editor') {
dispatch(getEditorCompetition(competitionId))
} else {
dispatch(getPresentationCompetition(competitionId))
}
})
.catch(console.log)
}
}
}
const deleteAnswer = async () => {
await axios
.delete(`/api/competitions/${competitionId}/teams/${teamId}/answers`) // TODO: fix
.then(() => {
dispatch(getEditorCompetition(competitionId))
})
.catch(console.log)
}
const GreenCheckbox = withStyles({
root: {
color: grey[900],
'&$checked': {
color: green[600],
},
},
checked: {},
})((props: CheckboxProps) => <Checkbox color="default" {...props} />)
return (
<div>
<ListItem divider>
<Center>
<ListItemText primary="Välj ett eller flera svar:" />
</Center>
</ListItem>
{activeSlide &&
activeSlide.questions[0] &&
activeSlide.questions[0].alternatives &&
activeSlide.questions[0].alternatives.map((alt) => (
<div key={alt.id}>
<ListItem divider>
{
//<GreenCheckbox checked={checkbox} onChange={(event) => updateAnswer(alt, event.target.checked)} />
}
<GreenCheckbox checked={decideChecked(alt)} onChange={() => updateAnswer(alt)} />
<Typography style={{ wordBreak: 'break-all' }}>{alt.text}</Typography>
</ListItem>
</div>
))}
</div>
)
}
export default AnswerMultiple
import { Checkbox, ListItem, ListItemText, Typography, withStyles } from '@material-ui/core'
import { CheckboxProps } from '@material-ui/core/Checkbox'
import { green, grey } from '@material-ui/core/colors'
import RadioButtonCheckedIcon from '@material-ui/icons/RadioButtonCheckedOutlined'
import RadioButtonUncheckedIcon from '@material-ui/icons/RadioButtonUncheckedOutlined'
import axios from 'axios'
import React from 'react'
import { getEditorCompetition } from '../../../../actions/editor'
import { getPresentationCompetition } from '../../../../actions/presentation'
import { useAppDispatch, useAppSelector } from '../../../../hooks'
import { QuestionAlternative } from '../../../../interfaces/ApiModels'
import { RichSlide } from '../../../../interfaces/ApiRichModels'
import { Center, Clickable } from '../styled'
type AnswerSingleProps = {
variant: 'editor' | 'presentation'
activeSlide: RichSlide | undefined
competitionId: string
}
const AnswerSingle = ({ variant, activeSlide, competitionId }: AnswerSingleProps) => {
const dispatch = useAppDispatch()
const teamId = useAppSelector((state) => state.competitionLogin.data?.team_id)
const team = useAppSelector((state) => {
if (variant === 'editor') return state.editor.competition.teams.find((team) => team.id === teamId)
return state.presentation.competition.teams.find((team) => team.id === teamId)
})
const answerId = team?.question_answers.find((answer) => answer.question_id === activeSlide?.questions[0].id)?.id
const decideChecked = (alternative: QuestionAlternative) => {
const teamAnswer = team?.question_answers.find((answer) => answer.answer === alternative.text)?.answer
if (teamAnswer) return true
else return false
}
const updateAnswer = async (alternative: QuestionAlternative) => {
if (activeSlide) {
// TODO: ignore API calls when an answer is already checked
if (team?.question_answers[0]) {
await axios
.put(`/api/competitions/${competitionId}/teams/${teamId}/answers/${answerId}`, {
answer: alternative.text,
})
.then(() => {
if (variant === 'editor') {
dispatch(getEditorCompetition(competitionId))
} else {
dispatch(getPresentationCompetition(competitionId))
}
})
.catch(console.log)
} else {
await axios
.post(`/api/competitions/${competitionId}/teams/${teamId}/answers`, {
answer: alternative.text,
score: 0,
question_id: activeSlide.questions[0].id,
})
.then(() => {
if (variant === 'editor') {
dispatch(getEditorCompetition(competitionId))
} else {
dispatch(getPresentationCompetition(competitionId))
}
})
.catch(console.log)
}
}
}
const deleteAnswer = async () => {
await axios
.delete(`/api/competitions/${competitionId}/teams/${teamId}/answers`)
.then(() => {
dispatch(getEditorCompetition(competitionId))
})
.catch(console.log)
}
const GreenCheckbox = withStyles({
root: {
color: grey[900],
'&$checked': {
color: green[600],
},
},
checked: {},
})((props: CheckboxProps) => <Checkbox color="default" {...props} />)
const renderRadioButton = (alt: QuestionAlternative) => {
if (variant === 'presentation') {
if (decideChecked(alt)) {
return (
<Clickable>
<RadioButtonCheckedIcon onClick={() => updateAnswer(alt)} />
</Clickable>
)
} else {
return (
<Clickable>
<RadioButtonUncheckedIcon onClick={() => updateAnswer(alt)} />
</Clickable>
)
}
} else {
return <RadioButtonUncheckedIcon onClick={() => updateAnswer(alt)} />
}
}
return (
<div>
<ListItem divider>
<Center>
<ListItemText primary="Välj ett svar:" />
</Center>
</ListItem>
{activeSlide &&
activeSlide.questions[0] &&
activeSlide.questions[0].alternatives &&
activeSlide.questions[0].alternatives.map((alt) => (
<div key={alt.id}>
<ListItem divider>
{renderRadioButton(alt)}
<Typography style={{ wordBreak: 'break-all' }}>{alt.text}</Typography>
</ListItem>
</div>
))}
</div>
)
}
export default AnswerSingle
import { ListItem, ListItemText, TextField } from '@material-ui/core'
import axios from 'axios'
import React from 'react'
import { getEditorCompetition } from '../../../../actions/editor'
import { useAppDispatch, useAppSelector } from '../../../../hooks'
import { RichSlide } from '../../../../interfaces/ApiRichModels'
import { Center } from '../styled'
import { AnswerTextFieldContainer } from './styled'
type AnswerTextProps = {
activeSlide: RichSlide | undefined
competitionId: string
}
const AnswerText = ({ activeSlide, competitionId }: AnswerTextProps) => {
const [timerHandle, setTimerHandle] = React.useState<number | undefined>(undefined)
const dispatch = useAppDispatch()
const teamId = useAppSelector((state) => state.competitionLogin.data?.team_id)
const team = useAppSelector((state) => state.presentation.competition.teams.find((team) => team.id === teamId))
const answerId = team?.question_answers.find((answer) => answer.question_id === activeSlide?.questions[0].id)?.id
const onAnswerChange = (answer: string) => {
if (timerHandle) {
clearTimeout(timerHandle)
setTimerHandle(undefined)
}
//Only updates answer 100ms after last input was made
setTimerHandle(window.setTimeout(() => updateAnswer(answer), 100))
}
const updateAnswer = async (answer: string) => {
if (activeSlide && team) {
if (team?.question_answers.find((answer) => answer.question_id === activeSlide.questions[0].id)) {
await axios
.put(`/api/competitions/${competitionId}/teams/${teamId}/answers/${answerId}`, {
answer,
})
.then(() => {
dispatch(getEditorCompetition(competitionId))
})
.catch(console.log)
} else {
await axios
.post(`/api/competitions/${competitionId}/teams/${teamId}/answers`, {
answer,
score: 0,
question_id: activeSlide.questions[0].id,
})
.then(() => {
dispatch(getEditorCompetition(competitionId))
})
.catch(console.log)
}
}
}
return (
<AnswerTextFieldContainer>
<ListItem divider>
<Center>
<ListItemText primary="Skriv ditt svar nedan" />
</Center>
</ListItem>
<ListItem style={{ height: '100%' }}>
<TextField
disabled={team === undefined}
defaultValue={
team?.question_answers.find((questionAnswer) => questionAnswer.id === answerId)?.answer || 'Svar...'
}
style={{ height: '100%' }}
variant="outlined"
fullWidth={true}
multiline
onChange={(event) => onAnswerChange(event.target.value)}
/>
</ListItem>
</AnswerTextFieldContainer>
)
}
export default AnswerText
import styled from 'styled-components'
export const AnswerTextFieldContainer = styled.div`
height: calc(100% - 90px);
`
import { ListItem, ListItemText } from '@material-ui/core'
import CloseIcon from '@material-ui/icons/Close'
import RadioButtonCheckedIcon from '@material-ui/icons/RadioButtonCheckedOutlined'
import RadioButtonUncheckedIcon from '@material-ui/icons/RadioButtonUncheckedOutlined'
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, AlternativeTextField, Center, Clickable, SettingsList } from '../styled'
type SingleChoiceAlternativeProps = {
activeSlide: RichSlide
competitionId: string
}
const SingleChoiceAlternatives = ({ activeSlide, competitionId }: SingleChoiceAlternativeProps) => {
const dispatch = useAppDispatch()
const activeSlideId = useAppSelector((state) => state.editor.activeSlideId)
const updateAlternativeValue = async (alternative: QuestionAlternative) => {
if (activeSlide && activeSlide.questions[0]) {
// Remove check from previously checked alternative
const previousCheckedAltId = activeSlide.questions[0].alternatives.find((alt) => alt.value === 1)?.id
if (previousCheckedAltId !== alternative.id) {
if (previousCheckedAltId) {
axios.put(
`/api/competitions/${competitionId}/slides/${activeSlide?.id}/questions/${activeSlide?.questions[0].id}/alternatives/${previousCheckedAltId}`,
{ value: 0 }
)
}
// Set new checked alternative
await axios
.put(
`/api/competitions/${competitionId}/slides/${activeSlide?.id}/questions/${activeSlide?.questions[0].id}/alternatives/${alternative.id}`,
{ value: 1 }
)
.then(() => {
dispatch(getEditorCompetition(competitionId))
})
.catch(console.log)
}
}
}
const updateAlternativeText = async (alternative_id: number, newText: string) => {
if (activeSlide && activeSlide.questions[0]) {
await axios
.put(
`/api/competitions/${competitionId}/slides/${activeSlide?.id}/questions/${activeSlide?.questions[0].id}/alternatives/${alternative_id}`,
{ text: newText }
)
.then(() => {
dispatch(getEditorCompetition(competitionId))
})
.catch(console.log)
}
}
const addAlternative = async () => {
if (activeSlide && activeSlide.questions[0]) {
await axios
.post(
`/api/competitions/${competitionId}/slides/${activeSlide?.id}/questions/${activeSlide?.questions[0].id}/alternatives`,
{ text: '', value: 0 }
)
.then(() => {
dispatch(getEditorCompetition(competitionId))
})
.catch(console.log)
}
}
const handleCloseAnswerClick = async (alternative_id: number) => {
if (activeSlide && activeSlide.questions[0]) {
await axios
.delete(
`/api/competitions/${competitionId}/slides/${activeSlideId}/questions/${activeSlide?.questions[0].id}/alternatives/${alternative_id}`
)
.then(() => {
dispatch(getEditorCompetition(competitionId))
})
.catch(console.log)
}
}
const renderRadioButton = (alt: QuestionAlternative) => {
if (alt.value) return <RadioButtonCheckedIcon onClick={() => updateAlternativeValue(alt)} />
else return <RadioButtonUncheckedIcon onClick={() => updateAlternativeValue(alt)} />
}
return (
<SettingsList>
<ListItem divider>
<Center>
<ListItemText
primary="Svarsalternativ"
secondary="(Fyll i cirkeln 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"
/>
<Clickable>{renderRadioButton(alt)}</Clickable>
<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 SingleChoiceAlternatives
......@@ -15,7 +15,7 @@ import {
import axios from 'axios'
import React, { useState } from 'react'
import { getEditorCompetition } from '../../../../actions/editor'
import { useAppDispatch } from '../../../../hooks'
import { useAppDispatch, useAppSelector } from '../../../../hooks'
import { RichSlide } from '../../../../interfaces/ApiRichModels'
import { Center, FirstItem } from '../styled'
......@@ -30,6 +30,11 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => {
// For "slide type" dialog
const [selectedSlideType, setSelectedSlideType] = useState(0)
const [slideTypeDialog, setSlideTypeDialog] = useState(false)
const components = useAppSelector(
(state) => state.editor.competition.slides.find((slide) => slide.id === state.editor.activeSlideId)?.components
)
const questionComponentId = components?.find((qCompId) => qCompId.type_id === 3)?.id
const openSlideTypeDialog = (type_id: number) => {
setSelectedSlideType(type_id)
setSlideTypeDialog(true)
......@@ -41,6 +46,7 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => {
const updateSlideType = async () => {
closeSlideTypeDialog()
if (activeSlide) {
deleteQuestionComponent(questionComponentId)
if (activeSlide.questions[0] && activeSlide.questions[0].type_id !== selectedSlideType) {
if (selectedSlideType === 0) {
// Change slide type from a question type to information
......@@ -67,6 +73,7 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => {
})
.then(() => {
dispatch(getEditorCompetition(competitionId))
createQuestionComponent()
})
.catch(console.log)
}
......@@ -80,11 +87,38 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => {
})
.then(() => {
dispatch(getEditorCompetition(competitionId))
createQuestionComponent()
})
.catch(console.log)
}
}
}
const createQuestionComponent = () => {
axios
.post(`/api/competitions/${competitionId}/slides/${activeSlide.id}/components`, {
x: 0,
y: 0,
w: 400,
h: 250,
type_id: 3,
view_type_id: 1,
question_id: activeSlide.questions[0].id,
})
.then(() => {
dispatch(getEditorCompetition(competitionId))
})
.catch(console.log)
}
const deleteQuestionComponent = (componentId: number | undefined) => {
if (componentId) {
axios
.delete(`/api/competitions/${competitionId}/slides/${activeSlide.id}/components/${componentId}`)
.catch(console.log)
}
}
return (
<FirstItem>
<ListItem>
......@@ -108,7 +142,12 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => {
</MenuItem>
<MenuItem value={3}>
<Typography variant="button" onClick={() => openSlideTypeDialog(3)}>
Flervalsfråga
Kryssfråga
</Typography>
</MenuItem>
<MenuItem value={4}>
<Typography variant="button" onClick={() => openSlideTypeDialog(4)}>
Alternativfråga
</Typography>
</MenuItem>
</Select>
......
import {
FormControl,
List,
Tab,
TextField,
Typography,
Button,
Card,
ListItem,
Select,
InputLabel,
ListItemText,
} from '@material-ui/core'
import { Button, Card, List, ListItemText, Tab, TextField, Typography } from '@material-ui/core'
import styled from 'styled-components'
export const SettingsTab = styled(Tab)`
......@@ -148,3 +136,7 @@ export const HoverContainer = styled.div<HoverContainerProps>`
export const ImageNameText = styled(ListItemText)`
word-break: break-all;
`
export const QuestionComponent = styled.div`
outline-style: double;
`
......@@ -27,6 +27,7 @@ import axios from 'axios'
import React, { useEffect } from 'react'
import { useHistory } from 'react-router-dom'
import { useAppSelector } from '../../hooks'
import { RichTeam } from '../../interfaces/ApiRichModels'
import {
socketConnect,
socketEndPresentation,
......@@ -112,14 +113,13 @@ const OperatorViewPage: React.FC = () => {
useEffect(() => {
socketConnect()
socketSetSlide // Behövs denna?
socketSetSlide
handleOpenCodes()
setTimeout(startCompetition, 1000) // Ghetto, wait for everything to load
// console.log(id)
setTimeout(startCompetition, 1000) // Wait for socket to connect
}, [])
/** Handles the browsers back button and if pressed cancels the ongoing competition */
window.onpopstate = () => {
//Handle browser back arrow
alert('Tävlingen avslutas för alla')
endCompetition()
}
......@@ -135,11 +135,12 @@ const OperatorViewPage: React.FC = () => {
}
const startCompetition = () => {
socketStartPresentation()
socketStartPresentation() // Calls the socket to start competition
console.log('started competition for')
console.log(competitionId)
}
/** Making sure the user wants to exit the competition by displaying a dialog box */
const handleVerifyExit = () => {
setOpen(true)
}
......@@ -154,7 +155,7 @@ const OperatorViewPage: React.FC = () => {
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
window.location.reload(false) // TODO: fix this, we "need" to refresh site to be able to run the competition correctly again
}
const getCodes = async () => {
......@@ -204,12 +205,11 @@ const OperatorViewPage: React.FC = () => {
return typeName
}
const addScore = (id: number) => {
// Sums the scores for the teams. id must be id-1 because it starts at 1
/** Sums the scores for the teams. */
const addScore = (team: RichTeam) => {
let totalScore = 0
for (let j = 0; j < teams[id - 1].question_answers.length; j++) {
totalScore = totalScore + teams[id - 1].question_answers[j].score
for (let j = 0; j < team.question_answers.length; j++) {
totalScore = totalScore + team.question_answers[j].score
}
return totalScore
}
......@@ -305,31 +305,6 @@ const OperatorViewPage: React.FC = () => {
</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" />
......@@ -373,7 +348,7 @@ const OperatorViewPage: React.FC = () => {
{teams &&
teams.map((team) => (
<ListItem key={team.id}>
{team.name} score:{addScore(team.id)}
{team.name} score:{addScore(team)}
</ListItem>
))}
</List>
......
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 QuestionComponentDisplay from '../../presentationEditor/components/QuestionComponentDisplay'
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 PresentationComponent = ({ component, scale }: PresentationComponentProps) => {
const renderInnerComponent = () => {
switch (component.type_id) {
case ComponentTypes.Text:
......@@ -28,6 +24,8 @@ const PresentationComponent = ({ component, width, height, scale }: Presentation
component={component as ImageComponent}
/>
)
case ComponentTypes.Question:
return <QuestionComponentDisplay variant="presentation" />
default:
break
}
......
......@@ -2,11 +2,14 @@ import { AnyAction } from 'redux'
import Types from '../actions/types'
import { City } from '../interfaces/ApiModels'
// Define a type for the city state
interface CityState {
cities: City[]
total: number
count: number
}
// Define initial values for the city state
const initialState: CityState = {
cities: [],
total: 0,
......
import { AnyAction } from 'redux'
import Types from '../actions/types'
// Define a type for the competition login data
interface CompetitionLoginData {
competition_id: number
team_id: number | null
view: string
}
// Define a type for UI error
interface UIError {
message: string
}
// Define a type for the competition login state
interface CompetitionLoginState {
loading: boolean
errors: null | UIError
......@@ -19,6 +21,7 @@ interface CompetitionLoginState {
initialized: boolean
}
// Define the initial values for the competition login state
const initialState: CompetitionLoginState = {
loading: false,
errors: null,
......
......@@ -10,6 +10,7 @@ interface CompetitionState {
filterParams: CompetitionFilterParams
}
// Define the initial values for the competition state
const initialState: CompetitionState = {
competitions: [],
total: 0,
......
......@@ -7,6 +7,8 @@ interface MediaState {
mediatype_id: number
user_id: number
}
// Define the initial values for the media state
const initialState: MediaState = {
id: 0,
filename: '',
......
import { AnyAction } from 'redux'
import Types from '../actions/types'
import { Slide, Team } from '../interfaces/ApiModels'
import { Slide } from '../interfaces/ApiModels'
import { Timer } from '../interfaces/Timer'
import { RichCompetition } from './../interfaces/ApiRichModels'
// Define a type for the presentation state
interface PresentationState {
competition: RichCompetition
slide: Slide
......@@ -11,6 +12,7 @@ interface PresentationState {
timer: Timer
}
// Define the initial values for the presentation state
const initialState: PresentationState = {
competition: {
name: '',
......
......@@ -2,9 +2,12 @@ import { AnyAction } from 'redux'
import Types from '../actions/types'
import { Role } from '../interfaces/ApiModels'
// Define a type for the role state
interface RoleState {
roles: Role[]
}
// Define the initial values for the role state
const initialState: RoleState = {
roles: [],
}
......
......@@ -3,6 +3,7 @@ import Types from '../actions/types'
import { User } from '../interfaces/ApiModels'
import { UserFilterParams } from '../interfaces/FilterParams'
// Define a type for the search user state
interface SearchUserState {
users: User[]
total: number
......@@ -10,6 +11,7 @@ interface SearchUserState {
filterParams: UserFilterParams
}
// Define the initial values for the search user state
const initialState: SearchUserState = {
users: [],
total: 0,
......
import { AnyAction } from 'redux'
import Types from '../actions/types'
// Define a type for the statistics state
interface StatisticsState {
users: number
competitions: number
regions: number
}
// Define the initial values for the statistics state
const initialState: StatisticsState = {
users: 0,
competitions: 0,
......
......@@ -2,12 +2,14 @@ import { AnyAction } from 'redux'
import Types from '../actions/types'
import { ComponentType, MediaType, QuestionType, ViewType } from '../interfaces/ApiModels'
// Define a type for the Types state
interface TypesState {
componentTypes: ComponentType[]
viewTypes: ViewType[]
questionTypes: QuestionType[]
mediaTypes: MediaType[]
}
// Define the initial values for the types state
const initialState: TypesState = {
componentTypes: [],
viewTypes: [],
......
import { AnyAction } from 'redux'
import Types from '../actions/types'
// Define a type for the UI error
interface UIError {
message: string
}
// Define a type for the UI state
interface UIState {
loading: boolean
errors: null | UIError
}
// Define the initial values for the UI state
const initialState: UIState = {
loading: false,
errors: null,
......
import { AnyAction } from 'redux'
import Types from '../actions/types'
// Define a type for users info
interface UserInfo {
name: string
email: string
......@@ -9,12 +10,14 @@ interface UserInfo {
id: number
}
// Define a type for the users state
interface UserState {
authenticated: boolean
userInfo: UserInfo | null
loading: boolean
}
// Define the initial values for the users state
const initialState: UserState = {
authenticated: false,
loading: false,
......