From 24f35be0585c990ca1c6e8fcc57973bcc3a0a4fd Mon Sep 17 00:00:00 2001 From: Alexander Polynomdivision Date: Mon, 24 Sep 2018 18:29:29 +0200 Subject: [PATCH] feat: Implement the new /dashboard API --- backend/src/api/user.ts | 29 +++++- frontend/src/actions/index.ts | 32 ++---- frontend/src/components/app.tsx | 25 ++++- frontend/src/containers/Dashboard.ts | 11 +-- frontend/src/pages/dashboard.tsx | 140 ++++++++++++--------------- frontend/src/pages/levelList.tsx | 1 - frontend/src/reducers/index.ts | 32 ++---- 7 files changed, 127 insertions(+), 143 deletions(-) diff --git a/backend/src/api/user.ts b/backend/src/api/user.ts index 2219ca1..3abb3e8 100644 --- a/backend/src/api/user.ts +++ b/backend/src/api/user.ts @@ -114,16 +114,35 @@ userRouter.post("/level/:id", async (req, res) => { }); // 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"); + 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 res.send({ - error: "0", + error: "200", data: { - nextLevel: {}, - topTen: {}, - lastReview: {}, + nextLevel: { + name: "Test level", + desc: "Just a test", + level: 3, + + done: false, + }, + topTen: users, + lastReview: { + correct: 0, + wrong: 0, + }, }, }); }); diff --git a/frontend/src/actions/index.ts b/frontend/src/actions/index.ts index 81fecbb..c2e5f84 100644 --- a/frontend/src/actions/index.ts +++ b/frontend/src/actions/index.ts @@ -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 function setTopTen(topTen: TopTen[]) { 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 function setDidLogin(state: boolean) { 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 function setReviewDialog(state: boolean) { return { @@ -200,3 +176,11 @@ export function setReviewDialog(state: boolean) { state, }; }; + +export const DASHBOARD_SET_LOADING = "DASHBOARD_SET_LOADING"; +export function setDashboardLoading(state: boolean) { + return { + type: DASHBOARD_SET_LOADING, + state, + }; +}; diff --git a/frontend/src/components/app.tsx b/frontend/src/components/app.tsx index 3ea6277..e21f999 100644 --- a/frontend/src/components/app.tsx +++ b/frontend/src/components/app.tsx @@ -173,6 +173,27 @@ export default class Application extends React.Component { }); } + // TODO: Type? + getDashboard = (): Promise => { + 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 => { return new Promise((res, rej) => { fetch(`${BACKEND_URL}/api/login`, { @@ -233,9 +254,7 @@ export default class Application extends React.Component { path="/dashboard" component={() => { return + getDashboard={this.getDashboard} /> }} /> { return { + loading: state.review.loading, nextLevel: state.nextLevel, - loadingNextLevel: state.dashboard.loadingNL, - loadingTopTen: state.dashboard.loadingTT, topTen: state.topTen, - loadingLastReview: state.dashboard.loadingLR, lastReview: state.lastReview, }; }; const mapDispatchToProps = dispatch => { return { - setLoadingNL: (state: boolean) => dispatch(setDashboardNLLoading(state)), + setLoading: (state: boolean) => dispatch(setDashboardLoading(state)), setNextLevel: (level: ILevel) => dispatch(setNextLevel(level)), setTopTen: (topTen: ILearner[]) => dispatch(setTopTen(topTen)), - setLoadingTT: (state: boolean) => dispatch(setDashboardTTLoading(state)), setLastReview: (review: IReviewMetadata) => dispatch(setLastReview(review)), - setLoadingLR: (state: boolean) => dispatch(setDashboardLRLoading(state)), } }; diff --git a/frontend/src/pages/dashboard.tsx b/frontend/src/pages/dashboard.tsx index 608a828..27613b0 100644 --- a/frontend/src/pages/dashboard.tsx +++ b/frontend/src/pages/dashboard.tsx @@ -16,51 +16,50 @@ import { ILearner, TopTen } from "../models/learner"; import { IReviewMetadata } from "../models/review"; interface IProps { - getNextLevel: () => Promise; - getLastReview: () => Promise; - getTopTen: () => Promise; + getDashboard: () => Promise; + loading: boolean; + setLoading: (state: boolean) => void; nextLevel: ILevel; - loadingNextLevel: boolean; - setLoadingNL: (state: boolean) => void; setNextLevel: (level: ILevel) => void; topTen: TopTen[]; - loadingTopTen: boolean; - setLoadingTT: (state: boolean) => void; setTopTen: (topten: TopTen[]) => void; lastReview: IReviewMetadata; - loadingLastReview: boolean; - setLoadingLR: (state: boolean) => void; setLastReview: (review: IReviewMetadata) => void; } export default class Dashboard extends React.Component { componentDidMount() { - this.props.setLoadingNL(true); - - this.props.getNextLevel().then(res => { - this.props.setLoadingNL(false); - this.props.setNextLevel(res); - }, err => { - 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"); - }); + this.props.setLoading(true); + this.props.getDashboard().then(res => { + this.props.setNextLevel(res.nextLevel); + this.props.setTopTen(res.topTen); + this.props.setLastReview(res.lastReview); + this.props.setLoading(false); + }) } render() { + if (this.props.loading) { + return
+ + + + + + + + + +
; + } + const small = window.matchMedia("(max-width: 700px)").matches; const direction = small ? "column" : "row"; @@ -70,63 +69,48 @@ export default class Dashboard extends React.Component { - {this.props.loadingNextLevel ? ( - - ) : ( -
- {`Level ${level.level}`} - {level.name} -
- - {level.desc} - - -
- - )} +
+ {`Level ${level.level}`} + {level.name} +
+ + {level.desc} + + +
- {this.props.loadingTopTen ? ( - - ) : ( -
- - Rangliste: Top 10 - +
+ + Rangliste: Top 10 + - -
- )} + +
- { - this.props.loadingLastReview ? ( - - ) : ( -
- - Letzte Wiederholung - - +
+ + Letzte Wiederholung + + - -
- ) - } + +
diff --git a/frontend/src/pages/levelList.tsx b/frontend/src/pages/levelList.tsx index 4363240..84c135d 100644 --- a/frontend/src/pages/levelList.tsx +++ b/frontend/src/pages/levelList.tsx @@ -51,7 +51,6 @@ export default class Dashboard extends React.Component { - ; } diff --git a/frontend/src/reducers/index.ts b/frontend/src/reducers/index.ts index a448c71..de210a5 100644 --- a/frontend/src/reducers/index.ts +++ b/frontend/src/reducers/index.ts @@ -37,9 +37,7 @@ interface IState { }; dashboard: { - loadingNL: boolean; - loadingTT: boolean; - loadingLR: boolean; + loading: boolean; }; review: { @@ -97,9 +95,7 @@ const initialState: IState = { }, dashboard: { - loadingNL: true, - loadingTT: true, - loadingLR: true, + loading: true, }, review: { @@ -227,38 +223,26 @@ export function LateinicusApp(state: IState = initialState, action: any) { return Object.assign({}, state, { 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: return Object.assign({}, state, { 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: return Object.assign({}, 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: return Object.assign({}, state, { review: Object.assign({}, state.review, { dialogOpen: action.state, }), }); + case Actions.DASHBOARD_SET_LOADING: + return Object.assign({}, state, { + review: Object.assign({}, state.review, { + loading: action.state, + }), + }); default: // Ignore the initialization call to the reducer. By that we can // catch all actions that are not implemented