diff --git a/client/.eslintrc b/client/.eslintrc index 3e2dbeb0fc5a04a61ba058eb0b1015fcc43e9a50..d5bbb2edee2b3642f71bc90f8261324605b0a22e 100644 --- a/client/.eslintrc +++ b/client/.eslintrc @@ -1,29 +1,29 @@ { - "parser": "@typescript-eslint/parser", - "parserOptions": { - "sourceType": "module", - "project": [ - "tsconfig.json" - ] - }, - "ecmaFeatures": { - "jsx": true - }, - "settings": { - "react": { - "version": "detect" - } - }, - "extends": [ - "plugin:react/recommended", - "plugin:@typescript-eslint/recommended", - "prettier/@typescript-eslint", - "plugin:prettier/recommended" - ], - "rules": { - "prettier/prettier": ["warn", { - "endOfLine":"auto" - }] + "parser": "@typescript-eslint/parser", + "parserOptions": { + "sourceType": "module", + "project": ["tsconfig.json"] + }, + "ecmaFeatures": { + "jsx": true + }, + "settings": { + "react": { + "version": "detect" } + }, + "extends": [ + "plugin:react/recommended", + "plugin:@typescript-eslint/recommended", + "prettier/@typescript-eslint", + "plugin:prettier/recommended" + ], + "rules": { + "prettier/prettier": [ + "warn", + { + "endOfLine": "auto" + } + ] } - +} diff --git a/client/src/actions/competitions.ts b/client/src/actions/competitions.ts index a758fd635550306eecb09e8100d5e17cb09a8ce1..7d248e46c05df64e948b483c5092616c4a828f35 100644 --- a/client/src/actions/competitions.ts +++ b/client/src/actions/competitions.ts @@ -1,5 +1,5 @@ import axios from 'axios' -import { CompetitionFilterParams } from '../interfaces/CompetitionFilterParams' +import { CompetitionFilterParams } from '../interfaces/FilterParams' import { AppDispatch, RootState } from './../store' import Types from './types' diff --git a/client/src/actions/presentation.ts b/client/src/actions/presentation.ts index 8c9db0e280a7e813d92fd6390d5c199d384f36ca..43232014b4fe8e77ad6b63fbc93660d8d9254e23 100644 --- a/client/src/actions/presentation.ts +++ b/client/src/actions/presentation.ts @@ -1,4 +1,5 @@ import axios from 'axios' +import { Slide } from '../interfaces/Slide' import { AppDispatch } from './../store' import Types from './types' @@ -31,9 +32,15 @@ export const getPresentationTeams = (id: string) => async (dispatch: AppDispatch console.log(err) }) } + +export const setCurrentSlide = (slide: Slide) => (dispatch: AppDispatch) => { + dispatch({ type: Types.SET_PRESENTATION_SLIDE, payload: slide }) +} + export const setCurrentSlidePrevious = () => (dispatch: AppDispatch) => { dispatch({ type: Types.SET_PRESENTATION_SLIDE_PREVIOUS }) } + export const setCurrentSlideNext = () => (dispatch: AppDispatch) => { dispatch({ type: Types.SET_PRESENTATION_SLIDE_NEXT }) } diff --git a/client/src/actions/searchUser.ts b/client/src/actions/searchUser.ts index fb4bf122e40d13952537c3e942bf351f10f99604..c6211056bda8250f1b5c849450d018ef4bd42819 100644 --- a/client/src/actions/searchUser.ts +++ b/client/src/actions/searchUser.ts @@ -1,5 +1,5 @@ import axios from 'axios' -import { UserFilterParams } from '../interfaces/UserData' +import { UserFilterParams } from '../interfaces/FilterParams' import { AppDispatch, RootState } from './../store' import Types from './types' diff --git a/client/src/actions/types.ts b/client/src/actions/types.ts index d18be7aba0704c35935c2735e4fe47cf1cb00700..c0b7b629afa54a43de4c8ef8ddfba2cc551aa5d4 100644 --- a/client/src/actions/types.ts +++ b/client/src/actions/types.ts @@ -16,6 +16,7 @@ export default { SET_COMPETITIONS_TOTAL: 'SET_COMPETITIONS_TOTAL', SET_COMPETITIONS_COUNT: 'SET_COMPETITIONS_COUNT', 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_TEAMS: 'SET_PRESENTATION_TEAMS', diff --git a/client/src/actions/user.ts b/client/src/actions/user.ts index b543a68ae0631036e3a2df1f1312698c733f5bfa..72d764c861db49024c5fbcf00e3291efc35b2609 100644 --- a/client/src/actions/user.ts +++ b/client/src/actions/user.ts @@ -1,10 +1,10 @@ import axios from 'axios' import { History } from 'history' import { AppDispatch } from '../store' -import { AdminLoginData } from './../interfaces/AdminLoginData' +import { AccountLoginModel } from './../interfaces/FormModels' import Types from './types' -export const loginUser = (userData: AdminLoginData, history: History) => async (dispatch: AppDispatch) => { +export const loginUser = (userData: AccountLoginModel, history: History) => async (dispatch: AppDispatch) => { dispatch({ type: Types.LOADING_UI }) await axios .post('/auth/login', userData) diff --git a/client/src/interfaces/AdminLoginData.ts b/client/src/interfaces/AdminLoginData.ts deleted file mode 100644 index 70f61036d73c599f650abcca254c3ba5c6594992..0000000000000000000000000000000000000000 --- a/client/src/interfaces/AdminLoginData.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface AdminLoginData { - email: string - password: string -} diff --git a/client/src/interfaces/ApiModels.ts b/client/src/interfaces/ApiModels.ts new file mode 100644 index 0000000000000000000000000000000000000000..194fb2e727449b39befe2e1d43ee9913cca1ee00 --- /dev/null +++ b/client/src/interfaces/ApiModels.ts @@ -0,0 +1,74 @@ +interface NameID { + id: number + name: string +} +export interface City extends NameID {} +export interface Role extends NameID {} +export interface MediaType extends NameID {} +export interface QuestionType extends NameID {} + +export interface Media { + id: number + filename: string + mediatype_id: number + user_id: number +} + +export interface User extends NameID { + email: string + role_id: number + city_id: number +} + +export interface Competition extends NameID { + city_id: number + year: number +} + +export interface Team extends NameID { + competition_id: number +} + +export interface Question extends NameID { + slide_id: number + title: string + total_score: number + type_id: number +} + +export interface QuestionAlternative { + id: number + text: string + value: boolean + question_id: number +} +export interface QuestionAnswer { + id: number + question_id: number + team_id: string + data: string + score: number +} + +export interface Component { + id: number + x: number + y: number + w: number + h: number + type: number +} + +export interface ImageComponent extends Component { + media_id: number +} + +export interface TextComponent extends Component { + text: string + font: string +} + +export interface QuestionAlternativeComponent extends Component { + question_alternative_id: number + font: string +} diff --git a/client/src/interfaces/ApiRichModels.ts b/client/src/interfaces/ApiRichModels.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f7e012435518939f79c9bc30d43e17458215026 --- /dev/null +++ b/client/src/interfaces/ApiRichModels.ts @@ -0,0 +1,37 @@ +import { City, Component, Media, QuestionAnswer, QuestionType } from './ApiModels' + +export interface RichCompetition { + name: string + id: number + year: number + city: City + slides: RichSlide[] + teams: RichTeam[] +} + +export interface RichSlide { + id: number + order: number + timer: number + title: string + competition_id: number + question: RichQuestion[] + components: Component[] + medias: Media[] +} + +export interface RichTeam { + id: number + name: string + question_answers: QuestionAnswer[] + competition_id: number +} + +export interface RichQuestion { + id: number + slide_id: number + name: string + title: string + total_score: number + question_type: QuestionType +} diff --git a/client/src/interfaces/City.ts b/client/src/interfaces/City.ts deleted file mode 100644 index e5190b6b56284e97015a66b1c2febd0548bd236a..0000000000000000000000000000000000000000 --- a/client/src/interfaces/City.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface City { - id: number - name: string -} diff --git a/client/src/interfaces/Competition.ts b/client/src/interfaces/Competition.ts deleted file mode 100644 index d36d97da1bc824f996fb7c46e88eaa71aa720995..0000000000000000000000000000000000000000 --- a/client/src/interfaces/Competition.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface Competition { - name: string - id: number - city_id: number - year: number -} diff --git a/client/src/interfaces/CompetitionFilterParams.ts b/client/src/interfaces/CompetitionFilterParams.ts deleted file mode 100644 index 839460527a548572cf4dc26015f477e33e871187..0000000000000000000000000000000000000000 --- a/client/src/interfaces/CompetitionFilterParams.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface CompetitionFilterParams { - name?: string - year?: number - cityId?: number - styleId?: number - page: number - pageSize: number -} diff --git a/client/src/interfaces/FilterParams.ts b/client/src/interfaces/FilterParams.ts new file mode 100644 index 0000000000000000000000000000000000000000..eca6551f4705aa3e4dfe6c96878fc9d3039fd983 --- /dev/null +++ b/client/src/interfaces/FilterParams.ts @@ -0,0 +1,26 @@ +export interface CompetitionFilterParams { + name?: string + year?: number + cityId?: number + styleId?: number + page: number + pageSize: number +} + +export interface SearchUserFilterParams { + name?: string + year?: number + cityId?: number + styleId?: number + page: number + pageSize: number +} + +export interface UserFilterParams { + name?: string + email?: string + cityId?: number + roleId?: number + page: number + pageSize: number +} diff --git a/client/src/interfaces/models.ts b/client/src/interfaces/FormModels.ts similarity index 64% rename from client/src/interfaces/models.ts rename to client/src/interfaces/FormModels.ts index 70d4cc08b5f8e449ad251526389dd84fe42a4d3e..294887131ab91c4715d3fe39f03121d1c835b11b 100644 --- a/client/src/interfaces/models.ts +++ b/client/src/interfaces/FormModels.ts @@ -1,18 +1,31 @@ +export interface ServerResponse { + code: number + message: string +} +export interface FormModel<T> { + model: T + error?: string +} + +//#region LOGIN export interface AccountLoginModel { email: string password: string } +export interface CompetitionLoginModel { + code: string +} + +//#endregion + +////ADD//// export interface AddCompetitionModel { name: string city: string year: number } -export interface CompetitionLoginModel { - code: string -} - export interface AddUserModel { email: string password: string @@ -21,6 +34,11 @@ export interface AddUserModel { name?: string } +export interface AddCityModel { + name: string +} + +////EDIT//// export interface EditUserModel { email: string role: string diff --git a/client/src/interfaces/Role.ts b/client/src/interfaces/Role.ts deleted file mode 100644 index 08a54dfcb012ee03398d17d4a7153ab040cde909..0000000000000000000000000000000000000000 --- a/client/src/interfaces/Role.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Role { - id: number - name: string -} diff --git a/client/src/interfaces/SearchUserFilterParams.ts b/client/src/interfaces/SearchUserFilterParams.ts deleted file mode 100644 index 8d7230d83d12d164a0db4683b4db7b791d835a81..0000000000000000000000000000000000000000 --- a/client/src/interfaces/SearchUserFilterParams.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface SearchUSerFilterParams { - name?: string - year?: number - cityId?: number - styleId?: number - page: number - pageSize: number -} diff --git a/client/src/interfaces/UserData.ts b/client/src/interfaces/UserData.ts deleted file mode 100644 index a0c7394699b314c49e8f19bb259b6d73dd610bcf..0000000000000000000000000000000000000000 --- a/client/src/interfaces/UserData.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface UserData { - id: number - name?: string - email: string - role_id: number - city_id: number -} - -export interface UserFilterParams { - name?: string - email?: string - cityId?: number - roleId?: number - page: number - pageSize: number -} diff --git a/client/src/pages/admin/AdminPage.tsx b/client/src/pages/admin/AdminPage.tsx index 7a011687e4ea1362edd50b2359a9c27ac8ec9baa..793da84f23281985c6a1e3e6818d101bb8b05321 100644 --- a/client/src/pages/admin/AdminPage.tsx +++ b/client/src/pages/admin/AdminPage.tsx @@ -16,8 +16,10 @@ import ExitToAppIcon from '@material-ui/icons/ExitToApp' import LocationCityIcon from '@material-ui/icons/LocationCity' import PeopleIcon from '@material-ui/icons/People' import SettingsOverscanIcon from '@material-ui/icons/SettingsOverscan' -import React from 'react' +import React, { useEffect } from 'react' import { Link, Route, Switch, useRouteMatch } from 'react-router-dom' +import { getCities } from '../../actions/cities' +import { getRoles } from '../../actions/roles' import { logoutUser } from '../../actions/user' import { useAppDispatch } from '../../hooks' import CompetitionManager from './components/CompetitionManager' @@ -62,6 +64,12 @@ const AdminView: React.FC = () => { dispatch(logoutUser()) } const dispatch = useAppDispatch() + + useEffect(() => { + dispatch(getCities()) + dispatch(getRoles()) + }, []) + return ( <div className={classes.root}> <CssBaseline /> diff --git a/client/src/pages/admin/components/AddCompetition.tsx b/client/src/pages/admin/components/AddCompetition.tsx index 7bc82f00bbe7a21712b5a4b3b3b0b22b17de5bda..65cb7c266d1e5d5c48fba2ead6b3fff5bfdc855b 100644 --- a/client/src/pages/admin/components/AddCompetition.tsx +++ b/client/src/pages/admin/components/AddCompetition.tsx @@ -6,21 +6,15 @@ import React from 'react' import * as Yup from 'yup' import { getCompetitions } from '../../../actions/competitions' import { useAppDispatch, useAppSelector } from '../../../hooks' -import { City } from '../../../interfaces/City' -import { AddCompetitionModel } from '../../../interfaces/models' +import { City } from '../../../interfaces/ApiModels' +import { AddCompetitionModel, FormModel } from '../../../interfaces/FormModels' import { AddButton, AddContent, AddForm } from './styled' -interface ServerResponse { - code: number - message: string -} -interface AddCompetitionFormModel { - model: AddCompetitionModel - error?: string -} +type formType = FormModel<AddCompetitionModel> + const noCitySelected = 'Välj stad' -const competitionSchema: Yup.SchemaOf<AddCompetitionFormModel> = Yup.object({ +const competitionSchema: Yup.SchemaOf<formType> = Yup.object({ model: Yup.object() .shape({ name: Yup.string().required('Namn krävs'), @@ -50,17 +44,14 @@ const AddCompetition: React.FC = (props: any) => { const dispatch = useAppDispatch() const id = open ? 'simple-popover' : undefined const currentYear = new Date().getFullYear() - const handleCompetitionSubmit = async ( - values: AddCompetitionFormModel, - actions: FormikHelpers<AddCompetitionFormModel> - ) => { + const handleCompetitionSubmit = async (values: formType, actions: FormikHelpers<formType>) => { const params = { name: values.model.name, year: values.model.year, city_id: selectedCity?.id as number, } await axios - .post<ServerResponse>('/competitions', params) + .post('/competitions', params) .then(() => { actions.resetForm() setAnchorEl(null) @@ -78,7 +69,7 @@ const AddCompetition: React.FC = (props: any) => { }) } - const competitionInitialValues: AddCompetitionFormModel = { + const competitionInitialValues: formType = { model: { name: '', city: userCity?.name ? userCity.name : noCitySelected, year: currentYear }, } return ( diff --git a/client/src/pages/admin/components/AddRegion.tsx b/client/src/pages/admin/components/AddRegion.tsx index 658fe63fdbc5e36d3eb2f63c50e428565a89295d..7f0ccf4e3cc1a43f23a7173b6de883c047899054 100644 --- a/client/src/pages/admin/components/AddRegion.tsx +++ b/client/src/pages/admin/components/AddRegion.tsx @@ -9,20 +9,9 @@ import React from 'react' import * as Yup from 'yup' import { getCities } from '../../../actions/cities' import { useAppDispatch } from '../../../hooks' +import { AddCityModel, FormModel } from '../../../interfaces/FormModels' import { AddForm } from './styled' -interface AddRegionModel { - city: '' -} -interface ServerResponse { - code: number - message: string -} -interface AddRegionFormModel { - model: AddRegionModel - error?: string -} - const useStyles = makeStyles((theme: Theme) => createStyles({ table: { @@ -39,7 +28,9 @@ const useStyles = makeStyles((theme: Theme) => }) ) -const schema: Yup.SchemaOf<AddRegionFormModel> = Yup.object({ +type formType = FormModel<AddCityModel> + +const schema: Yup.SchemaOf<formType> = Yup.object({ model: Yup.object() .shape({ city: Yup.string() @@ -55,12 +46,12 @@ const AddRegion: React.FC = (props: any) => { const classes = useStyles() const dispatch = useAppDispatch() - const handleSubmit = async (values: AddRegionFormModel, actions: FormikHelpers<AddRegionFormModel>) => { + const handleSubmit = async (values: formType, actions: FormikHelpers<formType>) => { const params = { - name: values.model.city, + name: values.model.name, } await axios - .post<ServerResponse>('/misc/cities', params) + .post('/misc/cities', params) .then(() => { actions.resetForm() dispatch(getCities()) @@ -76,8 +67,8 @@ const AddRegion: React.FC = (props: any) => { }) } - const initValues: AddRegionFormModel = { - model: { city: '' }, + const initValues: formType = { + model: { name: '' }, } return ( @@ -88,8 +79,8 @@ const AddRegion: React.FC = (props: any) => { <Grid container={true}> <TextField className={classes.margin} - helperText={formik.touched.model?.city ? formik.errors.model?.city : ''} - error={Boolean(formik.touched.model?.city && formik.errors.model?.city)} + helperText={formik.touched.model?.name ? formik.errors.model?.name : ''} + error={Boolean(formik.touched.model?.name && formik.errors.model?.name)} onChange={formik.handleChange} onBlur={formik.handleBlur} name="model.city" diff --git a/client/src/pages/admin/components/AddUser.tsx b/client/src/pages/admin/components/AddUser.tsx index f55caa3a83b552536c78d3741fbe5e85bb603707..8880f4003e7162e0e6c93657265506ab9d39cce9 100644 --- a/client/src/pages/admin/components/AddUser.tsx +++ b/client/src/pages/admin/components/AddUser.tsx @@ -7,24 +7,16 @@ import React from 'react' import * as Yup from 'yup' import { getSearchUsers } from '../../../actions/searchUser' import { useAppDispatch, useAppSelector } from '../../../hooks' -import { City } from '../../../interfaces/City' -import { AddUserModel } from '../../../interfaces/models' -import { Role } from '../../../interfaces/Role' +import { City, Role } from '../../../interfaces/ApiModels' +import { AddUserModel, FormModel } from '../../../interfaces/FormModels' import { AddButton, AddContent, AddForm } from './styled' -interface ServerResponse { - code: number - message: string -} -interface AddUserFormModel { - model: AddUserModel - error?: string -} +type formType = FormModel<AddUserModel> const noRoleSelected = 'Välj roll' const noCitySelected = 'Välj stad' -const userSchema: Yup.SchemaOf<AddUserFormModel> = Yup.object({ +const userSchema: Yup.SchemaOf<formType> = Yup.object({ model: Yup.object() .shape({ name: Yup.string(), //.required('Namn krävs'), @@ -58,7 +50,7 @@ const AddUser: React.FC = (props: any) => { const open = Boolean(anchorEl) const dispatch = useAppDispatch() const id = open ? 'simple-popover' : undefined - const handleCompetitionSubmit = async (values: AddUserFormModel, actions: FormikHelpers<AddUserFormModel>) => { + const handleCompetitionSubmit = async (values: formType, actions: FormikHelpers<formType>) => { const params = { email: values.model.email, password: values.model.password, @@ -67,7 +59,7 @@ const AddUser: React.FC = (props: any) => { role_id: selectedRole?.id as number, } await axios - .post<ServerResponse>('/auth/signup', params) + .post('/auth/signup', params) .then(() => { actions.resetForm() setAnchorEl(null) @@ -86,7 +78,7 @@ const AddUser: React.FC = (props: any) => { }) } - const userInitialValues: AddUserFormModel = { + const userInitialValues: formType = { model: { email: '', password: '', name: '', city: noCitySelected, role: noRoleSelected }, } return ( diff --git a/client/src/pages/admin/components/CompetitionManager.tsx b/client/src/pages/admin/components/CompetitionManager.tsx index a957608b7bcd9faae999688839056488d1abc8d9..c271641fac4ccd99e5e83e4d7edf195ddbeb993f 100644 --- a/client/src/pages/admin/components/CompetitionManager.tsx +++ b/client/src/pages/admin/components/CompetitionManager.tsx @@ -15,10 +15,9 @@ import MoreHorizIcon from '@material-ui/icons/MoreHoriz' import axios from 'axios' import React, { useEffect } from 'react' import { Link, useHistory } from 'react-router-dom' -import { getCities } from '../../../actions/cities' import { getCompetitions, setFilterParams } from '../../../actions/competitions' import { useAppDispatch, useAppSelector } from '../../../hooks' -import { CompetitionFilterParams } from '../../../interfaces/CompetitionFilterParams' +import { CompetitionFilterParams } from '../../../interfaces/FilterParams' import AddCompetition from './AddCompetition' import { FilterContainer, RemoveMenuItem, TopBar, YearFilterTextField } from './styled' @@ -56,7 +55,6 @@ const CompetitionManager: React.FC = (props: any) => { } useEffect(() => { - dispatch(getCities()) dispatch(getCompetitions()) }, []) diff --git a/client/src/pages/admin/components/UserManager.tsx b/client/src/pages/admin/components/UserManager.tsx index d85290b2305808cfd35063d3ed7d403b19b57b18..e2f2412f96ad5963314a07aac3d68f1760d52f79 100644 --- a/client/src/pages/admin/components/UserManager.tsx +++ b/client/src/pages/admin/components/UserManager.tsx @@ -14,11 +14,9 @@ import TableRow from '@material-ui/core/TableRow' import MoreHorizIcon from '@material-ui/icons/MoreHoriz' import axios from 'axios' import React, { useEffect } from 'react' -import { getCities } from '../../../actions/cities' -import { getRoles } from '../../../actions/roles' import { getSearchUsers, setFilterParams } from '../../../actions/searchUser' import { useAppDispatch, useAppSelector } from '../../../hooks' -import { UserFilterParams } from '../../../interfaces/UserData' +import { UserFilterParams } from '../../../interfaces/FilterParams' import AddUser from './AddUser' import { FilterContainer, RemoveMenuItem, TopBar } from './styled' @@ -56,8 +54,6 @@ const UserManager: React.FC = (props: any) => { } useEffect(() => { - dispatch(getCities()) - dispatch(getRoles()) dispatch(getSearchUsers()) }, []) diff --git a/client/src/pages/login/components/AdminLogin.tsx b/client/src/pages/login/components/AdminLogin.tsx index cdff4d3fd14a422f22461e1cccd3b1acf26f1e84..39892ba1f4c975e1c53e5583aa50927d029c0644 100644 --- a/client/src/pages/login/components/AdminLogin.tsx +++ b/client/src/pages/login/components/AdminLogin.tsx @@ -6,7 +6,7 @@ import { useHistory } from 'react-router-dom' import * as Yup from 'yup' import { loginUser } from '../../../actions/user' import { useAppDispatch, useAppSelector } from '../../../hooks' -import { AccountLoginModel } from '../../../interfaces/models' +import { AccountLoginModel } from '../../../interfaces/FormModels' import { CenteredCircularProgress, LoginForm } from './styled' interface AccountLoginFormModel { diff --git a/client/src/pages/login/components/CompetitionLogin.tsx b/client/src/pages/login/components/CompetitionLogin.tsx index 9ebf658f80d4281739f45105cb27349a04b25fc3..75f73c5be187569fcc5adec2baf591a7cbdd62d3 100644 --- a/client/src/pages/login/components/CompetitionLogin.tsx +++ b/client/src/pages/login/components/CompetitionLogin.tsx @@ -4,7 +4,7 @@ import axios from 'axios' import { Formik, FormikHelpers } from 'formik' import React from 'react' import * as Yup from 'yup' -import { CompetitionLoginModel } from '../../../interfaces/models' +import { CompetitionLoginModel } from '../../../interfaces/FormModels' import { LoginForm } from './styled' interface CompetitionLoginFormModel { diff --git a/client/src/pages/views/JudgeViewPage.tsx b/client/src/pages/views/JudgeViewPage.tsx index 60b3f1c32e720207491278d9ae7d02d91be0bdbf..293a1f08ca5a7bff56b980844233bcb879edd215 100644 --- a/client/src/pages/views/JudgeViewPage.tsx +++ b/client/src/pages/views/JudgeViewPage.tsx @@ -1,21 +1,25 @@ -import { List, ListItem, ListItemText, Typography } from '@material-ui/core' +import { Divider, List, ListItemText } 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 } from '../../actions/presentation' +import { getPresentationCompetition, getPresentationTeams, setCurrentSlide } from '../../actions/presentation' import { useAppDispatch, useAppSelector } from '../../hooks' import { ViewParams } from '../../interfaces/ViewParams' import { SlideListItem } from '../presentationEditor/styled' -import { Content, LeftDrawer, RightDrawer } from './styled' +import JudgeScoreDisplay from './components/JudgeScoreDisplay' +import SlideDisplay from './components/SlideDisplay' +import { + Content, + JudgeAnswersLabel, + JudgeAppBar, + JudgeQuestionsLabel, + JudgeToolbar, + LeftDrawer, + RightDrawer, +} from './styled' const leftDrawerWidth = 150 const rightDrawerWidth = 390 -const answers = [ - { name: 'Team1', answer: 'Answer1' }, - { name: 'Team2', answer: 'Answer2' }, - { name: 'Team3', answer: 'Answer3' }, - { name: 'Team4', answer: 'Answer4' }, -] const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -25,6 +29,7 @@ const useStyles = makeStyles((theme: Theme) => rightDrawerPaper: { width: rightDrawerWidth, }, + toolbar: theme.mixins.toolbar, }) ) @@ -35,11 +40,22 @@ const JudgeViewPage: React.FC = () => { const [activeSlideIndex, setActiveSlideIndex] = useState<number>(0) useEffect(() => { dispatch(getPresentationCompetition(id)) + dispatch(getPresentationTeams(id)) }, []) const teams = useAppSelector((state) => state.presentation.teams) const slides = useAppSelector((state) => state.presentation.competition.slides) + const handleSelectSlide = (index: number) => { + setActiveSlideIndex(index) + dispatch(setCurrentSlide(slides[index])) + } return ( <div> + <JudgeAppBar position="fixed"> + <JudgeToolbar> + <JudgeQuestionsLabel variant="h5">Frågor</JudgeQuestionsLabel> + <JudgeAnswersLabel variant="h5">Svar</JudgeAnswersLabel> + </JudgeToolbar> + </JudgeAppBar> <LeftDrawer width={leftDrawerWidth} variant="permanent" @@ -48,11 +64,12 @@ const JudgeViewPage: React.FC = () => { }} anchor="left" > + <div className={classes.toolbar} /> <List> {slides.map((slide, index) => ( <SlideListItem selected={index === activeSlideIndex} - onClick={() => setActiveSlideIndex(index)} + onClick={() => handleSelectSlide(index)} divider button key={slide.id} @@ -70,16 +87,21 @@ const JudgeViewPage: React.FC = () => { }} anchor="right" > - <Typography variant="h4"> Svar </Typography> + <div className={classes.toolbar} /> <List> - {answers.map((answer, index) => ( - <ListItem key={answer.name}> - {answer.name}:{answer.answer} - </ListItem> + {teams.map((answer, index) => ( + <div key={answer.name}> + <JudgeScoreDisplay teamIndex={index} /> + <Divider /> + </div> ))} </List> </RightDrawer> - <Content width={leftDrawerWidth}>content</Content> + aaa + <Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}> + <div className={classes.toolbar} /> + <SlideDisplay /> + </Content> </div> ) } diff --git a/client/src/pages/views/ParticipantViewPage.tsx b/client/src/pages/views/ParticipantViewPage.tsx index 1e004791c1d1e43c03686449f9a43bf8d20013ff..55c28af06cff72a24862acaa03e8066d8a8f4a0c 100644 --- a/client/src/pages/views/ParticipantViewPage.tsx +++ b/client/src/pages/views/ParticipantViewPage.tsx @@ -1,5 +1,5 @@ import React from 'react' -import SlideDisplay from './components/Slide' +import SlideDisplay from './components/SlideDisplay' const ParticipantViewPage: React.FC = () => { return <SlideDisplay /> diff --git a/client/src/pages/views/PresenterViewPage.tsx b/client/src/pages/views/PresenterViewPage.tsx index 04f0214a940d5ba6f8b63bbafdb51c06593d3ce9..131bde22f5b23e70e9ac071563395f353b7b9e1f 100644 --- a/client/src/pages/views/PresenterViewPage.tsx +++ b/client/src/pages/views/PresenterViewPage.tsx @@ -10,7 +10,7 @@ import { } from '../../actions/presentation' import { useAppDispatch, useAppSelector } from '../../hooks' import { ViewParams } from '../../interfaces/ViewParams' -import SlideDisplay from './components/Slide' +import SlideDisplay from './components/SlideDisplay' import { PresenterButton, PresenterContainer, PresenterFooter, PresenterHeader } from './styled' const PresenterViewPage: React.FC = () => { diff --git a/client/src/pages/views/components/JudgeScoreDisplay.tsx b/client/src/pages/views/components/JudgeScoreDisplay.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ae4d8ab3e7e95a44357565093711d9f4722be0a3 --- /dev/null +++ b/client/src/pages/views/components/JudgeScoreDisplay.tsx @@ -0,0 +1,39 @@ +import { Box, Typography } from '@material-ui/core' +import React from 'react' +import { 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]) + return ( + <ScoreDisplayContainer> + <ScoreDisplayHeader> + <Typography variant="h5"> + <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> + </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> + </ScoreDisplayContainer> + ) +} + +export default JudgeScoreDisplay diff --git a/client/src/pages/views/components/Slide.test.tsx b/client/src/pages/views/components/SlideDisplay.test.tsx similarity index 77% rename from client/src/pages/views/components/Slide.test.tsx rename to client/src/pages/views/components/SlideDisplay.test.tsx index bc278433a79b439c593efa3c390e8fda6b638825..b3cfb1dc1daf6619211c3433a876cc126409cf0f 100644 --- a/client/src/pages/views/components/Slide.test.tsx +++ b/client/src/pages/views/components/SlideDisplay.test.tsx @@ -1,6 +1,6 @@ import { render } from '@testing-library/react' import React from 'react' -import SlideDisplay from './Slide' +import SlideDisplay from './SlideDisplay' it('renders audience view page', () => { render(<SlideDisplay />) diff --git a/client/src/pages/views/components/Slide.tsx b/client/src/pages/views/components/SlideDisplay.tsx similarity index 100% rename from client/src/pages/views/components/Slide.tsx rename to client/src/pages/views/components/SlideDisplay.tsx diff --git a/client/src/pages/views/components/styled.tsx b/client/src/pages/views/components/styled.tsx index 0e0b8e17b0f6d643f95a431fd9e34a39fb4617fa..0034c39beceb8bcb4371589758959c7cae1dd1ab 100644 --- a/client/src/pages/views/components/styled.tsx +++ b/client/src/pages/views/components/styled.tsx @@ -1,6 +1,27 @@ +import { TextField } from '@material-ui/core' import styled from 'styled-components' export const SlideContainer = styled.div` display: flex; justify-content: center; ` + +export const ScoreDisplayContainer = styled.div` + padding-top: 5px; + padding-right: 10px; + padding-left: 10px; +` + +export const ScoreDisplayHeader = styled.div` + display: flex; + justify-content: space-between; +` + +export const ScoreInput = styled(TextField)` + width: 40px; +` + +export const AnswerContainer = styled.div` + display: flex; + flex-wrap: wrap; +` diff --git a/client/src/pages/views/styled.tsx b/client/src/pages/views/styled.tsx index 6a7554e8217d242018a010935ed1e242d0b3586f..261727b91641605366f7fe3ced3b22f1706fffb5 100644 --- a/client/src/pages/views/styled.tsx +++ b/client/src/pages/views/styled.tsx @@ -1,6 +1,23 @@ -import { Button, Drawer } from '@material-ui/core' +import { AppBar, Button, Drawer, Toolbar, Typography } from '@material-ui/core' import styled from 'styled-components' +export const JudgeAppBar = styled(AppBar)` + z-index: 9000; +` + +export const JudgeToolbar = styled(Toolbar)` + display: flex; + justify-content: space-between; +` + +export const JudgeQuestionsLabel = styled(Typography)` + margin-left: 15px; +` + +export const JudgeAnswersLabel = styled(Typography)` + margin-right: 160px; +` + export const ViewSelectContainer = styled.div` display: flex; justify-content: center; @@ -49,19 +66,25 @@ interface DrawerProps { } export const LeftDrawer = styled(Drawer)<DrawerProps>` - width: ${(props) => (props ? props.width : 150)}; flex-shrink: 0; position: 'relative'; + z-index: -5; + width: ${(props) => (props ? props.width : 150)}; ` export const RightDrawer = styled(Drawer)<DrawerProps>` width: ${(props) => (props ? props.width : 150)}; flex-shrink: 0; + z-index: 1; ` + interface ContentProps { - drawerWidth: number + leftDrawerWidth: number + rightDrawerWidth: number } -export const Content = styled(Drawer)<DrawerProps>` - padding-left: ${(props) => (props ? props.width : 0)}; +export const Content = styled.div<ContentProps>` + 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); ` diff --git a/client/src/reducers/citiesReducer.ts b/client/src/reducers/citiesReducer.ts index 1c9542ff0c6ec2059a56f1ff5b5ac58c046114c6..7f5555b3eb002559df3952dfbac31d178ffd6271 100644 --- a/client/src/reducers/citiesReducer.ts +++ b/client/src/reducers/citiesReducer.ts @@ -1,6 +1,6 @@ import { AnyAction } from 'redux' import Types from '../actions/types' -import { City } from '../interfaces/City' +import { City } from '../interfaces/ApiModels' interface CityState { cities: City[] diff --git a/client/src/reducers/competitionsReducer.ts b/client/src/reducers/competitionsReducer.ts index b3003d4a366a95665779b4fc1fafbd7af8cd352e..bb788da5439874f8f9963dfbb756a222012697e6 100644 --- a/client/src/reducers/competitionsReducer.ts +++ b/client/src/reducers/competitionsReducer.ts @@ -1,7 +1,7 @@ import { AnyAction } from 'redux' import Types from '../actions/types' -import { Competition } from '../interfaces/Competition' -import { CompetitionFilterParams } from './../interfaces/CompetitionFilterParams' +import { Competition } from '../interfaces/ApiModels' +import { CompetitionFilterParams } from './../interfaces/FilterParams' interface CompetitionState { competitions: Competition[] diff --git a/client/src/reducers/presentationReducer.ts b/client/src/reducers/presentationReducer.ts index abff35213609bf96e544a651e1a97027327db95a..d71bcafbca4720c9d82ed41a18a6e55db6d91927 100644 --- a/client/src/reducers/presentationReducer.ts +++ b/client/src/reducers/presentationReducer.ts @@ -1,11 +1,11 @@ import { AnyAction } from 'redux' import Types from '../actions/types' -import { Competition } from '../interfaces/Competition' +import { RichCompetition } from './../interfaces/ApiRichModels' import { Slide } from './../interfaces/Slide' import { Team } from './../interfaces/Team' interface PresentationState { - competition: Competition + competition: RichCompetition slide: Slide teams: Team[] } @@ -20,6 +20,7 @@ const initialState: PresentationState = { }, slides: [], year: 0, + teams: [], }, slide: { competition_id: 0, @@ -37,13 +38,18 @@ export default function (state = initialState, action: AnyAction) { return { ...state, slide: action.payload.slides[0] as Slide, - competition: action.payload as Competition, + competition: action.payload as RichCompetition, } case Types.SET_PRESENTATION_TEAMS: return { ...state, teams: action.payload as Team[], } + case Types.SET_PRESENTATION_SLIDE: + return { + ...state, + slide: action.payload as Slide, + } case Types.SET_PRESENTATION_SLIDE_PREVIOUS: if (state.slide.order - 1 >= 0) { return { diff --git a/client/src/reducers/rolesReducer.ts b/client/src/reducers/rolesReducer.ts index 652d4afcf12c1f58aad0c1b9cf21ab36e2ca96da..5028ae04cb13a4b1bf44536cd42bf3f8935b268c 100644 --- a/client/src/reducers/rolesReducer.ts +++ b/client/src/reducers/rolesReducer.ts @@ -1,6 +1,6 @@ import { AnyAction } from 'redux' import Types from '../actions/types' -import { Role } from '../interfaces/Role' +import { Role } from '../interfaces/ApiModels' interface RoleState { roles: Role[] diff --git a/client/src/reducers/searchUserReducer.ts b/client/src/reducers/searchUserReducer.ts index 9b77d68af26b3fbf2f0b428c8a6a959f6e9c5991..e0c1250683ae273318a4bcd6e4a3f5ae5f6324bd 100644 --- a/client/src/reducers/searchUserReducer.ts +++ b/client/src/reducers/searchUserReducer.ts @@ -1,9 +1,10 @@ import { AnyAction } from 'redux' import Types from '../actions/types' -import { UserData, UserFilterParams } from '../interfaces/UserData' +import { User } from '../interfaces/ApiModels' +import { UserFilterParams } from '../interfaces/FilterParams' interface SearchUserState { - users: UserData[] + users: User[] total: number count: number filterParams: UserFilterParams @@ -21,7 +22,7 @@ export default function (state = initialState, action: AnyAction) { case Types.SET_SEARCH_USERS: return { ...state, - users: action.payload as UserData[], + users: action.payload as User[], } case Types.SET_SEARCH_USERS_FILTER_PARAMS: return { diff --git a/client/src/reducers/userReducer.ts b/client/src/reducers/userReducer.ts index 9a26b1c9f8d62a61733299ccfff48c24d67310d1..2b340ae61d155504c81c886126e64db589c68745 100644 --- a/client/src/reducers/userReducer.ts +++ b/client/src/reducers/userReducer.ts @@ -1,7 +1,6 @@ import { AnyAction } from 'redux' import Types from '../actions/types' -import { City } from '../interfaces/City' -import { Competition } from './../interfaces/Competition' +import { City, Competition } from '../interfaces/ApiModels' interface UserInfo { name: string