feature(database-connection): Add routing for dynamic seasons
This commit is contained in:
@@ -1,51 +1,15 @@
|
||||
import React from 'react';
|
||||
import './App.scss';
|
||||
import UserStatsMockService from "./mock/UserStatsMockService";
|
||||
import UserStatsService from "./services/UserStatsService";
|
||||
import {findDOMNode} from "react-dom";
|
||||
import TableEntry from "./models/TableEntry";
|
||||
import UserList from './components/UserList/UserList';
|
||||
import ErrorContainer from './components/ErrorContainer/ErrorContainer';
|
||||
import Footer from './components/Footer/Footer';
|
||||
import Header from './components/Header/Header';
|
||||
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
|
||||
|
||||
interface State {
|
||||
error?: string,
|
||||
loading: boolean,
|
||||
users: TableEntry[],
|
||||
mock: TableEntry[]
|
||||
}
|
||||
import {library} from "@fortawesome/fontawesome-svg-core";
|
||||
import {faArrowCircleLeft, faArrowCircleRight} from "@fortawesome/free-solid-svg-icons";
|
||||
import MainPage, {IMainPageProps} from "./pages/MainPage/MainPage";
|
||||
|
||||
library.add(faArrowCircleLeft, faArrowCircleRight)
|
||||
|
||||
export default class App extends React.Component {
|
||||
|
||||
private apiService: UserStatsService = new UserStatsService();
|
||||
private mockService = new UserStatsMockService();
|
||||
|
||||
state: State;
|
||||
|
||||
constructor(props: Readonly<{}>) {
|
||||
super(props);
|
||||
this.state = {
|
||||
error: undefined,
|
||||
loading: false,
|
||||
users: [],
|
||||
mock: this.mockService.getStatsWithoutPromise()
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
private fetchData() {
|
||||
this.setState({ loading: true })
|
||||
this.apiService.getStats()
|
||||
.then(data => this.setState({
|
||||
loading: false,
|
||||
users: data
|
||||
}))
|
||||
}
|
||||
|
||||
// componentDidMount() {
|
||||
// this.setState({loading: false, mock: this.mockService.getStatsWithoutPromise()});
|
||||
// this.apiService.getStats()
|
||||
@@ -62,28 +26,14 @@ export default class App extends React.Component {
|
||||
// });
|
||||
// }
|
||||
|
||||
componentDidUpdate() {
|
||||
const element = findDOMNode(this);
|
||||
if (element != null) {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="App">
|
||||
<Header />
|
||||
{/* { this.state.error != null ? <p className="error-message"> { this.state.error } Please try again later!</p> : null} */}
|
||||
{
|
||||
(!this.state.error && this.state.loading)
|
||||
? <UserList users={this.state.mock} />
|
||||
: <UserList users={this.state.users} />
|
||||
}
|
||||
{
|
||||
this.state.error && <ErrorContainer message={this.state.error} />
|
||||
}
|
||||
<Footer />
|
||||
</div>
|
||||
<Router>
|
||||
<Switch>
|
||||
<Route exact path={'/'} component={(props: IMainPageProps) => (<MainPage {...props}/>)} />
|
||||
<Route path={'/season/:id'} component={(props: IMainPageProps) => (<MainPage {...props}/>)} />
|
||||
</Switch>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import ErrorContainer from './ErrorContainer';
|
||||
|
||||
describe('<ErrorContainer />', () => {
|
||||
test('it should mount', () => {
|
||||
render(<ErrorContainer />);
|
||||
|
||||
const errorContainer = screen.getByTestId('ErrorContainer');
|
||||
|
||||
expect(errorContainer).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import Footer from './Footer';
|
||||
|
||||
describe('<Footer />', () => {
|
||||
test('it should mount', () => {
|
||||
render(<Footer />);
|
||||
|
||||
const footer = screen.getByTestId('Footer');
|
||||
|
||||
expect(footer).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import Header from './Header';
|
||||
|
||||
describe('<Header />', () => {
|
||||
test('it should mount', () => {
|
||||
render(<Header />);
|
||||
|
||||
const header = screen.getByTestId('Header');
|
||||
|
||||
expect(header).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
1
frontend/src/components/SeasonDetail/SeasonDetail.scss
Normal file
1
frontend/src/components/SeasonDetail/SeasonDetail.scss
Normal file
@@ -0,0 +1 @@
|
||||
.SeasonDetail {}
|
||||
24
frontend/src/components/SeasonDetail/SeasonDetail.tsx
Normal file
24
frontend/src/components/SeasonDetail/SeasonDetail.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import './SeasonDetail.scss';
|
||||
|
||||
export interface SeasonDetailProperties {
|
||||
seasonId: string,
|
||||
dates: {
|
||||
start: Date,
|
||||
end: Date
|
||||
}
|
||||
}
|
||||
|
||||
class SeasonDetail extends React.Component<SeasonDetailProperties> {
|
||||
|
||||
render() {
|
||||
const { seasonId, dates } = this.props
|
||||
return (
|
||||
<span className="SeasonDetail" data-testid="SeasonDetail">
|
||||
Season {seasonId} - Duration: {dates.start.toDateString()} - {dates.end.toDateString()}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default SeasonDetail;
|
||||
1
frontend/src/components/SeasonSwitch/SeasonSwitch.scss
Normal file
1
frontend/src/components/SeasonSwitch/SeasonSwitch.scss
Normal file
@@ -0,0 +1 @@
|
||||
.SeasonSwitch {}
|
||||
28
frontend/src/components/SeasonSwitch/SeasonSwitch.tsx
Normal file
28
frontend/src/components/SeasonSwitch/SeasonSwitch.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import './SeasonSwitch.scss';
|
||||
import SeasonDetail, { SeasonDetailProperties } from '../SeasonDetail/SeasonDetail';
|
||||
import {withRouter} from "react-router";
|
||||
|
||||
class SeasonSwitch extends React.Component<SeasonDetailProperties> {
|
||||
|
||||
render() {
|
||||
let seasonId = Number(this.props.seasonId)
|
||||
return(
|
||||
<div className="SeasonSwitch" data-testid="SeasonSwitch">
|
||||
<Link to={"/season/" + (seasonId - 1)}>
|
||||
<FontAwesomeIcon icon="arrow-circle-left" />
|
||||
</Link>
|
||||
|
||||
<SeasonDetail {...this.props} />
|
||||
|
||||
<Link to={"/season/" + (seasonId + 1)}>
|
||||
<FontAwesomeIcon icon="arrow-circle-right" />
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default SeasonSwitch;
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import UserList from './UserList';
|
||||
|
||||
describe('<UserList />', () => {
|
||||
test('it should mount', () => {
|
||||
render(<UserList />);
|
||||
|
||||
const userList = screen.getByTestId('UserList');
|
||||
|
||||
expect(userList).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import TableEntry from '../../models/TableEntry';
|
||||
import './UserList.scss';
|
||||
import SeasonSwitch from "../SeasonSwitch/SeasonSwitch";
|
||||
import UserStatsResponse from "../../models/UserStatsResponse";
|
||||
|
||||
// const UserList: React.FC = () => (
|
||||
// <div className="UserList" data-testid="UserList">
|
||||
@@ -8,17 +10,15 @@ import './UserList.scss';
|
||||
// </div>
|
||||
// );
|
||||
|
||||
interface UserListProperties {
|
||||
users: TableEntry[]
|
||||
}
|
||||
export default class UserList extends React.Component<IUserListProperties, IUserListState> {
|
||||
|
||||
class UserList extends React.Component<UserListProperties> {
|
||||
constructor(props: Readonly<IUserListProperties> | IUserListProperties) {
|
||||
super(props);
|
||||
|
||||
constructor(props: Readonly<UserListProperties>) {
|
||||
super(props);
|
||||
}
|
||||
this.state = { mocked: this.props.mocked }
|
||||
}
|
||||
|
||||
private createTableEntries(entries: TableEntry[]) {
|
||||
private createTableEntries(entries: TableEntry[]) {
|
||||
return entries.map((entry, index) => {
|
||||
const placement = index + 1;
|
||||
const placementClassName = placement === 1 ? "first-place"
|
||||
@@ -51,6 +51,7 @@ class UserList extends React.Component<UserListProperties> {
|
||||
render() {
|
||||
return (
|
||||
<div className="UserList" data-testid="UserList">
|
||||
<SeasonSwitch seasonId={this.props.userStats.seasonId} dates={this.props.userStats.dates} />
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -61,7 +62,7 @@ class UserList extends React.Component<UserListProperties> {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.createTableEntries(this.props.users)}
|
||||
{this.createTableEntries(this.props.userStats.stats)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -69,4 +70,11 @@ class UserList extends React.Component<UserListProperties> {
|
||||
}
|
||||
}
|
||||
|
||||
export default UserList;
|
||||
interface IUserListProperties {
|
||||
userStats: UserStatsResponse
|
||||
mocked: boolean
|
||||
}
|
||||
|
||||
interface IUserListState {
|
||||
mocked: boolean
|
||||
}
|
||||
@@ -1,26 +1,46 @@
|
||||
import TableEntry from "../models/TableEntry";
|
||||
import UserStatsService from "../services/UserStatsService";
|
||||
import UserStatsResponse from "../models/UserStatsResponse";
|
||||
|
||||
export default class UserStatsMockService extends UserStatsService {
|
||||
private readonly entries: TableEntry[];
|
||||
private readonly mocks: UserStatsResponse[] = [
|
||||
{
|
||||
seasonId: "1",
|
||||
dates: {
|
||||
start: new Date(2019, 12, 5),
|
||||
end: new Date()
|
||||
},
|
||||
stats: [
|
||||
{ name: "Humen", rank: "Overwatch Noob 2", onlineTime: "0d 1h 0m 0s" },
|
||||
{ name: "Humen", rank: "Random Rank 3 4", onlineTime: "0d 1h 0m 0s" },
|
||||
{ name: "Humen", rank: "Kas is cool 5", onlineTime: "0d 1h 0m 0s" },
|
||||
{ name: "Humen", rank: "Bremsspu 6r", onlineTime: "0d 1h 0m 0s" },
|
||||
{ name: "Humen", rank: "127 3", onlineTime: "0d 1h 0m 0s" },
|
||||
{ name: "Humen", rank: "ok343", onlineTime: "0d 1h 0m 0s" }
|
||||
]
|
||||
},
|
||||
{
|
||||
seasonId: "2",
|
||||
dates: {
|
||||
start: new Date(new Date().getFullYear(), 0, 1),
|
||||
end: new Date()
|
||||
},
|
||||
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" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.entries = [
|
||||
{ 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" }
|
||||
];
|
||||
async getStats(seasonId: string): Promise<UserStatsResponse> {
|
||||
return Promise.resolve(this.mocks[Number(seasonId) - 1])
|
||||
}
|
||||
|
||||
async getStats(): Promise<TableEntry[]> {
|
||||
return Promise.resolve(this.entries);
|
||||
}
|
||||
|
||||
getStatsWithoutPromise(): TableEntry[] {
|
||||
return this.entries;
|
||||
getStatsWithoutPromise(seasonId: string): UserStatsResponse {
|
||||
return this.mocks[Number(seasonId) - 1]
|
||||
}
|
||||
}
|
||||
|
||||
6
frontend/src/models/UserStatsResponse.tsx
Normal file
6
frontend/src/models/UserStatsResponse.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import TableEntry from "./TableEntry";
|
||||
import {SeasonDetailProperties} from "../components/SeasonDetail/SeasonDetail";
|
||||
|
||||
export default interface UserStatsResponse extends SeasonDetailProperties {
|
||||
stats: TableEntry[]
|
||||
}
|
||||
3
frontend/src/pages/MainPage/MainPage.scss
Normal file
3
frontend/src/pages/MainPage/MainPage.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.MainPage {
|
||||
|
||||
}
|
||||
96
frontend/src/pages/MainPage/MainPage.tsx
Normal file
96
frontend/src/pages/MainPage/MainPage.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import React, {useEffect} from "react";
|
||||
import UserStatsService from "../../services/UserStatsService";
|
||||
import UserStatsMockService from "../../mock/UserStatsMockService";
|
||||
import {findDOMNode} from "react-dom";
|
||||
import Header from "../../components/Header/Header";
|
||||
import UserList from "../../components/UserList/UserList";
|
||||
import ErrorContainer from "../../components/ErrorContainer/ErrorContainer";
|
||||
import Footer from "../../components/Footer/Footer";
|
||||
import {RouteComponentProps} from "react-router/index";
|
||||
import UserStatsResponse from "../../models/UserStatsResponse";
|
||||
import {withRouter} from "react-router";
|
||||
|
||||
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 })
|
||||
// }
|
||||
}
|
||||
|
||||
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 }> {
|
||||
|
||||
}
|
||||
|
||||
interface IMainPageState {
|
||||
id: string;
|
||||
error?: string;
|
||||
loading: boolean;
|
||||
users: UserStatsResponse;
|
||||
}
|
||||
|
||||
export default withRouter(MainPage)
|
||||
@@ -1,5 +1,5 @@
|
||||
import TableEntry from "../models/TableEntry";
|
||||
import RequestError from "../models/RequestError";
|
||||
import UserStatsResponse from "../models/UserStatsResponse";
|
||||
|
||||
|
||||
export default class UserStatsService {
|
||||
@@ -13,7 +13,7 @@ export default class UserStatsService {
|
||||
}
|
||||
};
|
||||
|
||||
async getStats(): Promise<TableEntry[]> {
|
||||
async getStats(seasonId: string): Promise<UserStatsResponse> {
|
||||
return fetch(this.apiURL, this.requestInit)
|
||||
.then(res => UserStatsService.checkResponse(res))
|
||||
.then(data => data.json());
|
||||
|
||||
Reference in New Issue
Block a user