diff --git a/client/src/actions/competitionLogin.ts b/client/src/actions/competitionLogin.ts index 4b1eeb831f3dd03a3e5db83abf01b2d7a83a7023..ff177a2bc82f8d37174f4f2df4bd050ebb3822de 100644 --- a/client/src/actions/competitionLogin.ts +++ b/client/src/actions/competitionLogin.ts @@ -5,16 +5,19 @@ import { AccountLoginModel } from './../interfaces/FormModels' import Types from './types' export const loginCompetition = (code: string, history: History) => async (dispatch: AppDispatch) => { + dispatch({ type: Types.LOADING_COMPETITION_LOGIN }) await axios .post('/api/auth/login/code', { code }) .then((res) => { console.log(code, res.data[0]) + dispatch({ type: Types.CLEAR_COMPETITION_LOGIN_ERRORS }) // no error // history.push('/admin') //redirecting to admin page after login success if (res.data && res.data[0] && res.data[0].view_type_id) { history.push(`/${code}`) } }) .catch((err) => { + dispatch({ type: Types.SET_COMPETITION_LOGIN_ERRORS, payload: err && err.response && err.response.data }) console.log(err) }) } diff --git a/client/src/actions/types.ts b/client/src/actions/types.ts index 189bf16ca5f3baa63bdf8d0ad5d5ae0fa6805d10..6e9c098e3f10870a5fecc89e5e42fe83d38ef13b 100644 --- a/client/src/actions/types.ts +++ b/client/src/actions/types.ts @@ -1,6 +1,7 @@ export default { LOADING_UI: 'LOADING_UI', LOADING_USER: 'LOADING_USER', + LOADING_COMPETITION_LOGIN: 'LOADING_COMPETITION_LOGIN', SET_ROLES: 'SET_ROLES', SET_USER: 'SET_USER', SET_SEARCH_USERS: 'SET_SEARCH_USERS', @@ -9,6 +10,8 @@ export default { SET_SEARCH_USERS_TOTAL_COUNT: 'SET_SEARCH_USERS_TOTAL_COUNT', SET_ERRORS: 'SET_ERRORS', CLEAR_ERRORS: 'CLEAR_ERRORS', + SET_COMPETITION_LOGIN_ERRORS: 'SET_COMPETITION_LOGIN_ERRORS', + CLEAR_COMPETITION_LOGIN_ERRORS: 'CLEAR_COMPETITION_LOGIN_ERRORS', SET_UNAUTHENTICATED: 'SET_UNAUTHENTICATED', SET_AUTHENTICATED: 'SET_AUTHENTICATED', SET_COMPETITIONS: 'SET_COMPETITIONS', diff --git a/client/src/pages/login/components/AdminLogin.tsx b/client/src/pages/login/components/AdminLogin.tsx index 7f478caf14a7dae4cbc866e0f67dbf2efce49efd..964fb8abd148a9a0735206e9dbe3288cbe72ea3a 100644 --- a/client/src/pages/login/components/AdminLogin.tsx +++ b/client/src/pages/login/components/AdminLogin.tsx @@ -1,4 +1,4 @@ -import { Button, TextField } from '@material-ui/core' +import { Button, TextField, Typography } from '@material-ui/core' import { Alert, AlertTitle } from '@material-ui/lab' import { Formik, FormikHelpers } from 'formik' import React, { useEffect, useState } from 'react' @@ -83,7 +83,8 @@ const AdminLogin: React.FC = () => { {errors.message && ( <Alert severity="error"> <AlertTitle>Error</AlertTitle> - {errors.message} + <Typography>Någonting gick fel. Kontrollera</Typography> + <Typography>dina användaruppgifter och försök igen</Typography> </Alert> )} {loading && <CenteredCircularProgress color="secondary" />} diff --git a/client/src/pages/login/components/CompetitionLogin.tsx b/client/src/pages/login/components/CompetitionLogin.tsx index 738fcb34074a77582d1d89516db02ddada3c7088..d89cafcf8216197de081d65550116e2e06b56f22 100644 --- a/client/src/pages/login/components/CompetitionLogin.tsx +++ b/client/src/pages/login/components/CompetitionLogin.tsx @@ -1,29 +1,28 @@ -import { Button, TextField } from '@material-ui/core' +import { Button, TextField, Typography } from '@material-ui/core' import { Alert, AlertTitle } from '@material-ui/lab' import axios from 'axios' import { Formik, FormikHelpers } from 'formik' import { useHistory } from 'react-router-dom' -import React, { useEffect } from 'react' +import React, { useEffect, useState } from 'react' import * as Yup from 'yup' import { loginCompetition } from '../../../actions/competitionLogin' -import { useAppDispatch } from '../../../hooks' +import { useAppDispatch, useAppSelector } from '../../../hooks' import { CompetitionLoginModel } from '../../../interfaces/FormModels' -import { LoginForm } from './styled' +import { CenteredCircularProgress, LoginForm } from './styled' interface CompetitionLoginFormModel { model: CompetitionLoginModel error?: string } -interface ServerResponse { - code: number +interface formError { message: string } const competitionSchema: Yup.SchemaOf<CompetitionLoginFormModel> = Yup.object({ model: Yup.object() .shape({ - code: Yup.string().required('Mata in kod').min(6, 'Koden måste vara minst 6 tecken'), + code: Yup.string().required('Mata in kod').length(6, 'Koden måste vara 6 tecken'), }) .required(), error: Yup.string().optional(), @@ -32,27 +31,13 @@ const competitionSchema: Yup.SchemaOf<CompetitionLoginFormModel> = Yup.object({ const CompetitionLogin: React.FC = () => { const dispatch = useAppDispatch() const history = useHistory() - + const errors = useAppSelector((state) => state.competitionLogin.errors) + const loading = useAppSelector((state) => state.competitionLogin.loading) const competitionInitialValues: CompetitionLoginFormModel = { model: { code: '' }, } - const handleCompetitionSubmit = async ( - values: CompetitionLoginFormModel, - actions: FormikHelpers<CompetitionLoginFormModel> - ) => { + const handleCompetitionSubmit = async (values: CompetitionLoginFormModel) => { dispatch(loginCompetition(values.model.code, history)) - /* await axios - .post<ServerResponse>(`users/login`, { code: values.model.code }) - .then(() => { - actions.resetForm() - }) - .catch(({ response }) => { - console.log(response.data.message) - actions.setFieldError('error', response.data.message) - }) - .finally(() => { - actions.setSubmitting(false) - }) */ } return ( <Formik @@ -74,14 +59,14 @@ const CompetitionLogin: React.FC = () => { <Button type="submit" fullWidth variant="contained" color="secondary" disabled={!formik.isValid}> Anslut till tävling </Button> - {formik.errors.error ? ( + {errors && errors.message && ( <Alert severity="error"> <AlertTitle>Error</AlertTitle> - {formik.errors.error} + <Typography>En tävling med den koden hittades ej.</Typography> + <Typography>kontrollera koden och försök igen</Typography> </Alert> - ) : ( - <div /> )} + {loading && <CenteredCircularProgress color="secondary" />} </LoginForm> )} </Formik> diff --git a/client/src/pages/views/JudgeViewPage.tsx b/client/src/pages/views/JudgeViewPage.tsx index 60018624457a6242cbf1f27f4c8201fdb12ebb89..66450f3a1f8ac98f14423d7040e215c0c96881c6 100644 --- a/client/src/pages/views/JudgeViewPage.tsx +++ b/client/src/pages/views/JudgeViewPage.tsx @@ -14,6 +14,7 @@ import { socket_connect } from '../../sockets' import { SlideListItem } from '../presentationEditor/styled' import JudgeScoreDisplay from './components/JudgeScoreDisplay' import SlideDisplay from './components/SlideDisplay' +import { useHistory } from 'react-router-dom' import { Content, JudgeAnswersLabel, @@ -41,6 +42,7 @@ const useStyles = makeStyles((theme: Theme) => const JudgeViewPage: React.FC = () => { const classes = useStyles() + const history = useHistory() const { id, code }: ViewParams = useParams() const dispatch = useAppDispatch() const [activeSlideIndex, setActiveSlideIndex] = useState<number>(0) @@ -50,12 +52,13 @@ const JudgeViewPage: React.FC = () => { setActiveSlideIndex(index) dispatch(setCurrentSlide(slides[index])) } - useEffect(() => { socket_connect() dispatch(getPresentationCompetition(id)) dispatch(getPresentationTeams(id)) dispatch(setPresentationCode(code)) + //hides the url so people can't sneak peak + history.push('judge') }, []) return ( diff --git a/client/src/pages/views/ParticipantViewPage.tsx b/client/src/pages/views/ParticipantViewPage.tsx index 7447f2a6e4ca74d4c14a664591d3d4e57edb0faf..f531ad76db15bc5ba8578b4015e2d5feed8ea1e8 100644 --- a/client/src/pages/views/ParticipantViewPage.tsx +++ b/client/src/pages/views/ParticipantViewPage.tsx @@ -5,6 +5,7 @@ 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 /> diff --git a/client/src/pages/views/PresenterViewPage.tsx b/client/src/pages/views/PresenterViewPage.tsx index 23e5928a5c223f4bda26fd3c5a35e9489ac4b0b7..1abeee92c519c3aae977d667f68470ca78cafe2d 100644 --- a/client/src/pages/views/PresenterViewPage.tsx +++ b/client/src/pages/views/PresenterViewPage.tsx @@ -46,31 +46,27 @@ import { /** * Presentation is an active competition */ -type PresenterViewPageProps = { - competitionId: number -} -const PresenterViewPage = ({ competitionId }: PresenterViewPageProps) => { +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 { code }: ViewParams = useParams() + const { id, code }: ViewParams = useParams() const presentation = useAppSelector((state) => state.presentation) const history = useHistory() const dispatch = useAppDispatch() useEffect(() => { - dispatch(getPresentationCompetition(competitionId.toString())) - dispatch(getPresentationTeams(competitionId.toString())) + 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(competitionId) + // console.log(id) }, []) const handleOpenPopover = (event: React.MouseEvent<HTMLButtonElement>) => { @@ -85,7 +81,7 @@ const PresenterViewPage = ({ competitionId }: PresenterViewPageProps) => { const startCompetition = () => { socketStartPresentation() console.log('started competition for') - console.log(competitionId) + console.log(id) } const handleVerifyExit = () => { diff --git a/client/src/pages/views/ViewSelectPage.tsx b/client/src/pages/views/ViewSelectPage.tsx index fa3a58b8fddeeb1440f3679bbdb310afa12a283b..3c3599edeaf3d9d46ff6c462506d196d79d1a9f7 100644 --- a/client/src/pages/views/ViewSelectPage.tsx +++ b/client/src/pages/views/ViewSelectPage.tsx @@ -15,27 +15,23 @@ interface ViewSelectParams { code: string } const ViewSelectPage: React.FC = () => { - const url = useRouteMatch().url const [loading, setLoading] = useState(true) const [error, setError] = useState(false) - const [viewType, setViewType] = useState(undefined) + const [viewTypeId, setViewTypeId] = useState(undefined) const [competitionId, setCompetitionId] = useState<number | undefined>(undefined) const { code }: ViewSelectParams = useParams() + const viewType = useAppSelector((state) => state.types.viewTypes.find((viewType) => viewType.id === viewTypeId)?.name) const renderView = (viewTypeId: number | undefined) => { //Renders the correct view depending on view type - // TODO: use state instead of hard-coded values - //const viewTypes = useAppSelector(state => state.types.viewTypes) if (competitionId) { - switch (viewTypeId) { - case 1: + switch (viewType) { + case 'Team': return <ParticipantViewPage /> - case 2: + case 'Judge': return <JudgeViewPage /> - case 3: + case 'Audience': return <AudienceViewPage /> - case 4: - return <PresenterViewPage competitionId={competitionId} /> default: return <Typography>Inkorrekt vy</Typography> } @@ -47,8 +43,8 @@ const ViewSelectPage: React.FC = () => { .post('/api/auth/login/code', { code }) .then((response) => { setLoading(false) - setViewType(response.data[0].view_type_id) - setCompetitionId(response.data[0].pointer) + setViewTypeId(response.data[0].view_type_id) + setCompetitionId(response.data[0].competition_id) }) .catch(() => { setLoading(false) @@ -60,7 +56,7 @@ const ViewSelectPage: React.FC = () => { <ViewSelectContainer> <ViewSelectButtonGroup> {loading && <CircularProgress />} - {!loading && renderView(viewType)} + {!loading && renderView(viewTypeId)} {error && <Typography>Något gick fel, dubbelkolla koden och försök igen</Typography>} </ViewSelectButtonGroup> </ViewSelectContainer> diff --git a/client/src/reducers/allReducers.ts b/client/src/reducers/allReducers.ts index c23384c2a2a21dfba86c60550154985a5c879782..038b172e3e308af71d5762ade8a644b67d7992d2 100644 --- a/client/src/reducers/allReducers.ts +++ b/client/src/reducers/allReducers.ts @@ -2,6 +2,7 @@ import { combineReducers } from 'redux' import citiesReducer from './citiesReducer' +import competitionLoginReducer from './competitionLoginReducer' import competitionsReducer from './competitionsReducer' import editorReducer from './editorReducer' import presentationReducer from './presentationReducer' @@ -24,5 +25,6 @@ const allReducers = combineReducers({ searchUsers: searchUserReducer, types: typesReducer, statistics: statisticsReducer, + competitionLogin: competitionLoginReducer, }) export default allReducers diff --git a/client/src/reducers/competitionLoginReducer.ts b/client/src/reducers/competitionLoginReducer.ts new file mode 100644 index 0000000000000000000000000000000000000000..81c426a4a297f138fdfa6350e17a3cd4fc72d3fd --- /dev/null +++ b/client/src/reducers/competitionLoginReducer.ts @@ -0,0 +1,38 @@ +import { AnyAction } from 'redux' +import Types from '../actions/types' + +interface UIError { + message: string +} + +interface UserState { + loading: boolean + errors: null | UIError +} + +const initialState: UserState = { + loading: false, + errors: null, +} + +export default function (state = initialState, action: AnyAction) { + switch (action.type) { + case Types.SET_COMPETITION_LOGIN_ERRORS: + return { + errors: action.payload as UIError, + loading: false, + } + case Types.CLEAR_COMPETITION_LOGIN_ERRORS: + return { + loading: false, + errors: null, + } + case Types.LOADING_COMPETITION_LOGIN: + return { + ...state, + loading: true, + } + default: + return state + } +}