feature(database-connection): Fix routing and refactor style sheets

This commit is contained in:
2021-01-12 11:49:08 +01:00
parent 29b6974714
commit 816d5df943
15 changed files with 251 additions and 119 deletions

View File

@@ -7,7 +7,8 @@
text-align: center; text-align: center;
height: 100vh; height: 100vh;
color: whitesmoke; color: whitesmoke;
background-color: #282c34; } background-color: #282c34;
}
.first-place { .first-place {
font-size: xx-large; font-size: xx-large;

View File

@@ -1,4 +1,4 @@
.App { @mixin background() {
width: auto; width: auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -10,7 +10,7 @@
background-color: #282c34; background-color: #282c34;
} }
.blurred { @mixin blurred() {
-webkit-filter: blur(5px); -webkit-filter: blur(5px);
-moz-filter: blur(5px); -moz-filter: blur(5px);
-o-filter: blur(5px); -o-filter: blur(5px);

View File

@@ -1,4 +1,4 @@
import React from 'react'; import React, {FunctionComponent} from 'react';
import './App.scss'; import './App.scss';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'; import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
@@ -8,7 +8,20 @@ import MainPage, {IMainPageProps} from "./pages/MainPage/MainPage";
library.add(faArrowCircleLeft, faArrowCircleRight) library.add(faArrowCircleLeft, faArrowCircleRight)
export default class App extends React.Component { const App: FunctionComponent = () => (
<Router>
<Switch>
{/*<Route exact path={'/'} component={(props: IMainPageProps) => (<MainPage {...props}/>)} />*/}
<Route exact path={'/'} to={'/season/1'} />
<Route path={'/season/:id'} component={(props: IMainPageProps) => (<MainPage {...props}/>)} />
</Switch>
</Router>
);
export default App;
// export default class App extends React.Component {
// componentDidMount() { // componentDidMount() {
// this.setState({loading: false, mock: this.mockService.getStatsWithoutPromise()}); // this.setState({loading: false, mock: this.mockService.getStatsWithoutPromise()});
@@ -24,16 +37,4 @@ export default class App extends React.Component {
// error: err.error // error: err.error
// })) // }))
// }); // });
// } // }
render() {
return (
<Router>
<Switch>
<Route exact path={'/'} component={(props: IMainPageProps) => (<MainPage {...props}/>)} />
<Route path={'/season/:id'} component={(props: IMainPageProps) => (<MainPage {...props}/>)} />
</Switch>
</Router>
);
}
}

View File

@@ -1,3 +1,4 @@
.Header { .Header {
.title { .title {
font-size: 4vw; font-size: 4vw;

View File

@@ -1 +1,4 @@
.SeasonDetail {} .SeasonDetail {
vertical-align: middle;
justify-content: center;
}

View File

@@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import './SeasonDetail.scss'; import './SeasonDetail.scss';
import {IUserListProperties} from "../UserList/UserList";
export interface SeasonDetailProperties { export interface SeasonDetailProperties {
seasonId: string, seasonId: string,
@@ -9,10 +10,10 @@ export interface SeasonDetailProperties {
} }
} }
class SeasonDetail extends React.Component<SeasonDetailProperties> { class SeasonDetail extends React.Component<IUserListProperties> {
render() { render() {
const { seasonId, dates } = this.props const { seasonId, dates } = this.props.userStats
return ( return (
<span className="SeasonDetail" data-testid="SeasonDetail"> <span className="SeasonDetail" data-testid="SeasonDetail">
Season {seasonId} - Duration: {dates.start.toDateString()} - {dates.end.toDateString()} Season {seasonId} - Duration: {dates.start.toDateString()} - {dates.end.toDateString()}

View File

@@ -1 +1,13 @@
.SeasonSwitch {} .SeasonSwitch {
a {
font-size: 3vw;
max-font-size: 4vw;
padding-left: 1em;
padding-right: 1em;
vertical-align: middle;
justify-content: center;
}
}

View File

@@ -2,24 +2,27 @@ import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import './SeasonSwitch.scss'; import './SeasonSwitch.scss';
import SeasonDetail, { SeasonDetailProperties } from '../SeasonDetail/SeasonDetail'; import SeasonDetail from '../SeasonDetail/SeasonDetail';
import {withRouter} from "react-router"; import {IUserListProperties} from "../UserList/UserList";
class SeasonSwitch extends React.Component<SeasonDetailProperties> { class SeasonSwitch extends React.Component<IUserListProperties> {
render() { render() {
let seasonId = Number(this.props.seasonId) let seasonId = Number(this.props.userStats.seasonId)
return( return(
<div className="SeasonSwitch" data-testid="SeasonSwitch"> <div className="SeasonSwitch" data-testid="SeasonSwitch">
<Link to={"/season/" + (seasonId - 1)}> {
<FontAwesomeIcon icon="arrow-circle-left" /> seasonId > 1
</Link> && <Link to={"/season/" + (seasonId - 1)} onClick={() => this.props.onSeasonIdChange('' + (seasonId - 1))}>
<FontAwesomeIcon icon="arrow-circle-left" />
</Link>
}
<SeasonDetail {...this.props} /> <SeasonDetail {...this.props} />
<Link to={"/season/" + (seasonId + 1)}> <Link to={"/season/" + (seasonId + 1)} onClick={() => this.props.onSeasonIdChange('' + (seasonId + 1))}>
<FontAwesomeIcon icon="arrow-circle-right" /> <FontAwesomeIcon icon="arrow-circle-right" />
</Link> </Link>
</div> </div>
) )
} }

View File

@@ -15,5 +15,9 @@
font-size: larger; font-size: larger;
color: saddlebrown; color: saddlebrown;
} }
.SeasonSwitch {
padding-bottom: 1rem;
}
} }

View File

@@ -15,7 +15,7 @@ export default class UserList extends React.Component<IUserListProperties, IUser
constructor(props: Readonly<IUserListProperties> | IUserListProperties) { constructor(props: Readonly<IUserListProperties> | IUserListProperties) {
super(props); super(props);
this.state = { mocked: this.props.mocked } // this.state = { mocked: this.props.mocked }
} }
private createTableEntries(entries: TableEntry[]) { private createTableEntries(entries: TableEntry[]) {
@@ -51,7 +51,7 @@ export default class UserList extends React.Component<IUserListProperties, IUser
render() { render() {
return ( return (
<div className="UserList" data-testid="UserList"> <div className="UserList" data-testid="UserList">
<SeasonSwitch seasonId={this.props.userStats.seasonId} dates={this.props.userStats.dates} /> <SeasonSwitch onSeasonIdChange={this.props.onSeasonIdChange} userStats={this.props.userStats} />
<table> <table>
<thead> <thead>
<tr> <tr>
@@ -65,14 +65,15 @@ export default class UserList extends React.Component<IUserListProperties, IUser
{this.createTableEntries(this.props.userStats.stats)} {this.createTableEntries(this.props.userStats.stats)}
</tbody> </tbody>
</table> </table>
</div> </div>
) )
} }
} }
interface IUserListProperties { export interface IUserListProperties {
userStats: UserStatsResponse userStats: UserStatsResponse
mocked: boolean mocked?: boolean
onSeasonIdChange: any
} }
interface IUserListState { interface IUserListState {

View File

@@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import './index.css'; import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker'; import * as serviceWorker from './serviceWorker';
import App from "./App";
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>

View File

@@ -2,8 +2,8 @@ import TableEntry from "../models/TableEntry";
import UserStatsService from "../services/UserStatsService"; import UserStatsService from "../services/UserStatsService";
import UserStatsResponse from "../models/UserStatsResponse"; import UserStatsResponse from "../models/UserStatsResponse";
export default class UserStatsMockService extends UserStatsService { export default class UserStatsMockService {
private readonly mocks: UserStatsResponse[] = [ private static readonly mocks: UserStatsResponse[] = [
{ {
seasonId: "1", seasonId: "1",
dates: { dates: {
@@ -36,11 +36,12 @@ export default class UserStatsMockService extends UserStatsService {
} }
] ]
async getStats(seasonId: string): Promise<UserStatsResponse> { static async getStats(seasonId: string): Promise<UserStatsResponse> {
return Promise.resolve(this.mocks[Number(seasonId) - 1]) return Promise.resolve(this.mocks[Number(seasonId) - 1])
} }
getStatsWithoutPromise(seasonId: string): UserStatsResponse { static getStatsWithoutPromise(seasonId: string): UserStatsResponse {
return this.mocks[Number(seasonId) - 1] return this.mocks[Number(seasonId) - 1]
} }
} }

View File

@@ -1,3 +1,5 @@
@import "src/App.scss";
.MainPage { .MainPage {
@include background();
} }

View File

@@ -1,7 +1,5 @@
import React, {useEffect} from "react"; import React, {FC, FunctionComponent, useCallback, useEffect, useState} from "react";
import UserStatsService from "../../services/UserStatsService";
import UserStatsMockService from "../../mock/UserStatsMockService"; import UserStatsMockService from "../../mock/UserStatsMockService";
import {findDOMNode} from "react-dom";
import Header from "../../components/Header/Header"; import Header from "../../components/Header/Header";
import UserList from "../../components/UserList/UserList"; import UserList from "../../components/UserList/UserList";
import ErrorContainer from "../../components/ErrorContainer/ErrorContainer"; import ErrorContainer from "../../components/ErrorContainer/ErrorContainer";
@@ -9,77 +7,115 @@ import Footer from "../../components/Footer/Footer";
import {RouteComponentProps} from "react-router/index"; import {RouteComponentProps} from "react-router/index";
import UserStatsResponse from "../../models/UserStatsResponse"; import UserStatsResponse from "../../models/UserStatsResponse";
import {withRouter} from "react-router"; import {withRouter} from "react-router";
import RequestError from "../../models/RequestError";
import './MainPage.scss';
class MainPage extends React.Component<IMainPageProps, IMainPageState> { // class MainPage extends React.Component<IMainPageProps, IMainPageState> {
//
// private apiService: UserStatsService = new UserStatsService();
// private mockService = new UserStatsMockService();
//
// constructor(props: IMainPageProps) {
// super(props)
//
// console.log("this.props.match.params.id = " + this.props.match.params.id)
//
//
//
// let seasonId = !this.props.match.params.id ? "1" : this.props.match.params.id
//
// console.log("seasonId = " + seasonId)
//
// this.state = {
// id: seasonId,
// error: undefined,
// loading: false,
// users: this.mockService.getStatsWithoutPromise("1")
// }
// }
//
// componentWillMount() {
// this.fetchData();
// }
//
// componentDidUpdate(prevProps: Readonly<IMainPageProps>) {
// const element = findDOMNode(this);
// if (element != null) {
// window.scrollTo(0, 0);
// }
//
// if(prevProps.match.params.id !== this.props.match.params.id) {
// this.setState({ id: this.props.match.params.id })
// this.fetchData()
// }
// }
//
// private fetchData() {
// this.setState({ loading: true })
// // this.apiService.getStats(this.state.id)
// // .then(data => this.setState({
// // loading: false,
// // users: data
// // }))
// this.mockService.getStats(this.state.id)
// .then(data => this.setState({
// loading: false,
// users: data
// }))
// }
//
// render() {
// console.log("this.state.id = " + this.state.id)
// console.log("this.state.users.seasonId = " + this.state.users.seasonId)
//
// return (
// <div className="MainPage">
// <Header />
// {
// this.state.error && <ErrorContainer message={this.state.error} />
// }
// {
// (this.state.loading)
// ? <UserList userStats={this.state.users} mocked={true} />
// : <UserList userStats={this.state.users} mocked={!(!this.state.error)} />
// }
// {/* { this.state.error != null ? <p className="error-message"> { this.state.error } Please try again later!</p> : null} */}
// <Footer />
// </div>
// )
// }
// }
private apiService: UserStatsService = new UserStatsService(); const MainPage: FC<IMainPageProps> = (props: IMainPageProps) => {
private mockService = new UserStatsMockService(); const [seasonId, setSeasonId] = useState(props.match.params.id)
const [error, setError] = useState<RequestError | undefined>(undefined)
const [loading, setLoadingState] = useState(true)
const [seasonStats, setSeasonStats] = useState<UserStatsResponse>(UserStatsMockService.getStatsWithoutPromise(seasonId))
constructor(props: IMainPageProps) { const toggleLoading = useCallback(() => setLoadingState(!loading), [setLoadingState, loading])
super(props)
console.log("this.props.match.params.id = " + this.props.match.params.id) useEffect(() => {
UserStatsMockService.getStats(seasonId)
.then(res => setSeasonStats(res))
.catch(err => setError(err))
.finally(() => toggleLoading())
}, [seasonId, toggleLoading])
return (
<div className="MainPage">
let seasonId = !this.props.match.params.id ? "1" : this.props.match.params.id <Header />
{
console.log("seasonId = " + seasonId) error && <ErrorContainer message={error.message} />
}
this.state = { {
id: seasonId, (loading)
error: undefined, ? <UserList onSeasonIdChange={setSeasonId} userStats={seasonStats} mocked={true} />
loading: false, : <UserList onSeasonIdChange={setSeasonId} userStats={seasonStats} mocked={!(!error)} />
users: this.mockService.getStatsWithoutPromise("1") }
} {/* { this.state.error != null ? <p className="error-message"> { this.state.error } Please try again later!</p> : null} */}
} <Footer />
</div>
componentWillMount() { )
this.fetchData();
}
componentDidUpdate(prevProps: Readonly<IMainPageProps>) {
const element = findDOMNode(this);
if (element != null) {
window.scrollTo(0, 0);
}
// if(prevProps.match.params.id !== this.props.match.params.id) {
// this.setState({ id: this.props.match.params.id })
// }
}
private fetchData() {
this.setState({ loading: true })
// this.apiService.getStats(this.state.id)
// .then(data => this.setState({
// loading: false,
// users: data
// }))
this.mockService.getStats(this.state.id)
.then(data => this.setState({
loading: false,
users: data
}))
}
render() {
return (
<div className="MainPage">
<Header />
{
this.state.error && <ErrorContainer message={this.state.error} />
}
{
(this.state.loading)
? <UserList userStats={this.state.users} mocked={true} />
: <UserList userStats={this.state.users} mocked={!(!this.state.error)} />
}
{/* { this.state.error != null ? <p className="error-message"> { this.state.error } Please try again later!</p> : null} */}
<Footer />
</div>
)
}
} }
export interface IMainPageProps extends RouteComponentProps<{ id: string }> { export interface IMainPageProps extends RouteComponentProps<{ id: string }> {

View File

@@ -1,22 +1,22 @@
import RequestError from "../models/RequestError"; import RequestError from "../models/RequestError";
import UserStatsResponse from "../models/UserStatsResponse"; import UserStatsResponse from "../models/UserStatsResponse";
import {useEffect, useState} from "react";
export default class UserStatsService { export default class UserStatsService {
private apiURL = 'https://api.tsotr.humenius.me/stats' private static apiURL = 'https://api.tsotr.humenius.me/stats'
private requestInit: RequestInit = { private static requestInit: RequestInit = {
method: 'GET', method: 'GET',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
}; };
async getStats(seasonId: string): Promise<UserStatsResponse> { public static async getStats(seasonId: string): Promise<UserStatsResponse> {
return fetch(this.apiURL, this.requestInit) return fetch(this.apiURL, this.requestInit)
.then(res => UserStatsService.checkResponse(res)) .then(res => UserStatsService.checkResponse(res))
.then(data => data.json()); .then(data => data.json());
} }
private static checkResponse(response: any): any { private static checkResponse(response: any): any {
@@ -29,3 +29,69 @@ export default class UserStatsService {
return response; return response;
} }
} }
// const checkResponse = <T extends Response>(response: T): T => {
// if (!response.ok) {
// console.log(response);
// let error = new RequestError(response.statusText);
// error.response = response;
// throw error;
// }
// return response;
// }
//
// const useApiService = <T>(url: string, method: string = 'GET'): { response: T | null; error: RequestError | null } => {
// const baseUrl = "https://api.tsotr.humenius.me"
// const [response, setResponse] = useState<T | null>(null)
// const [error, setError] = useState<RequestError | null>(null)
// const requestInit = {
// method: method,
// headers: {
// 'Content-Type': 'application/json'
// }
// }
//
// useEffect(() => {
// const fetchData = async (): Promise<void> => {
// 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<UserStatsResponse | null>(null)
// // const [error, setError] = useState<RequestError | null>(null)
// // const requestInit = {
// // method: 'GET',
// // headers: {
// // 'Content-Type': 'application/json'
// // }
// // }
// //
// // useEffect(() => {
// // const fetchData = async (): Promise<void> => {
// // 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<UserStatsResponse>('/stats')
//
// export default useApiService;