diff --git a/backend/src/app.controller.ts b/backend/src/app.controller.ts index f35a87c..41b4ac3 100644 --- a/backend/src/app.controller.ts +++ b/backend/src/app.controller.ts @@ -32,8 +32,9 @@ export class AppController { return await this.sinusBotService.fetchStats(); } - @Get('/stats/season/:id') + @Get('/stats/season/:id?') async getStats(@Param('id') id?: string): Promise { + logger.info(`Got id ${id}`) return await this.databaseService .fetchStats(id) .catch(err => { diff --git a/backend/src/database/database.service.ts b/backend/src/database/database.service.ts index 4f95ffa..3e35043 100644 --- a/backend/src/database/database.service.ts +++ b/backend/src/database/database.service.ts @@ -7,6 +7,7 @@ import { } from '../models/aliases'; import { PrismaService } from '../prisma/prisma.service'; import { seasons } from '@prisma/client'; +import logger from 'src/logger/Logger'; @Injectable() export class DatabaseService { @@ -25,7 +26,7 @@ export class DatabaseService { }) .then(value => this.prismaClient.seasons.findUnique({ where: { - season_id: Number((!seasonId ? value : seasonId)) + season_id: Number(seasonId ?? value) } }) as Promise) .then((result: SeasonInfo) => { diff --git a/frontend/package.json b/frontend/package.json index 6d3aadb..27a9300 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,8 +14,8 @@ "husky": "^4.3.7", "lint-staged": "^10.5.3", "prettier": "^2.2.1", - "react": "^16.13.1", - "react-dom": "^16.13.1", + "react": "17.0.0", + "react-dom": "17.0.0", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", "react-scripts": "3.4.1", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6ae09a8..05c9703 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -11,30 +11,10 @@ library.add(faArrowCircleLeft, faArrowCircleRight) const App: FunctionComponent = () => ( - {/* ()} />*/} - ()} /> + ()} /> ); -export default App; - - -// export default class App extends React.Component { - - // componentDidMount() { - // this.setState({loading: false, mock: this.mockService.getStatsWithoutPromise()}); - // this.apiService.getStats() - // .then(data => this.setState({ - // isLoaded: true, - // users: data - // })) - // .catch(error => { - // error.response.json() - // .then((err: any) => this.setState({ - // isLoaded: true, - // error: err.error - // })) - // }); - // } \ No newline at end of file +export default App; \ No newline at end of file diff --git a/frontend/src/components/SeasonDetail/SeasonDetail.tsx b/frontend/src/components/SeasonDetail/SeasonDetail.tsx index 041d7c4..bc85441 100644 --- a/frontend/src/components/SeasonDetail/SeasonDetail.tsx +++ b/frontend/src/components/SeasonDetail/SeasonDetail.tsx @@ -7,17 +7,21 @@ const SeasonDetail: React.FC = (props: IUserListProperties) return ( - Season {seasonId} - Duration: {dates.start.toDateString()} - {dates.end.toDateString()} + Season {seasonId} - Duration: {dateToString(dates.start)} - {dateToString(dates.end)} ) } +const dateToString = (date?: Date): string => { + return date ? date.toLocaleDateString('en-GB') : 'TBD'; +} + export interface SeasonDetailProperties { - seasonId: string, + seasonId?: string, maxSeasonId: string dates: { - start: Date, - end: Date + start?: Date, + end?: Date } } diff --git a/frontend/src/components/SeasonSwitch/SeasonSwitch.tsx b/frontend/src/components/SeasonSwitch/SeasonSwitch.tsx index ec60103..33912e6 100644 --- a/frontend/src/components/SeasonSwitch/SeasonSwitch.tsx +++ b/frontend/src/components/SeasonSwitch/SeasonSwitch.tsx @@ -10,14 +10,18 @@ const SeasonSwitch: React.FC = (props: IUserListProperties) const maxSeasonId = Number(props.userStats.maxSeasonId) return ( -
+
{ seasonId > 1 && (props.enabled - ? props.onSeasonIdChange('' + (seasonId - 1))}> + && { + console.log("nothing happens") + props.onSeasonIdChange('' + (seasonId - 1))}}> - : e.preventDefault()}> + || { + console.log("nothing happens2") + e.preventDefault()}}> ) } diff --git a/frontend/src/components/UserList/UserList.tsx b/frontend/src/components/UserList/UserList.tsx index 1513529..56bdce1 100644 --- a/frontend/src/components/UserList/UserList.tsx +++ b/frontend/src/components/UserList/UserList.tsx @@ -24,7 +24,7 @@ const createTableEntries = (entries: TableEntry[]) => const UserList: React.FC = (props: IUserListProperties) => (
- +
@@ -43,7 +43,7 @@ const UserList: React.FC = (props: IUserListProperties) => export interface IUserListProperties { userStats: UserStatsResponse mocked?: boolean - onSeasonIdChange: any + onSeasonIdChange: React.Dispatch> enabled: boolean } diff --git a/frontend/src/mock/UserStatsMockService.ts b/frontend/src/mock/UserStatsMockService.ts index a552a4c..6d47c8e 100644 --- a/frontend/src/mock/UserStatsMockService.ts +++ b/frontend/src/mock/UserStatsMockService.ts @@ -1,6 +1,7 @@ import UserStatsResponse from "../models/UserStatsResponse"; +import UserStatsService from "../services/UserStatsService"; -export default class UserStatsMockService { +export default class UserStatsMockService extends UserStatsService { private static readonly mocks: UserStatsResponse[] = [ { seasonId: "1", @@ -33,14 +34,30 @@ export default class UserStatsMockService { { name: "Humen", rank: "12", onlineTime: "0d 1h 0m 0s" }, { name: "Humen", rank: "ok", onlineTime: "0d 1h 0m 0s" } ] - } + }, + { + seasonId: 'undefined', + maxSeasonId: "2", + dates: { + start: undefined, + end: undefined + }, + stats: [ + { name: "Humen", rank: "Overwatch Noob", onlineTime: "0d 1h 0m 0s" }, + { name: "Humen", rank: "Random Rank 3", onlineTime: "0d 1h 0m 0s" }, + { name: "Humen", rank: "Kas is cool", onlineTime: "0d 1h 0m 0s" }, + { name: "Humen", rank: "Bremsspur", onlineTime: "0d 1h 0m 0s" }, + { name: "Humen", rank: "12", onlineTime: "0d 1h 0m 0s" }, + { name: "Humen", rank: "ok", onlineTime: "0d 1h 0m 0s" } + ] + } ] - static async getStats(seasonId: string): Promise { - return Promise.resolve(this.mocks[Number(seasonId) - 1]) + public static async getStats(seasonId?: string): Promise { + return Promise.resolve(this.mocks[Number((seasonId ? seasonId : 2)) - 1]) } - static getStatsWithoutPromise(seasonId: string): UserStatsResponse { - return this.mocks[Number(seasonId) - 1] + static getStatsWithoutPromise(seasonId?: string): UserStatsResponse { + return this.mocks[Number((seasonId ? seasonId : 2)) - 1] } } diff --git a/frontend/src/pages/MainPage/MainPage.tsx b/frontend/src/pages/MainPage/MainPage.tsx index e16560a..544dfed 100644 --- a/frontend/src/pages/MainPage/MainPage.tsx +++ b/frontend/src/pages/MainPage/MainPage.tsx @@ -13,13 +13,15 @@ import {ClipLoader} from "react-spinners"; import UserStatsService from "../../services/UserStatsService"; const MainPage: FC = (props: IMainPageProps) => { - const [seasonId, setSeasonId] = useState(props.match.params.id) - const [error, setError] = useState(undefined) - const [loading, setLoadingState] = useState(true) + const [seasonId, setSeasonId] = useState(props.match.params.id) + const [error, setError] = useState(undefined) + const [loading, setLoadingState] = useState(true) const [seasonStats, setSeasonStats] = useState(UserStatsMockService.getStatsWithoutPromise(seasonId)) const [spinnerColor] = useState('#61dafb') useEffect(() => { + setLoadingState(true) + setSeasonStats(UserStatsMockService.getStatsWithoutPromise('3')) UserStatsService.getStats(seasonId) .then(res => { setSeasonStats(res) @@ -30,7 +32,7 @@ const MainPage: FC = (props: IMainPageProps) => { setLoadingState(false) setError(new RequestError(0, "Could not retrieve stats. Try again later.")) }) - }, [seasonId, setLoadingState]) + }, [seasonId]) const spinnerCss = ` margin: 0; @@ -40,22 +42,20 @@ const MainPage: FC = (props: IMainPageProps) => { return (
-
+
{ error && } -
- -
+
) } -export interface IMainPageProps extends RouteComponentProps<{ id: string }> { +export interface IMainPageProps extends RouteComponentProps<{ id?: string }> { } diff --git a/frontend/src/services/UserStatsService.ts b/frontend/src/services/UserStatsService.ts index 2d5d2a5..cbdc4ed 100644 --- a/frontend/src/services/UserStatsService.ts +++ b/frontend/src/services/UserStatsService.ts @@ -4,8 +4,7 @@ import UserStatsResponse from "../models/UserStatsResponse"; export default class UserStatsService { private static apiURL = 'https://api.tsotr.humenius.me/stats' - // private static apiURL = 'http://localhost:3500/stats' - //private static apiURL = 'https://mock.codes/501' + //private static apiURL = 'http://localhost:3500/stats' private static requestInit: RequestInit = { method: 'GET', @@ -14,13 +13,13 @@ export default class UserStatsService { }, }; - public static async getStats(seasonId: string): Promise { - return fetch(`${this.apiURL}/season/${seasonId}`, this.requestInit) + public static async getStats(seasonId?: string): Promise { + return fetch(`${this.apiURL}/season/${seasonId ?? ''}`, this.requestInit) .then(res => UserStatsService.checkResponse(res)) .then(data => data.json()) .then(data => { - data.dates.start = new Date(data.dates.start) - data.dates.end = new Date(data.dates.end) + data.dates.start = data.dates.start ? new Date(data.dates.start) : undefined + data.dates.end = data.dates.end ? new Date(data.dates.end) : undefined return data }) } @@ -32,70 +31,4 @@ export default class UserStatsService { } return response; } -} - -// const checkResponse = (response: T): T => { -// if (!response.ok) { -// console.log(response); -// let error = new RequestError(response.statusText); -// error.response = response; -// throw error; -// } -// return response; -// } -// -// const useApiService = (url: string, method: string = 'GET'): { response: T | null; error: RequestError | null } => { -// const baseUrl = "https://api.tsotr.humenius.me" -// const [response, setResponse] = useState(null) -// const [error, setError] = useState(null) -// const requestInit = { -// method: method, -// headers: { -// 'Content-Type': 'application/json' -// } -// } -// -// useEffect(() => { -// const fetchData = async (): Promise => { -// const result = await fetch(`${baseUrl}${url}`, requestInit) -// .then(res => checkResponse(res)) -// .then(data => data.json()) -// .catch(err => setError(err)) -// setResponse(result) -// } -// -// fetchData() -// }, [url]) -// -// return { response, error } -// } -// -// // const seasonStatsQuery = (seasonId: number): { response: UserStatsResponse | null; error: RequestError | null } => { -// // const baseUrl = "https://api.tsotr.humenius.me/stats/season/" -// // const [response, setResponse] = useState(null) -// // const [error, setError] = useState(null) -// // const requestInit = { -// // method: 'GET', -// // headers: { -// // 'Content-Type': 'application/json' -// // } -// // } -// // -// // useEffect(() => { -// // const fetchData = async (): Promise => { -// // const result = await fetch(baseUrl, requestInit) -// // .then(res => checkResponse(res)) -// // .then(data => data.json()) -// // .catch(err => setError(err)) -// // setResponse(result) -// // } -// // -// // fetchData() -// // }, [seasonId]) -// // -// // return { response, error } -// // } -// -// const currentSeasonStatsQuery = useApiService('/stats') -// -// export default useApiService; \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 4699d81..b6decf3 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1667,11 +1667,6 @@ resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== -"@types/isomorphic-fetch@^0.0.35": - version "0.0.35" - resolved "https://registry.yarnpkg.com/@types/isomorphic-fetch/-/isomorphic-fetch-0.0.35.tgz#c1c0d402daac324582b6186b91f8905340ea3361" - integrity sha512-DaZNUvLDCAnCTjgwxgiL1eQdxIKEpNLOlTNtAgnZc50bG2copGhRrFN9/PxPBuJe+tZVLCbQ7ls0xveXVRPkvw== - "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -4806,11 +4801,6 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fetch-retry-ts@^1.1.23: - version "1.1.23" - resolved "https://registry.yarnpkg.com/fetch-retry-ts/-/fetch-retry-ts-1.1.23.tgz#7659974215c7a66b9bb81c006e5acee34d932ca8" - integrity sha512-UwqrMGfDPRa60etXl1HkDgIhY9k2AoB/Qa41/U2thzHtA/NVHGSmn037dw4iOg62ccyX2imtkU8SFfSwxLLEYA== - figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" @@ -6179,14 +6169,6 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -isomorphic-fetch@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" - integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== - dependencies: - node-fetch "^2.6.1" - whatwg-fetch "^3.4.1" - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -7522,11 +7504,6 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-fetch@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - node-forge@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" @@ -9241,15 +9218,14 @@ react-dev-utils@^10.2.1: strip-ansi "6.0.0" text-table "0.2.0" -react-dom@^16.13.1: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" - integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== +react-dom@17.0.0: + version "17.0.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.0.tgz#f8266e4d9861584553ccbd186d596a1c7dd8dcb4" + integrity sha512-OGnFbxCjI2TMAZYMVxi4hqheJiN8rCEVVrL7XIGzCB6beNc4Am8M47HtkvxODZw9QgjmAPKpLba9FTu4fC1byA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.19.1" + scheduler "^0.20.0" react-error-overlay@^6.0.7: version "6.0.8" @@ -9362,14 +9338,13 @@ react-spinners@^0.10.4: dependencies: "@emotion/core" "^10.0.35" -react@^16.13.1: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" - integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== +react@17.0.0: + version "17.0.0" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.0.tgz#ad96d5fa1a33bb9b06d0cc52672f7992d84aa662" + integrity sha512-rG9bqS3LMuetoSUKHN8G3fMNuQOePKDThK6+2yXFWtoeTDLVNh/QCaxT+Jr+rNf4lwNXpx+atdn3Aa0oi8/6eQ== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" - prop-types "^15.6.2" read-pkg-up@^2.0.0: version "2.0.0" @@ -9864,10 +9839,10 @@ saxes@^3.1.9: dependencies: xmlchars "^2.1.1" -scheduler@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" - integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== +scheduler@^0.20.0: + version "0.20.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.1.tgz#da0b907e24026b01181ecbc75efdc7f27b5a000c" + integrity sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -11335,7 +11310,7 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" -whatwg-fetch@^3.0.0, whatwg-fetch@^3.4.1: +whatwg-fetch@^3.0.0: version "3.5.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz#605a2cd0a7146e5db141e29d1c62ab84c0c4c868" integrity sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A==
Placement