feat: Implement routing
This commit is contained in:
parent
297d2e50f9
commit
6734e59a1b
@ -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
7
src/models/lesson.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface ILesson {
|
||||||
|
name: string;
|
||||||
|
desc: string;
|
||||||
|
level: number;
|
||||||
|
|
||||||
|
done: boolean;
|
||||||
|
}
|
52
src/pages/dashboard.tsx
Normal file
52
src/pages/dashboard.tsx
Normal 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
52
src/pages/lessons.tsx
Normal 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>
|
||||||
|
}
|
||||||
|
};
|
@ -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>;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
18
src/security/AuthRoute.tsx
Normal file
18
src/security/AuthRoute.tsx
Normal 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" />
|
||||||
|
} />;
|
||||||
|
}
|
||||||
|
};
|
Reference in New Issue
Block a user