feat: Implement a vocabulary view
This commit is contained in:
parent
528c291b70
commit
a902d9e8f7
@ -249,3 +249,20 @@ export function setRegisterLoading(state: boolean) {
|
|||||||
state,
|
state,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const VOCAB_SET_LOADING = "VOCAB_SET_LOADING";
|
||||||
|
export function setVocabLoading(state: boolean) {
|
||||||
|
return {
|
||||||
|
type: VOCAB_SET_LOADING,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const VOCAB_SET_VOCAB = "VOCAB_SET_VOCAB";
|
||||||
|
export function setVocabVocab(vocab: IVocab[]) {
|
||||||
|
return {
|
||||||
|
type: VOCAB_SET_VOCAB,
|
||||||
|
vocab,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
@ -172,6 +172,16 @@ export default class Drawer extends React.Component<IProps> {
|
|||||||
Vokabeln üben
|
Vokabeln üben
|
||||||
</ListItemText>
|
</ListItemText>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
component={Link}
|
||||||
|
to="/vocab"
|
||||||
|
onClick={this.closeDrawer}
|
||||||
|
button>
|
||||||
|
{/* <ListItemIcon>
|
||||||
|
<HomeIcon />
|
||||||
|
</ListItemIcon> */}
|
||||||
|
<ListItemText primary="Vokabelliste" />
|
||||||
|
</ListItem>
|
||||||
<ListItem
|
<ListItem
|
||||||
component={Link}
|
component={Link}
|
||||||
to="/levelList"
|
to="/levelList"
|
||||||
|
@ -13,6 +13,7 @@ import ReviewPage from "../containers/Review";
|
|||||||
import SummaryPage from "../containers/SummaryPage";
|
import SummaryPage from "../containers/SummaryPage";
|
||||||
import WelcomePage from "../pages/intro";
|
import WelcomePage from "../pages/intro";
|
||||||
import RegisterPage from "../containers/Register";
|
import RegisterPage from "../containers/Register";
|
||||||
|
import VocabPage from "../containers/VocabPage";
|
||||||
|
|
||||||
import Drawer from "../containers/Drawer";
|
import Drawer from "../containers/Drawer";
|
||||||
|
|
||||||
@ -83,6 +84,24 @@ export default class Application extends React.Component<IProps> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getVocab = (): Promise<IVocab[]> => {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
fetch(`${BACKEND_URL}/api/user/vocab`, {
|
||||||
|
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 {
|
||||||
|
rej(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getLevels = (): Promise<ILevel[]> => {
|
getLevels = (): Promise<ILevel[]> => {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
fetch(`${BACKEND_URL}/api/levels`, {
|
fetch(`${BACKEND_URL}/api/levels`, {
|
||||||
@ -431,6 +450,13 @@ export default class Application extends React.Component<IProps> {
|
|||||||
component={() => {
|
component={() => {
|
||||||
return <SummaryPage />
|
return <SummaryPage />
|
||||||
}} />
|
}} />
|
||||||
|
<AuthRoute
|
||||||
|
isAuth={this.isAuthenticated}
|
||||||
|
path="/vocab"
|
||||||
|
component={() => {
|
||||||
|
return <VocabPage
|
||||||
|
getVocab={this.getVocab} />;
|
||||||
|
}} />
|
||||||
</div>
|
</div>
|
||||||
</div >
|
</div >
|
||||||
</BrowserRouter >;
|
</BrowserRouter >;
|
||||||
|
24
frontend/src/containers/VocabPage.ts
Normal file
24
frontend/src/containers/VocabPage.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
|
import { setVocabLoading, setVocabVocab } from "../actions";
|
||||||
|
|
||||||
|
import { IVocab } from "../models/vocab";
|
||||||
|
|
||||||
|
import VocabPage from "../pages/vocab";
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
loading: state.vocab.loading,
|
||||||
|
vocab: state.vocab.vocab,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
setLoading: (state: boolean) => dispatch(setVocabLoading(state)),
|
||||||
|
setVocab: (vocab: IVocab[]) => dispatch(setVocabVocab(vocab)),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const VocabPageContainer = connect(mapStateToProps,
|
||||||
|
mapDispatchToProps)(VocabPage);
|
||||||
|
export default VocabPageContainer;
|
@ -8,7 +8,6 @@ import CardActions from '@material-ui/core/CardActions';
|
|||||||
import CardContent from '@material-ui/core/CardContent';
|
import CardContent from '@material-ui/core/CardContent';
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
import Snackbar from "@material-ui/core/Snackbar";
|
import Snackbar from "@material-ui/core/Snackbar";
|
||||||
|
|
||||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
|
|
||||||
import { withRouter } from "react-router-dom";
|
import { withRouter } from "react-router-dom";
|
||||||
|
79
frontend/src/pages/vocab.tsx
Normal file
79
frontend/src/pages/vocab.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
import Paper from "@material-ui/core/Paper";
|
||||||
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
|
|
||||||
|
import { IVocab, VocabType } from "../models/vocab";
|
||||||
|
|
||||||
|
import VocabularyData from "../components/VocabularyData";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
getVocab: () => Promise<IVocab[]>;
|
||||||
|
|
||||||
|
loading: boolean;
|
||||||
|
vocab: IVocab[];
|
||||||
|
|
||||||
|
setLoading: (state: boolean) => void;
|
||||||
|
setVocab: (vocab: IVocab[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class VocabPage extends React.Component<IProps> {
|
||||||
|
private uid = 0;
|
||||||
|
genUID = () => {
|
||||||
|
return `VOCABPAGE-${this.uid++}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.setLoading(true);
|
||||||
|
// TODO: Errorhandling
|
||||||
|
this.props.getVocab().then(vocab => {
|
||||||
|
this.props.setVocab(vocab);
|
||||||
|
this.props.setLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
vocabToCard = (voc: IVocab) => {
|
||||||
|
const vocabTypeToStr = {
|
||||||
|
[VocabType.NOMEN]: "Nomen",
|
||||||
|
[VocabType.VERB]: "Verb",
|
||||||
|
[VocabType.ADJEKTIV]: "Adjektiv",
|
||||||
|
[VocabType.ADVERB]: "Adverb",
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Paper key={this.genUID()} style={{ marginBottom: 12, padding: 12 }}>
|
||||||
|
<Typography gutterBottom variant="headline" component="h2">
|
||||||
|
{`${voc.latin.grundform} (${vocabTypeToStr[voc.type]})`}
|
||||||
|
</Typography>
|
||||||
|
<VocabularyData vocab={voc} />
|
||||||
|
</Paper>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 { vocab } = this.props;
|
||||||
|
return <div className="content">
|
||||||
|
{vocab.map(this.vocabToCard)}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
};
|
@ -58,6 +58,11 @@ interface IState {
|
|||||||
showHelp: boolean;
|
showHelp: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vocab: {
|
||||||
|
loading: boolean;
|
||||||
|
vocab: IVocab[];
|
||||||
|
};
|
||||||
|
|
||||||
register: {
|
register: {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
snackMsg: string;
|
snackMsg: string;
|
||||||
@ -126,6 +131,11 @@ const initialState: IState = {
|
|||||||
showHelp: false,
|
showHelp: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
vocab: {
|
||||||
|
loading: true,
|
||||||
|
vocab: [],
|
||||||
|
},
|
||||||
|
|
||||||
register: {
|
register: {
|
||||||
loading: false,
|
loading: false,
|
||||||
snackOpen: false,
|
snackOpen: false,
|
||||||
@ -313,6 +323,18 @@ export function LateinicusApp(state: IState = initialState, action: any) {
|
|||||||
leaveDialog: action.state,
|
leaveDialog: action.state,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
case Actions.VOCAB_SET_LOADING:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
vocab: Object.assign({}, state.vocab, {
|
||||||
|
loading: action.state,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
case Actions.VOCAB_SET_VOCAB:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
vocab: Object.assign({}, state.vocab, {
|
||||||
|
vocab: action.vocab,
|
||||||
|
}),
|
||||||
|
});
|
||||||
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