feat: Implement the new /dashboard API
This commit is contained in:
parent
7339e1ccac
commit
24f35be058
@ -114,16 +114,35 @@ userRouter.post("/level/:id", async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Get the data needed for the dashboard
|
// Get the data needed for the dashboard
|
||||||
userRouter.post("/dashboard", async (req, res) => {
|
userRouter.get("/dashboard", async (req, res) => {
|
||||||
console.log("STUB(post): /user/dashboard");
|
console.log("STUB(post): /user/dashboard");
|
||||||
|
|
||||||
|
let users: any[] = [];
|
||||||
|
let nr = 10;
|
||||||
|
for (let i = 0; i < 10; i++)
|
||||||
|
users = users.concat({
|
||||||
|
username: `Test User ${i}`,
|
||||||
|
score: 100 * i,
|
||||||
|
level: Math.floor(Math.random() * Math.floor(10)),
|
||||||
|
nr: nr--,
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: Stub
|
// TODO: Stub
|
||||||
res.send({
|
res.send({
|
||||||
error: "0",
|
error: "200",
|
||||||
data: {
|
data: {
|
||||||
nextLevel: {},
|
nextLevel: {
|
||||||
topTen: {},
|
name: "Test level",
|
||||||
lastReview: {},
|
desc: "Just a test",
|
||||||
|
level: 3,
|
||||||
|
|
||||||
|
done: false,
|
||||||
|
},
|
||||||
|
topTen: users,
|
||||||
|
lastReview: {
|
||||||
|
correct: 0,
|
||||||
|
wrong: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -153,14 +153,6 @@ export function setNextLevel(level: ILevel) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DASHBOARD_SET_NL_LOADING = "DASHBOARD_SET_NL_LOADING";
|
|
||||||
export function setDashboardNLLoading(state: boolean) {
|
|
||||||
return {
|
|
||||||
type: DASHBOARD_SET_NL_LOADING,
|
|
||||||
state,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SET_TOP_TEN = "SET_TOP_TEN";
|
export const SET_TOP_TEN = "SET_TOP_TEN";
|
||||||
export function setTopTen(topTen: TopTen[]) {
|
export function setTopTen(topTen: TopTen[]) {
|
||||||
return {
|
return {
|
||||||
@ -169,14 +161,6 @@ export function setTopTen(topTen: TopTen[]) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DASHBOARD_SET_TT_LOADING = "DASHBOARD_SET_TT_LOADING";
|
|
||||||
export function setDashboardTTLoading(state: boolean) {
|
|
||||||
return {
|
|
||||||
type: DASHBOARD_SET_TT_LOADING,
|
|
||||||
state,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SET_DID_LOGIN = "SET_DID_LOGIN";
|
export const SET_DID_LOGIN = "SET_DID_LOGIN";
|
||||||
export function setDidLogin(state: boolean) {
|
export function setDidLogin(state: boolean) {
|
||||||
return {
|
return {
|
||||||
@ -185,14 +169,6 @@ export function setDidLogin(state: boolean) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DASHBOARD_SET_LR_LOADING = "DASHBOARD_SET_LR_LOADING";
|
|
||||||
export function setDashboardLRLoading(state: boolean) {
|
|
||||||
return {
|
|
||||||
type: DASHBOARD_SET_LR_LOADING,
|
|
||||||
state,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const REVIEW_SET_DIALOG = "REVIEW_SET_DIALOG";
|
export const REVIEW_SET_DIALOG = "REVIEW_SET_DIALOG";
|
||||||
export function setReviewDialog(state: boolean) {
|
export function setReviewDialog(state: boolean) {
|
||||||
return {
|
return {
|
||||||
@ -200,3 +176,11 @@ export function setReviewDialog(state: boolean) {
|
|||||||
state,
|
state,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DASHBOARD_SET_LOADING = "DASHBOARD_SET_LOADING";
|
||||||
|
export function setDashboardLoading(state: boolean) {
|
||||||
|
return {
|
||||||
|
type: DASHBOARD_SET_LOADING,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -173,6 +173,27 @@ export default class Application extends React.Component<IProps> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Type?
|
||||||
|
getDashboard = (): Promise<any> => {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
fetch(`${BACKEND_URL}/api/user/dashboard`, {
|
||||||
|
headers: new Headers({
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Token": this.props.user.sessionToken,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then(resp => resp.json(), err => rej(err))
|
||||||
|
.then(data => {
|
||||||
|
if (data.error === "200") {
|
||||||
|
res(data.data);
|
||||||
|
} else {
|
||||||
|
console.log("Application::getDashboard: Failed to get dashboard");
|
||||||
|
rej(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
login = (username: string, password: string): Promise<IUser | IResponse> => {
|
login = (username: string, password: string): Promise<IUser | IResponse> => {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
fetch(`${BACKEND_URL}/api/login`, {
|
fetch(`${BACKEND_URL}/api/login`, {
|
||||||
@ -233,9 +254,7 @@ export default class Application extends React.Component<IProps> {
|
|||||||
path="/dashboard"
|
path="/dashboard"
|
||||||
component={() => {
|
component={() => {
|
||||||
return <Dashboard
|
return <Dashboard
|
||||||
getNextLevel={this.getNextLevel}
|
getDashboard={this.getDashboard} />
|
||||||
getLastReview={this.getLastReview}
|
|
||||||
getTopTen={this.getTopTenLearners} />
|
|
||||||
}} />
|
}} />
|
||||||
<AuthRoute
|
<AuthRoute
|
||||||
isAuth={this.isAuthenticated}
|
isAuth={this.isAuthenticated}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
setNextLevel, setDashboardNLLoading, setTopTen, setDashboardTTLoading,
|
setNextLevel, setTopTen, setLastReview, setDashboardLoading
|
||||||
setDashboardLRLoading, setLastReview,
|
|
||||||
} from "../actions";
|
} from "../actions";
|
||||||
|
|
||||||
import { ILevel } from "../models/level";
|
import { ILevel } from "../models/level";
|
||||||
@ -13,22 +12,18 @@ import DashboardPage from "../pages/dashboard";
|
|||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
|
loading: state.review.loading,
|
||||||
nextLevel: state.nextLevel,
|
nextLevel: state.nextLevel,
|
||||||
loadingNextLevel: state.dashboard.loadingNL,
|
|
||||||
loadingTopTen: state.dashboard.loadingTT,
|
|
||||||
topTen: state.topTen,
|
topTen: state.topTen,
|
||||||
loadingLastReview: state.dashboard.loadingLR,
|
|
||||||
lastReview: state.lastReview,
|
lastReview: state.lastReview,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
setLoadingNL: (state: boolean) => dispatch(setDashboardNLLoading(state)),
|
setLoading: (state: boolean) => dispatch(setDashboardLoading(state)),
|
||||||
setNextLevel: (level: ILevel) => dispatch(setNextLevel(level)),
|
setNextLevel: (level: ILevel) => dispatch(setNextLevel(level)),
|
||||||
setTopTen: (topTen: ILearner[]) => dispatch(setTopTen(topTen)),
|
setTopTen: (topTen: ILearner[]) => dispatch(setTopTen(topTen)),
|
||||||
setLoadingTT: (state: boolean) => dispatch(setDashboardTTLoading(state)),
|
|
||||||
setLastReview: (review: IReviewMetadata) => dispatch(setLastReview(review)),
|
setLastReview: (review: IReviewMetadata) => dispatch(setLastReview(review)),
|
||||||
setLoadingLR: (state: boolean) => dispatch(setDashboardLRLoading(state)),
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,51 +16,50 @@ import { ILearner, TopTen } from "../models/learner";
|
|||||||
import { IReviewMetadata } from "../models/review";
|
import { IReviewMetadata } from "../models/review";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
getNextLevel: () => Promise<ILevel>;
|
getDashboard: () => Promise<any>;
|
||||||
getLastReview: () => Promise<IReviewMetadata>;
|
|
||||||
getTopTen: () => Promise<ILearner[]>;
|
|
||||||
|
|
||||||
|
loading: boolean;
|
||||||
|
setLoading: (state: boolean) => void;
|
||||||
nextLevel: ILevel;
|
nextLevel: ILevel;
|
||||||
loadingNextLevel: boolean;
|
|
||||||
setLoadingNL: (state: boolean) => void;
|
|
||||||
setNextLevel: (level: ILevel) => void;
|
setNextLevel: (level: ILevel) => void;
|
||||||
topTen: TopTen[];
|
topTen: TopTen[];
|
||||||
loadingTopTen: boolean;
|
|
||||||
setLoadingTT: (state: boolean) => void;
|
|
||||||
setTopTen: (topten: TopTen[]) => void;
|
setTopTen: (topten: TopTen[]) => void;
|
||||||
lastReview: IReviewMetadata;
|
lastReview: IReviewMetadata;
|
||||||
loadingLastReview: boolean;
|
|
||||||
setLoadingLR: (state: boolean) => void;
|
|
||||||
setLastReview: (review: IReviewMetadata) => void;
|
setLastReview: (review: IReviewMetadata) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Dashboard extends React.Component<IProps> {
|
export default class Dashboard extends React.Component<IProps> {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.setLoadingNL(true);
|
this.props.setLoading(true);
|
||||||
|
this.props.getDashboard().then(res => {
|
||||||
this.props.getNextLevel().then(res => {
|
this.props.setNextLevel(res.nextLevel);
|
||||||
this.props.setLoadingNL(false);
|
this.props.setTopTen(res.topTen);
|
||||||
this.props.setNextLevel(res);
|
this.props.setLastReview(res.lastReview);
|
||||||
}, err => {
|
this.props.setLoading(false);
|
||||||
console.log("Failed to fetch next level!", err);
|
})
|
||||||
});
|
|
||||||
|
|
||||||
this.props.getTopTen().then(res => {
|
|
||||||
this.props.setLoadingTT(false);
|
|
||||||
this.props.setTopTen(res);
|
|
||||||
}, err => {
|
|
||||||
console.log("Failed to fetch Top Ten");
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.getLastReview().then(res => {
|
|
||||||
this.props.setLoadingLR(false);
|
|
||||||
this.props.setLastReview(res);
|
|
||||||
}, err => {
|
|
||||||
console.log("Failed to fetch Last Review");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (this.props.loading) {
|
||||||
|
return <div>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={0}
|
||||||
|
direction="column"
|
||||||
|
alignItems="center"
|
||||||
|
justify="center"
|
||||||
|
style={{ minHeight: '100vh' }}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Paper className="paper">
|
||||||
|
<Grid container direction="column" spacing={8}>
|
||||||
|
<CircularProgress />
|
||||||
|
</Grid>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
const small = window.matchMedia("(max-width: 700px)").matches;
|
const small = window.matchMedia("(max-width: 700px)").matches;
|
||||||
const direction = small ? "column" : "row";
|
const direction = small ? "column" : "row";
|
||||||
|
|
||||||
@ -70,63 +69,48 @@ export default class Dashboard extends React.Component<IProps> {
|
|||||||
<Grid container direction={direction} spacing={16}>
|
<Grid container direction={direction} spacing={16}>
|
||||||
<Grid item lg={4}>
|
<Grid item lg={4}>
|
||||||
<Paper className="paper">
|
<Paper className="paper">
|
||||||
{this.props.loadingNextLevel ? (
|
<div>
|
||||||
<CircularProgress />
|
<Typography variant="title">{`Level ${level.level}`}</Typography>
|
||||||
) : (
|
<Typography variant="title" component="p">{level.name}</Typography>
|
||||||
<div>
|
<br />
|
||||||
<Typography variant="title">{`Level ${level.level}`}</Typography>
|
<Typography component="p">
|
||||||
<Typography variant="title" component="p">{level.name}</Typography>
|
{level.desc}
|
||||||
<br />
|
</Typography>
|
||||||
<Typography component="p">
|
<Button
|
||||||
{level.desc}
|
component={Link}
|
||||||
</Typography>
|
to={`/level/${level.level}`}
|
||||||
<Button
|
className="lesson-card-btn">
|
||||||
component={Link}
|
Zum Level
|
||||||
to={`/level/${level.level}`}
|
</Button>
|
||||||
className="lesson-card-btn">
|
</div>
|
||||||
Zum Level
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
)}
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item lg={4}>
|
<Grid item lg={4}>
|
||||||
<Paper className="paper">
|
<Paper className="paper">
|
||||||
{this.props.loadingTopTen ? (
|
<div>
|
||||||
<CircularProgress />
|
<Typography variant="title" component="p">
|
||||||
) : (
|
Rangliste: Top 10
|
||||||
<div>
|
</Typography>
|
||||||
<Typography variant="title" component="p">
|
|
||||||
Rangliste: Top 10
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Scoreboard topTen={this.props.topTen} />
|
<Scoreboard topTen={this.props.topTen} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item lg={4}>
|
<Grid item lg={4}>
|
||||||
<Paper className="paper">
|
<Paper className="paper">
|
||||||
{
|
<div>
|
||||||
this.props.loadingLastReview ? (
|
<Typography variant="title">
|
||||||
<CircularProgress />
|
Letzte Wiederholung
|
||||||
) : (
|
</Typography>
|
||||||
<div>
|
<SummaryTable reviewMeta={this.props.lastReview} />
|
||||||
<Typography variant="title">
|
|
||||||
Letzte Wiederholung
|
|
||||||
</Typography>
|
|
||||||
<SummaryTable reviewMeta={this.props.lastReview} />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
component={Link}
|
component={Link}
|
||||||
to="/review/queue"
|
to="/review/queue"
|
||||||
className="lesson-card-btn">
|
className="lesson-card-btn">
|
||||||
Vokabeln üben
|
Vokabeln üben
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
|
||||||
}
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -51,7 +51,6 @@ export default class Dashboard extends React.Component<IProps> {
|
|||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,9 +37,7 @@ interface IState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
dashboard: {
|
dashboard: {
|
||||||
loadingNL: boolean;
|
loading: boolean;
|
||||||
loadingTT: boolean;
|
|
||||||
loadingLR: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
review: {
|
review: {
|
||||||
@ -97,9 +95,7 @@ const initialState: IState = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
dashboard: {
|
dashboard: {
|
||||||
loadingNL: true,
|
loading: true,
|
||||||
loadingTT: true,
|
|
||||||
loadingLR: true,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
review: {
|
review: {
|
||||||
@ -227,38 +223,26 @@ export function LateinicusApp(state: IState = initialState, action: any) {
|
|||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
nextLevel: action.level,
|
nextLevel: action.level,
|
||||||
});
|
});
|
||||||
case Actions.DASHBOARD_SET_NL_LOADING:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
dashboard: Object.assign({}, state.dashboard, {
|
|
||||||
loadingNL: action.state,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
case Actions.SET_TOP_TEN:
|
case Actions.SET_TOP_TEN:
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
topTen: action.topTen,
|
topTen: action.topTen,
|
||||||
});
|
});
|
||||||
case Actions.DASHBOARD_SET_TT_LOADING:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
dashboard: Object.assign({}, state.dashboard, {
|
|
||||||
loadingTT: action.state,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
case Actions.SET_DID_LOGIN:
|
case Actions.SET_DID_LOGIN:
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
didLogin: state,
|
didLogin: state,
|
||||||
});
|
});
|
||||||
case Actions.DASHBOARD_SET_LR_LOADING:
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
dashboard: Object.assign({}, state.dashboard, {
|
|
||||||
loadingLR: action.state,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
case Actions.REVIEW_SET_DIALOG:
|
case Actions.REVIEW_SET_DIALOG:
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
review: Object.assign({}, state.review, {
|
review: Object.assign({}, state.review, {
|
||||||
dialogOpen: action.state,
|
dialogOpen: action.state,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
case Actions.DASHBOARD_SET_LOADING:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
review: Object.assign({}, state.review, {
|
||||||
|
loading: action.state,
|
||||||
|
}),
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
// Ignore the initialization call to the reducer. By that we can
|
// Ignore the initialization call to the reducer. By that we can
|
||||||
// catch all actions that are not implemented
|
// catch all actions that are not implemented
|
||||||
|
Reference in New Issue
Block a user