feat: Implement routing

This commit is contained in:
Alexander Polynomdivision 2018-08-26 16:23:48 +02:00
parent 297d2e50f9
commit 6734e59a1b
6 changed files with 193 additions and 12 deletions

View File

@ -4,7 +4,15 @@ import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar"; import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import { BrowserRouter, Route, Redirect } from "react-router-dom";
import AuthRoute from "../security/AuthRoute";
import Dashboard from "../pages/dashboard";
import LoginPage from "../pages/login"; import LoginPage from "../pages/login";
import LessonsPage from "../pages/lessons";
import { ILesson } from "../models/lesson";
interface IState { interface IState {
loggedIn: boolean; loggedIn: boolean;
@ -19,17 +27,45 @@ export default class Application extends React.Component<{}, IState> {
}; };
this.login = this.login.bind(this); this.login = this.login.bind(this);
this.isAuthenticated = this.isAuthenticated.bind(this);
}
lessons(): ILesson[] {
const lessons = [{
name: "Der Bauer auf dem Feld",
desc: "So fängt alles an: Du bist ein einfacher Bauer und musst dich die Karriereleiter mit deinen freshen Latein-Skills hinaufarbeiten",
level: 1,
done: true,
}, {
name: "???",
desc: "Warum schreibe ich überhaupt was?dsd dddddddddddddddddddddd",
level: 2,
done: false,
}];
return lessons;
} }
login(username: string, password: string): Promise<boolean> { login(username: string, password: string): Promise<boolean> {
return new Promise((res, rej) => { return new Promise((res, rej) => {
// TODO // TODO
this.setState({
loggedIn: true
});
res(); res();
}); });
} }
// Checks whether the user is logged in
isAuthenticated() {
// TODO: Security?
return this.state.loggedIn === true;
}
render() { render() {
return <div className="flex"> return <BrowserRouter
basename="/">
<div className="flex">
<AppBar position="static"> <AppBar position="static">
<Toolbar> <Toolbar>
<Typography className="flex" variant="title" color="inherit"> <Typography className="flex" variant="title" color="inherit">
@ -38,7 +74,15 @@ export default class Application extends React.Component<{}, IState> {
</Toolbar> </Toolbar>
</AppBar> </AppBar>
<LoginPage login={this.login} /> <div className="content">
</div>; <Route exact path="/" component={() => <Redirect to="/login" />} />
<Route exact path="/login" component={() => {
return <LoginPage loggedIn={this.state.loggedIn} login={this.login} />
}} />
<AuthRoute isAuth={this.isAuthenticated} path="/dashboard" component={() => <Dashboard lessons={this.lessons()} />} />
<AuthRoute isAuth={this.isAuthenticated} path="/lessons" component={() => <LessonsPage lessons={this.lessons()} />} />
</div>
</div>
</BrowserRouter>;
} }
}; };

7
src/models/lesson.ts Normal file
View File

@ -0,0 +1,7 @@
export interface ILesson {
name: string;
desc: string;
level: number;
done: boolean;
}

52
src/pages/dashboard.tsx Normal file
View File

@ -0,0 +1,52 @@
import * as React from "react";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import { ILesson } from "../models/lesson";
interface IProps {
lessons: ILesson[];
}
export default class Dashboard extends React.Component<{}> {
constructor(props: any) {
super(props);
}
render() {
const small = window.matchMedia("(max-width: 700px)").matches;
const cName = small ? "lesson-card-xs" : "lesson-card-lg";
let key = 0;
const lessonToCard = (lesson: ILesson) => {
return <Grid item key={key++}>
<Card style={{
width: small ? window.width - 32 : "300px"
}}>
<CardContent className={cName}>
<Typography variant="title">{`Level ${lesson.level}`}</Typography>
<Typography variant="title" component="p">{lesson.name}</Typography>
<br />
<Typography component="p">
{lesson.desc}
</Typography>
</CardContent>
<CardActions>
<Button className="lesson-card-btn">
Zum Level
</Button>
</CardActions>
</Card>
</Grid>;
};
return <Grid container spacing={16} direction="row">
{this.props.lessons.map(lessonToCard)}
</Grid>
}
};

52
src/pages/lessons.tsx Normal file
View File

@ -0,0 +1,52 @@
import * as React from "react";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import { ILesson } from "../models/lesson";
interface IProps {
lessons: ILesson[];
}
export default class Dashboard extends React.Component<{}> {
constructor(props: any) {
super(props);
}
render() {
const small = window.matchMedia("(max-width: 700px)").matches;
const cName = small ? "lesson-card-xs" : "lesson-card-lg";
let key = 0;
const lessonToCard = (lesson: ILesson) => {
return <Grid item key={key++}>
<Card style={{
width: small ? window.width - 32 : "300px"
}}>
<CardContent className={cName}>
<Typography variant="title">{`Level ${lesson.level}`}</Typography>
<Typography variant="title" component="p">{lesson.name}</Typography>
<br />
<Typography component="p">
{lesson.desc}
</Typography>
</CardContent>
<CardActions>
<Button className="lesson-card-btn">
Zum Level
</Button>
</CardActions>
</Card>
</Grid>;
};
return <Grid container spacing={16} direction="row">
{this.props.lessons.map(lessonToCard)}
</Grid>
}
};

View File

@ -8,8 +8,11 @@ import Button from "@material-ui/core/Button";
import LinearProgress from "@material-ui/core/LinearProgress"; import LinearProgress from "@material-ui/core/LinearProgress";
import Snackbar from "@material-ui/core/Snackbar"; import Snackbar from "@material-ui/core/Snackbar";
import { Redirect } from "react-router-dom";
interface IProps { interface IProps {
login: (username: string, password: string) => Promise<boolean>; login: (username: string, password: string) => Promise<boolean>;
loggedIn: boolean;
} }
interface IState { interface IState {
@ -63,7 +66,7 @@ export default class LoginPage extends React.Component<{}, IState> {
const { username, password } = this.state; const { username, password } = this.state;
this.props.login(username, password).then((res) => { this.props.login(username, password).then((res) => {
load(false); // Don't even do anything here!
}, (err) => { }, (err) => {
load(false); load(false);
showSnackbar("Failed to log in"); showSnackbar("Failed to log in");
@ -121,6 +124,11 @@ export default class LoginPage extends React.Component<{}, IState> {
onClose={snackbarClose} onClose={snackbarClose}
message={this.state.snack} message={this.state.snack}
autoHideDuration={6000} /> autoHideDuration={6000} />
{
this.props.loggedIn ? (
<Redirect to="/dashboard" />
) : undefined
}
</div>; </div>;
} }
}; };

View File

@ -0,0 +1,18 @@
import * as React from "react";
import { Route, Redirect } from "react-router-dom";
interface IAuthRouteProps {
path: string;
component: any;
isAuth: () => boolean;
}
export default class AuthRoute extends React.Component<IAuthRouteProps, {}> {
render() {
const auth = this.props.isAuth();
return <Route path={this.props.path} component={
() => auth ? <this.props.component /> : <Redirect to="/login" />
} />;
}
};