diff --git a/client/package.json b/client/package.json index 7c354ece5569314b108c67617fecf9e0f9d637e0..8733d1d45b7c2e4c2349167573e7411aa66afd45 100644 --- a/client/package.json +++ b/client/package.json @@ -96,5 +96,5 @@ "html" ] }, - "proxy": "http://localhost:5000/api/" + "proxy": "http://localhost:5000/" } diff --git a/client/src/actions/cities.ts b/client/src/actions/cities.ts index 9226aee57b2f477d85d9d5fa781174d097773afc..594a43c43355f1c63d9248a2f98d28b4c243c820 100644 --- a/client/src/actions/cities.ts +++ b/client/src/actions/cities.ts @@ -4,7 +4,7 @@ import Types from './types' export const getCities = () => async (dispatch: AppDispatch) => { await axios - .get('/misc/cities') + .get('/api/misc/cities') .then((res) => { dispatch({ type: Types.SET_CITIES, diff --git a/client/src/actions/competitions.ts b/client/src/actions/competitions.ts index 7d248e46c05df64e948b483c5092616c4a828f35..1a9d789c09d6646657b622de1d4c43d0c9be1dbd 100644 --- a/client/src/actions/competitions.ts +++ b/client/src/actions/competitions.ts @@ -15,7 +15,7 @@ export const getCompetitions = () => async (dispatch: AppDispatch, getState: () year: currentParams.year, } await axios - .get('/competitions/search', { params }) + .get('/api/competitions/search', { params }) .then((res) => { dispatch({ type: Types.SET_COMPETITIONS, diff --git a/client/src/actions/editor.ts b/client/src/actions/editor.ts index 903dfa7bda973511d3eb5b13e31ac02a8f7bc2ee..45fbce986abae8df3da0c987be6faa4212cac2bf 100644 --- a/client/src/actions/editor.ts +++ b/client/src/actions/editor.ts @@ -4,7 +4,7 @@ import Types from './types' export const getEditorCompetition = (id: string) => async (dispatch: AppDispatch, getState: () => RootState) => { await axios - .get(`/competitions/${id}`) + .get(`/api/competitions/${id}`) .then((res) => { dispatch({ type: Types.SET_EDITOR_COMPETITION, diff --git a/client/src/actions/presentation.ts b/client/src/actions/presentation.ts index c967131f903809ac8af8e11c0218fb614de75227..97c611dfb27015f37fa33693ee90c6f8a1159ef3 100644 --- a/client/src/actions/presentation.ts +++ b/client/src/actions/presentation.ts @@ -6,7 +6,7 @@ import Types from './types' export const getPresentationCompetition = (id: string) => async (dispatch: AppDispatch) => { await axios - .get(`/competitions/${id}`) + .get(`/api/competitions/${id}`) .then((res) => { dispatch({ type: Types.SET_PRESENTATION_COMPETITION, @@ -20,7 +20,7 @@ export const getPresentationCompetition = (id: string) => async (dispatch: AppDi export const getPresentationTeams = (id: string) => async (dispatch: AppDispatch) => { await axios - .get(`/competitions/${id}/teams`) + .get(`/api/competitions/${id}/teams`) .then((res) => { dispatch({ type: Types.SET_PRESENTATION_TEAMS, diff --git a/client/src/actions/roles.ts b/client/src/actions/roles.ts index 0160353b184755b33f003592ad127880af2803d7..19a148c7a01d9dc4ccd6552f06dbf954509f16dc 100644 --- a/client/src/actions/roles.ts +++ b/client/src/actions/roles.ts @@ -4,7 +4,7 @@ import Types from './types' export const getRoles = () => async (dispatch: AppDispatch) => { await axios - .get('/misc/roles') + .get('/api/misc/roles') .then((res) => { dispatch({ type: Types.SET_ROLES, diff --git a/client/src/actions/searchUser.ts b/client/src/actions/searchUser.ts index c6211056bda8250f1b5c849450d018ef4bd42819..a3ea55c7ce7ef5afc1468de7cc1d350303b3674a 100644 --- a/client/src/actions/searchUser.ts +++ b/client/src/actions/searchUser.ts @@ -15,7 +15,7 @@ export const getSearchUsers = () => async (dispatch: AppDispatch, getState: () = email: currentParams.email, } await axios - .get('/users/search', { params }) + .get('/api/users/search', { params }) .then((res) => { dispatch({ type: Types.SET_SEARCH_USERS, diff --git a/client/src/actions/statistics.ts b/client/src/actions/statistics.ts new file mode 100644 index 0000000000000000000000000000000000000000..a32dce3553ba56ed47410b1499aeca57f7490f75 --- /dev/null +++ b/client/src/actions/statistics.ts @@ -0,0 +1,15 @@ +import axios from 'axios' +import { AppDispatch } from './../store' +import Types from './types' + +export const getStatistics = () => async (dispatch: AppDispatch) => { + await axios + .get('/api/misc/statistics') + .then((res) => { + dispatch({ + type: Types.SET_STATISTICS, + payload: res.data, + }) + }) + .catch((err) => console.log(err)) +} diff --git a/client/src/actions/types.ts b/client/src/actions/types.ts index e500f2c8b2e7281c85559c9b86590a5f3269c080..5932deb0b24a027b5fc329113f5513253a82ff2a 100644 --- a/client/src/actions/types.ts +++ b/client/src/actions/types.ts @@ -33,4 +33,5 @@ export default { SET_MEDIA_FILENAME: 'SET_MEDIA_ID', SET_MEDIA_TYPE_ID: 'SET_MEDIA_TYPE_ID', SET_MEDIA_USER_ID: 'SET_MEDIA_USER_ID', + SET_STATISTICS: 'SET_STATISTICS', } diff --git a/client/src/actions/typesAction.ts b/client/src/actions/typesAction.ts index 4fcde4f55ee72cb9f72f6d79f0e5f1db6995fa4b..32a212b0c5fc270d62dd6528efbea51824acdb8e 100644 --- a/client/src/actions/typesAction.ts +++ b/client/src/actions/typesAction.ts @@ -4,7 +4,7 @@ import Types from './types' export const getTypes = () => async (dispatch: AppDispatch) => { await axios - .get('/misc/types') + .get('/api/misc/types') .then((res) => { dispatch({ type: Types.SET_TYPES, diff --git a/client/src/actions/user.ts b/client/src/actions/user.ts index 371dcaeb6b9426c4235e9fc837257f5bbc6625f9..e2ad88acf776490ee49189236f2f921297c33d5c 100644 --- a/client/src/actions/user.ts +++ b/client/src/actions/user.ts @@ -7,7 +7,7 @@ import Types from './types' export const loginUser = (userData: AccountLoginModel, history: History) => async (dispatch: AppDispatch) => { dispatch({ type: Types.LOADING_UI }) await axios - .post('/auth/login', userData) + .post('/api/auth/login', userData) .then((res) => { const token = `Bearer ${res.data.access_token}` localStorage.setItem('token', token) //setting token to local storage @@ -28,7 +28,7 @@ export const loginUser = (userData: AccountLoginModel, history: History) => asyn export const getUserData = () => async (dispatch: AppDispatch) => { dispatch({ type: Types.LOADING_USER }) await axios - .get('/users') + .get('/api/users') .then((res) => { dispatch({ type: Types.SET_USER, @@ -42,7 +42,7 @@ export const getUserData = () => async (dispatch: AppDispatch) => { export const logoutUser = () => async (dispatch: AppDispatch) => { localStorage.removeItem('token') - await axios.post('/auth/logout').then(() => { + await axios.post('/api/auth/logout').then(() => { delete axios.defaults.headers.common['Authorization'] dispatch({ type: Types.SET_UNAUTHENTICATED, diff --git a/client/src/pages/admin/AdminPage.tsx b/client/src/pages/admin/AdminPage.tsx index 30cfe656c4bb336d82d3056849008c2c71bbac3d..1b543083da7fbfb1e5c1a48d44a40a2d7fdcc17e 100644 --- a/client/src/pages/admin/AdminPage.tsx +++ b/client/src/pages/admin/AdminPage.tsx @@ -20,6 +20,7 @@ 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 { getStatistics } from '../../actions/statistics' import { getTypes } from '../../actions/typesAction' import { logoutUser } from '../../actions/user' import { useAppDispatch, useAppSelector } from '../../hooks' @@ -71,6 +72,7 @@ const AdminView: React.FC = () => { dispatch(getCities()) dispatch(getRoles()) dispatch(getTypes()) + dispatch(getStatistics()) }, []) const menuAdminItems = [ diff --git a/client/src/pages/admin/competitions/AddCompetition.tsx b/client/src/pages/admin/competitions/AddCompetition.tsx index 0ce9a6ffa4601cf543383e7efa3bdf55eee259f8..6053f2f8de9c3d13cde3403ec1a62317e477d047 100644 --- a/client/src/pages/admin/competitions/AddCompetition.tsx +++ b/client/src/pages/admin/competitions/AddCompetition.tsx @@ -63,7 +63,7 @@ const AddCompetition: React.FC = (props: any) => { } await axios - .post('/competitions', params) // send to database + .post('/api/competitions', params) // send to database .then(() => { actions.resetForm() // reset the form setAnchorEl(null) diff --git a/client/src/pages/admin/competitions/CompetitionManager.tsx b/client/src/pages/admin/competitions/CompetitionManager.tsx index 929d3145d33fedd061f426af6e85461c1a355b77..83f399b95a0c083727333c2f842a1c61a8eca2d0 100644 --- a/client/src/pages/admin/competitions/CompetitionManager.tsx +++ b/client/src/pages/admin/competitions/CompetitionManager.tsx @@ -82,7 +82,7 @@ const CompetitionManager: React.FC = (props: any) => { const handleDeleteCompetition = async () => { if (activeId) { await axios - .delete(`/competitions/${activeId}`) + .delete(`/api/competitions/${activeId}`) .then(() => { setAnchorEl(null) dispatch(getCompetitions()) // refresh the competition list @@ -101,7 +101,7 @@ const CompetitionManager: React.FC = (props: any) => { const handleDuplicateCompetition = async () => { if (activeId) { await axios - .post(`/competitions/${activeId}/copy`) + .post(`/api/competitions/${activeId}/copy`) .then(() => { setAnchorEl(null) dispatch(getCompetitions()) diff --git a/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx b/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx index 87d8272ae035ce56d509dd99380bdb1ed17426a6..eb667ebd690f6013c184ac13e2038ce1a7726de9 100644 --- a/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx +++ b/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx @@ -1,24 +1,19 @@ import { Box, Typography } from '@material-ui/core' -import React, { useEffect } from 'react' -import { getCompetitions } from '../../../../actions/competitions' -import { useAppDispatch, useAppSelector } from '../../../../hooks' +import React from 'react' +import { useAppSelector } from '../../../../hooks' const NumberOfCompetitions: React.FC = () => { - const competitions = useAppSelector((state) => state.competitions.competitions) - const dispatch = useAppDispatch() + const competitions = useAppSelector((state) => state.statistics.competitions) const handleCount = () => { - if (competitions.length >= 1000000) { - ;<div>{competitions.length / 1000000 + 'M'}</div> - } else if (competitions.length >= 1000) { - ;<div>{competitions.length / 1000 + 'K'}</div> + if (competitions >= 1000000) { + ;<div>{competitions / 1000000 + 'M'}</div> + } else if (competitions >= 1000) { + ;<div>{competitions / 1000 + 'K'}</div> } - return <div>{competitions.length}</div> + return <div>{competitions}</div> } - useEffect(() => { - dispatch(getCompetitions()) - }, []) return ( <div> <Box width="100%" height="100%"> diff --git a/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx b/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx index 360b3663b0e45e9a24018f855ede3023c95bf39a..f3195f4c18969ed2ad1e4b185b2125ac2c15f782 100644 --- a/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx +++ b/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx @@ -4,7 +4,7 @@ import { getCities } from '../../../../actions/cities' import { useAppDispatch, useAppSelector } from '../../../../hooks' const NumberOfRegions: React.FC = () => { - const regions = useAppSelector((state) => state.cities.total) + const regions = useAppSelector((state) => state.statistics.regions) const dispatch = useAppDispatch() const handleCount = () => { diff --git a/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx b/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx index af0f97677565aa264a61269c274cf890eec774d8..0e75cf1b1069814801fadc010f30ac393dfe3b25 100644 --- a/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx +++ b/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx @@ -4,7 +4,7 @@ import { getSearchUsers } from '../../../../actions/searchUser' import { useAppDispatch, useAppSelector } from '../../../../hooks' const NumberOfUsers: React.FC = () => { - const usersTotal = useAppSelector((state) => state.searchUsers.total) + const usersTotal = useAppSelector((state) => state.statistics.users) const dispatch = useAppDispatch() const handleCount = () => { diff --git a/client/src/pages/admin/regions/AddRegion.tsx b/client/src/pages/admin/regions/AddRegion.tsx index c8ae412e680bda652327193af44cb728e36c1621..b00961384f73246365e60023af04ed7567adf311 100644 --- a/client/src/pages/admin/regions/AddRegion.tsx +++ b/client/src/pages/admin/regions/AddRegion.tsx @@ -51,7 +51,7 @@ const AddRegion: React.FC = (props: any) => { name: values.model.name, } await axios - .post('/misc/cities', params) + .post('/api/misc/cities', params) .then(() => { actions.resetForm() dispatch(getCities()) diff --git a/client/src/pages/admin/regions/Regions.tsx b/client/src/pages/admin/regions/Regions.tsx index eb79575ea7857658742f30f02d5f96a5e87e7f6d..436da6f69429ca6711452d7babf477f8da593eee 100644 --- a/client/src/pages/admin/regions/Regions.tsx +++ b/client/src/pages/admin/regions/Regions.tsx @@ -45,7 +45,7 @@ const RegionManager: React.FC = (props: any) => { const handleDeleteCity = async () => { if (activeId) { await axios - .delete(`/misc/cities/${activeId}`) + .delete(`/api/misc/cities/${activeId}`) .then(() => { setAnchorEl(null) dispatch(getCities()) @@ -63,7 +63,7 @@ const RegionManager: React.FC = (props: any) => { const handleAddCity = async () => { await axios - .post(`/misc/cities`, { name: newCity }) + .post(`/api/misc/cities`, { name: newCity }) .then(() => { setAnchorEl(null) dispatch(getCities()) diff --git a/client/src/pages/admin/users/AddUser.tsx b/client/src/pages/admin/users/AddUser.tsx index d58a5d5dc153463618015e9c4195958f5160bbcb..9f1511eac460c661ebd35dc995f6606e66080359 100644 --- a/client/src/pages/admin/users/AddUser.tsx +++ b/client/src/pages/admin/users/AddUser.tsx @@ -59,7 +59,7 @@ const AddUser: React.FC = (props: any) => { role_id: selectedRole?.id as number, } await axios - .post('/auth/signup', params) + .post('/api/auth/signup', params) .then(() => { actions.resetForm() setAnchorEl(null) diff --git a/client/src/pages/admin/users/EditUser.tsx b/client/src/pages/admin/users/EditUser.tsx index 7db5aeecd2be0c3f3f94c22a0c459a71cbc47aa4..9b4a5d1bd6aecd92a03f44221c2cb40d40e2ab70 100644 --- a/client/src/pages/admin/users/EditUser.tsx +++ b/client/src/pages/admin/users/EditUser.tsx @@ -110,7 +110,7 @@ const EditUser = ({ user }: UserIdProps) => { const handleDeleteUsers = async () => { setOpen(false) await axios - .delete(`/auth/delete/${user.id}`) + .delete(`/api/auth/delete/${user.id}`) .then(() => { setAnchorEl(null) dispatch(getSearchUsers()) @@ -141,7 +141,7 @@ const EditUser = ({ user }: UserIdProps) => { req['role_id'] = params.role_id } await axios - .put('/users/' + user.id, req) + .put('/api/users/' + user.id, req) .then((res) => { setAnchorEl(null) dispatch(getSearchUsers()) diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx index 956d0b7212baf5a3eed212c86e9946550b66e72f..c645724e3c7fabda69a2ad2434f4bd7b587c8c2e 100644 --- a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx +++ b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx @@ -28,7 +28,7 @@ it('renders presentation editor', () => { }, } ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => { - if (path.startsWith('/competitions')) return Promise.resolve(competitionRes) + if (path.startsWith('/api/competitions')) return Promise.resolve(competitionRes) return Promise.resolve(citiesRes) }) render( diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.tsx index 4087d6c798d17522dbaffe3f8c7041b57795338f..da18cd52fd6222b4a37c7a88bc111f0c4974ecc9 100644 --- a/client/src/pages/presentationEditor/PresentationEditorPage.tsx +++ b/client/src/pages/presentationEditor/PresentationEditorPage.tsx @@ -104,7 +104,7 @@ const PresentationEditorPage: React.FC = () => { } const createNewSlide = async () => { - await axios.post(`/competitions/${id}/slides`, { title: 'new slide' }) + await axios.post(`/api/competitions/${id}/slides`, { title: 'new slide' }) dispatch(getEditorCompetition(id)) } @@ -128,13 +128,13 @@ const PresentationEditorPage: React.FC = () => { } const handleRemoveSlide = async () => { - await axios.delete(`/competitions/${id}/slides/${contextState.slideId}`) + await axios.delete(`/api/competitions/${id}/slides/${contextState.slideId}`) dispatch(getEditorCompetition(id)) setContextState(initialState) } const handleDuplicateSlide = async () => { - await axios.post(`/competitions/${id}/slides/${contextState.slideId}/copy`) + await axios.post(`/api/competitions/${id}/slides/${contextState.slideId}/copy`) dispatch(getEditorCompetition(id)) setContextState(initialState) } diff --git a/client/src/pages/presentationEditor/components/CompetitionSettings.tsx b/client/src/pages/presentationEditor/components/CompetitionSettings.tsx index 75d418403d7ec51e13563adf3c9afcf2ef15152f..53c2ecdb4dd23cc81df0140bd2bede66dfe18b88 100644 --- a/client/src/pages/presentationEditor/components/CompetitionSettings.tsx +++ b/client/src/pages/presentationEditor/components/CompetitionSettings.tsx @@ -77,7 +77,7 @@ const CompetitionSettings: React.FC = () => { const competition = useAppSelector((state) => state.editor.competition) const updateCompetitionName = async (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => { await axios - .put(`/competitions/${id}`, { name: event.target.value }) + .put(`/api/competitions/${id}`, { name: event.target.value }) .then(() => { dispatch(getEditorCompetition(id)) }) @@ -87,7 +87,7 @@ const CompetitionSettings: React.FC = () => { const cities = useAppSelector((state) => state.cities.cities) const updateCompetitionCity = async (city: City) => { await axios - .put(`/competitions/${id}`, { city_id: city.id }) + .put(`/api/competitions/${id}`, { city_id: city.id }) .then(() => { dispatch(getEditorCompetition(id)) }) @@ -104,7 +104,7 @@ const CompetitionSettings: React.FC = () => { const removeTeam = async (tid: number) => { await axios - .delete(`/competitions/${id}/teams/${tid}`) + .delete(`/api/competitions/${id}/teams/${tid}`) .then(() => { dispatch(getEditorCompetition(id)) }) @@ -113,7 +113,7 @@ const CompetitionSettings: React.FC = () => { const addTeam = async () => { setAddTeamOpen(false) await axios - .post(`/competitions/${id}/teams`, { name: selectedTeamName }) + .post(`/api/competitions/${id}/teams`, { name: selectedTeamName }) .then(() => { dispatch(getEditorCompetition(id)) }) diff --git a/client/src/pages/presentationEditor/components/RndComponent.tsx b/client/src/pages/presentationEditor/components/RndComponent.tsx index 11875755f58067003b55fe70f8ae34e5031ae164..ed8170ff17718ad5cd76307648194cf396526724 100644 --- a/client/src/pages/presentationEditor/components/RndComponent.tsx +++ b/client/src/pages/presentationEditor/components/RndComponent.tsx @@ -20,13 +20,13 @@ const RndComponent = ({ component }: ImageComponentProps) => { const competitionId = useAppSelector((state) => state.editor.competition.id) const slideId = useAppSelector((state) => state.editor.activeSlideId) const handleUpdatePos = (pos: Position) => { - axios.put(`/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, { + axios.put(`/api/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, { x: pos.x, y: pos.y, }) } const handleUpdateSize = (size: Size) => { - axios.put(`/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, { + axios.put(`/api/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, { w: size.w, h: size.h, }) diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx index 1d8aad5fab5545a7820ed506cf05096d7de1ec6d..e06f1ce31e131044ed7216d1b808cb225633c55f 100644 --- a/client/src/pages/presentationEditor/components/SlideSettings.tsx +++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx @@ -147,7 +147,7 @@ const SlideSettings: React.FC = () => { if (selectedSlideType === 0) { // Change slide type from a question type to information await axios - .delete(`/competitions/${id}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}`) + .delete(`/api/competitions/${id}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}`) .then(() => { dispatch(getEditorCompetition(id)) }) @@ -155,10 +155,10 @@ const SlideSettings: React.FC = () => { } else { // Change slide type from question type to another question type await axios - .delete(`/competitions/${id}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}`) + .delete(`/api/competitions/${id}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}`) .catch(console.log) await axios - .post(`/competitions/${id}/slides/${activeSlide.id}/questions`, { + .post(`/api/competitions/${id}/slides/${activeSlide.id}/questions`, { name: 'Ny fråga', total_score: 0, type_id: selectedSlideType, @@ -172,7 +172,7 @@ const SlideSettings: React.FC = () => { } else if (selectedSlideType !== 0) { // Change slide type from information to a question type await axios - .post(`/competitions/${id}/slides/${activeSlide.id}/questions`, { + .post(`/api/competitions/${id}/slides/${activeSlide.id}/questions`, { name: 'Ny fråga', total_score: 0, type_id: selectedSlideType, @@ -222,10 +222,13 @@ const SlideSettings: React.FC = () => { const addAlternative = async () => { if (activeSlide && activeSlide.questions[0]) { await axios - .post(`/competitions/${id}/slides/${activeSlide?.id}/questions/${activeSlide?.questions[0].id}/alternatives`, { - text: '', - value: 0, - }) + .post( + `/api/competitions/${id}/slides/${activeSlide?.id}/questions/${activeSlide?.questions[0].id}/alternatives`, + { + text: '', + value: 0, + } + ) .then(() => { dispatch(getEditorCompetition(id)) }) @@ -280,7 +283,7 @@ const SlideSettings: React.FC = () => { const handleAddText = async () => { if (activeSlide) { - await axios.post(`/competitions/${id}/slides/${activeSlide?.id}/components`, { + await axios.post(`/api/competitions/${id}/slides/${activeSlide?.id}/components`, { type_id: 1, data: { text: 'Ny text' }, w: 315, @@ -304,7 +307,7 @@ const SlideSettings: React.FC = () => { setTimer(+event.target.value) if (activeSlide) { await axios - .put(`/competitions/${id}/slides/${activeSlide.id}`, { timer: event.target.value }) + .put(`/api/competitions/${id}/slides/${activeSlide.id}`, { timer: event.target.value }) .then(() => { dispatch(getEditorCompetition(id)) }) diff --git a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx index e0f0d5b475b87f2db7f71337c1c4978716fdb5e1..c347bd5b6845c0413251907d514b31e825bd1e68 100644 --- a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx +++ b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx @@ -37,7 +37,7 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => { setTimerHandle( window.setTimeout(async () => { console.log('Content was updated on server. id: ', component.id) - await axios.put(`/competitions/${competitionId}/slides/${activeSlideId}/components/${component.id}`, { + await axios.put(`/api/competitions/${competitionId}/slides/${activeSlideId}/components/${component.id}`, { data: { ...component.data, text: a }, }) dispatch(getEditorCompetition(id)) @@ -46,7 +46,7 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => { } const handleDeleteText = async (componentId: number) => { - await axios.delete(`/competitions/${id}/slides/${activeSlideId}/components/${componentId}`) + await axios.delete(`/api/competitions/${id}/slides/${activeSlideId}/components/${componentId}`) dispatch(getEditorCompetition(id)) } diff --git a/client/src/reducers/allReducers.ts b/client/src/reducers/allReducers.ts index 2a25bad73b3b039db9d2bec12b206bf303fad506..398ec0a71669a6eab2aaf851ac0ea0f10b226b91 100644 --- a/client/src/reducers/allReducers.ts +++ b/client/src/reducers/allReducers.ts @@ -8,6 +8,7 @@ import mediaReducer from './mediaReducer' import presentationReducer from './presentationReducer' import rolesReducer from './rolesReducer' import searchUserReducer from './searchUserReducer' +import statisticsReducer from './statisticsReducer' import typesReducer from './typesReducer' import uiReducer from './uiReducer' import userReducer from './userReducer' @@ -24,5 +25,6 @@ const allReducers = combineReducers({ searchUsers: searchUserReducer, types: typesReducer, media: mediaReducer, + statistics: statisticsReducer, }) export default allReducers diff --git a/client/src/reducers/statisticsReducer.ts b/client/src/reducers/statisticsReducer.ts new file mode 100644 index 0000000000000000000000000000000000000000..78a06e1157f6428c10ea17073afbdf46cf8609e5 --- /dev/null +++ b/client/src/reducers/statisticsReducer.ts @@ -0,0 +1,24 @@ +import { AnyAction } from 'redux' +import Types from '../actions/types' + +interface StatisticsState { + users: number + competitions: number + regions: number +} + +const initialState: StatisticsState = { + users: 0, + competitions: 0, + regions: 0, +} + +export default function (state = initialState, action: AnyAction) { + switch (action.type) { + case Types.SET_STATISTICS: + state = action.payload as StatisticsState + return state + default: + return state + } +} diff --git a/client/src/utils/checkAuthentication.ts b/client/src/utils/checkAuthentication.ts index a782ea10f79a65998ed7d1a9e091361aa6a48982..9225aa29858d2f9ea58f8ac118dd2feed01d1cc5 100644 --- a/client/src/utils/checkAuthentication.ts +++ b/client/src/utils/checkAuthentication.ts @@ -16,7 +16,7 @@ export const CheckAuthentication = async () => { axios.defaults.headers.common['Authorization'] = authToken store.dispatch({ type: Types.LOADING_USER }) await axios - .get('/users') + .get('/api/users') .then((res) => { store.dispatch({ type: Types.SET_AUTHENTICATED }) store.dispatch({ diff --git a/server/app/__init__.py b/server/app/__init__.py index 9215563a9538bf1d33aecabc2b1abbc32779950a..2add65446ea6ca429a204a1564813d9fe19e25d0 100644 --- a/server/app/__init__.py +++ b/server/app/__init__.py @@ -34,7 +34,7 @@ def create_app(config_name="configmodule.DevelopmentConfig"): return redirect(rp[:-1]) @app.after_request - def set_core(response): + def set_corse(response): header = response.headers header["Access-Control-Allow-Origin"] = "*" return response diff --git a/server/app/apis/alternatives.py b/server/app/apis/alternatives.py index d56a2f8ba0163a9187aa451eac45a416890052c6..ce7b4e7d3bb8a550aa52cbc0b2492c1118999876 100644 --- a/server/app/apis/alternatives.py +++ b/server/app/apis/alternatives.py @@ -13,7 +13,7 @@ schema = QuestionAlternativeDTO.schema list_schema = QuestionAlternativeDTO.list_schema -@api.route("/") +@api.route("") @api.param("competition_id, slide_id, question_id") class QuestionAlternativeList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/answers.py b/server/app/apis/answers.py index 308b1001de9fa325b7ff01c4e518f7db9e3c10eb..0ef3003931d52d304d66cff1cedb36b0c8cfa777 100644 --- a/server/app/apis/answers.py +++ b/server/app/apis/answers.py @@ -13,7 +13,7 @@ schema = QuestionAnswerDTO.schema list_schema = QuestionAnswerDTO.list_schema -@api.route("/") +@api.route("") @api.param("competition_id, team_id") class QuestionAnswerList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/codes.py b/server/app/apis/codes.py index c761420fabab70254973a7d352ce6f9b7833ba25..2a2eea5c55c6e46cf516ff35dd79ed3d96f58679 100644 --- a/server/app/apis/codes.py +++ b/server/app/apis/codes.py @@ -12,7 +12,7 @@ schema = CodeDTO.schema list_schema = CodeDTO.list_schema -@api.route("/") +@api.route("") @api.param("competition_id") class CodesList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py index 386d4051c5e32f852a9a165810e3303812e58440..4a6bf79d6bf17d132e3a5fed6a403b21762bbc13 100644 --- a/server/app/apis/competitions.py +++ b/server/app/apis/competitions.py @@ -15,7 +15,7 @@ rich_schema = CompetitionDTO.rich_schema list_schema = CompetitionDTO.list_schema -@api.route("/") +@api.route("") class CompetitionsList(Resource): @check_jwt(editor=True) def post(self): diff --git a/server/app/apis/components.py b/server/app/apis/components.py index c895f02322d16c8f603b97c7c60644db4ea36233..23d250256120f4bf600b10cd010b5de9aa67e0dc 100644 --- a/server/app/apis/components.py +++ b/server/app/apis/components.py @@ -35,7 +35,7 @@ class ComponentByID(Resource): return {}, codes.NO_CONTENT -@api.route("/") +@api.route("") @api.param("competition_id, slide_id") class ComponentList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/misc.py b/server/app/apis/misc.py index 5364a5e44d4e309c327bf91121bc1f8d95e40afa..8b3bd5e57e5b877fdaed85f3e6a372b4984d8ef8 100644 --- a/server/app/apis/misc.py +++ b/server/app/apis/misc.py @@ -1,7 +1,8 @@ import app.database.controller as dbc from app.apis import check_jwt, item_response, list_response +from app.core import http_codes from app.core.dto import MiscDTO -from app.database.models import City, ComponentType, MediaType, QuestionType, Role, ViewType +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 @@ -72,3 +73,13 @@ class Cities(Resource): dbc.delete.default(item) items = dbc.get.all(City) return list_response(city_schema.dump(items)) + + +@api.route("/statistics") +class Statistics(Resource): + @check_jwt(editor=True) + def get(self): + user_count = User.query.count() + competition_count = Competition.query.count() + region_count = City.query.count() + return {"users": user_count, "competitions": competition_count, "regions": region_count}, http_codes.OK diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py index ef8cf89c463519b8d60d78ec37bc440c494b8018..9ca4a5f229488e03931ec544dd3ccf43406e5b9e 100644 --- a/server/app/apis/slides.py +++ b/server/app/apis/slides.py @@ -12,7 +12,7 @@ schema = SlideDTO.schema list_schema = SlideDTO.list_schema -@api.route("/") +@api.route("") @api.param("competition_id") class SlidesList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/teams.py b/server/app/apis/teams.py index 6596244c9085b850106e567a6974ced5428413aa..514748ae27c7ff685d2b1099d1bf710610928860 100644 --- a/server/app/apis/teams.py +++ b/server/app/apis/teams.py @@ -12,7 +12,7 @@ schema = TeamDTO.schema list_schema = TeamDTO.list_schema -@api.route("/") +@api.route("") @api.param("competition_id") class TeamsList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/users.py b/server/app/apis/users.py index 767f01cb441553e717aea58a202a03f0b50ece64..1bae000bc6821ff7c7ac3dde82474bd351c2fe4d 100644 --- a/server/app/apis/users.py +++ b/server/app/apis/users.py @@ -27,7 +27,7 @@ def edit_user(item_user, args): return dbc.edit.default(item_user, **args) -@api.route("/") +@api.route("") class UsersList(Resource): @check_jwt(editor=True) def get(self): diff --git a/server/app/core/schemas.py b/server/app/core/schemas.py index 79bdbc1c48fd7460e85fe18f13a744f45a0a57e1..378f99af09167fc7ea6d7b40798d355cff5e8514 100644 --- a/server/app/core/schemas.py +++ b/server/app/core/schemas.py @@ -1,3 +1,4 @@ +from marshmallow.decorators import pre_dump import app.database.models as models from app.core import ma from marshmallow_sqlalchemy import fields @@ -37,8 +38,9 @@ class CodeSchema(IdNameSchema): id = ma.auto_field() code = ma.auto_field() - pointer = ma.auto_field() view_type_id = ma.auto_field() + competition_id = fields.fields.Integer() + team_id = fields.fields.Integer() class ViewTypeSchema(IdNameSchema): diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py index 216160cdd9172305eb926a46940baf31352b548e..1f57f7d06c74d9a0fd9999c99a5108b1d59ad25f 100644 --- a/server/app/database/controller/add.py +++ b/server/app/database/controller/add.py @@ -151,11 +151,11 @@ def question_answer(data, score, question_id, team_id): return db_add(QuestionAnswer(data, score, question_id, team_id)) -def code(pointer, view_type_id): +def code(view_type_id, competition_id=None, team_id=None): """ Adds a code to the database using the provided arguments. """ code_string = utils.generate_unique_code() - return db_add(Code(code_string, pointer, view_type_id)) + return db_add(Code(code_string, view_type_id, competition_id, team_id)) def team(name, competition_id): @@ -164,7 +164,7 @@ def team(name, competition_id): item = db_add(Team(name, competition_id)) # Add code for the team - code(item.id, 1) + code(1, competition_id, item.id) return item @@ -209,10 +209,9 @@ def competition(name, year, city_id): slide(item_competition.id) # Add code for Judge view - code(item_competition.id, 2) - + code(2, item_competition.id) # Add code for Audience view - code(item_competition.id, 3) + code(3, item_competition.id) item_competition = utils.refresh(item_competition) return item_competition diff --git a/server/app/database/controller/get.py b/server/app/database/controller/get.py index 15e908ef401c7e63b99c5767bcc5440ea5e7a820..eb0a77117a2df976d08c4e5d8f34e2f94db5f26f 100644 --- a/server/app/database/controller/get.py +++ b/server/app/database/controller/get.py @@ -39,13 +39,10 @@ def code_by_code(code): def code_list(competition_id): """ Gets a list of all code objects associated with a the provided competition. """ - - team_view_id = 1 - join_filters = (Code.view_type_id == team_view_id) & (Team.id == Code.pointer) - filters = ((Code.view_type_id != team_view_id) & (Code.pointer == competition_id))( - (Code.view_type_id == team_view_id) & (competition_id == Team.competition_id) - ) - return Code.query.join(Team, join_filters, isouter=True).filter(filters).all() + # team_view_id = 1 + join_competition = Competition.id == Code.competition_id + filters = Competition.id == competition_id + return Code.query.join(Competition, join_competition).filter(filters).all() ### Users ### diff --git a/server/app/database/models.py b/server/app/database/models.py index cded9ddf7aa00d2b5545a2ae5f40cf28eecba209..7174080b799eeea7d5956ca641c0c8fdca8d5ee2 100644 --- a/server/app/database/models.py +++ b/server/app/database/models.py @@ -205,17 +205,17 @@ class Component(db.Model): class Code(db.Model): - table_args = (db.UniqueConstraint("pointer", "type"),) id = db.Column(db.Integer, primary_key=True) code = db.Column(db.Text, unique=True) - pointer = db.Column(db.Integer, nullable=False) - view_type_id = db.Column(db.Integer, db.ForeignKey("view_type.id"), nullable=False) + competition_id = db.Column(db.Integer, db.ForeignKey("competition.id"), nullable=False) + team_id = db.Column(db.Integer, db.ForeignKey("team.id"), nullable=True) - def __init__(self, code, pointer, view_type_id): + def __init__(self, code, view_type_id, competition_id=None, team_id=None): self.code = code - self.pointer = pointer self.view_type_id = view_type_id + self.competition_id = competition_id + self.team_id = team_id class ViewType(db.Model):