Add tracking of previous mistakes

This commit is contained in:
2021-10-08 18:56:19 +01:00
parent e6bd026f72
commit b532e2deb1
6 changed files with 292 additions and 13 deletions

View File

@@ -14,6 +14,7 @@ import UserSets from "./UserSets";
import EditSet from "./EditSet";
import Error404 from "./Error404";
import History from "./History";
import MistakesHistory from "./MistakesHistory";
import TermsOfService from "./TermsOfService";
import PrivacyPolicy from "./PrivacyPolicy";
import Button from "./Button";
@@ -320,6 +321,9 @@ class App extends React.Component {
<Route path="/history" exact>
<History db={db} user={this.state.user} logEvent={analytics.logEvent} page={this.page} />
</Route>
<Route path="/history/mistakes" exact>
<MistakesHistory db={db} user={this.state.user} logEvent={analytics.logEvent} page={this.page} />
</Route>
<Route path="/tos" exact>
<TermsOfService logEvent={analytics.logEvent} page={this.page} />
</Route>

View File

@@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { HomeRounded as HomeRoundedIcon, QuestionAnswerRounded as QuestionAnswerRoundedIcon, PeopleRounded as PeopleRoundedIcon, SwapHorizRounded as SwapHorizRoundedIcon, DeleteRounded as DeleteRoundedIcon } from "@material-ui/icons";
import { TimelineRounded as TimelineRoundedIcon, HomeRounded as HomeRoundedIcon, QuestionAnswerRounded as QuestionAnswerRoundedIcon, PeopleRounded as PeopleRoundedIcon, SwapHorizRounded as SwapHorizRoundedIcon, DeleteRounded as DeleteRoundedIcon } from "@material-ui/icons";
import NavBar from "./NavBar";
import Button from "./Button";
import Footer from "./Footer";
@@ -15,6 +15,13 @@ export default class History extends Component {
user: props.user,
db: props.db,
navbarItems: [
{
type: "link",
name: "Mistakes",
link: "/history/mistakes",
icon: <TimelineRoundedIcon />,
hideTextMobile: true,
},
{
type: "link",
link: "/",

209
src/MistakesHistory.js Normal file
View File

@@ -0,0 +1,209 @@
import React, { Component } from 'react';
import { HistoryRounded as HistoryRoundedIcon, HomeRounded as HomeRoundedIcon, QuestionAnswerRounded as QuestionAnswerRoundedIcon, PeopleRounded as PeopleRoundedIcon, SwapHorizRounded as SwapHorizRoundedIcon, DeleteRounded as DeleteRoundedIcon } from "@material-ui/icons";
import NavBar from "./NavBar";
import Footer from "./Footer";
import "./css/History.css";
import "./css/MistakesHistory.css";
export default class IncorrectHistory extends Component {
constructor(props) {
super(props);
this.state = {
user: props.user,
db: props.db,
navbarItems: [
{
type: "link",
name: "History",
link: "/history",
icon: <HistoryRoundedIcon />,
hideTextMobile: true,
},
{
type: "link",
link: "/",
icon: <HomeRoundedIcon />,
hideTextMobile: true,
}
],
incorrectAnswers: [],
totalIncorrect: 0,
totalTests: 0,
};
let isMounted = true;
Object.defineProperty(this, "isMounted", {
get: () => isMounted,
set: (value) => isMounted = value,
});
}
setState = (state, callback = null) => {
if (this.isMounted) super.setState(state, callback);
}
async componentDidMount() {
document.title = "Incorrect | History | Parandum";
let promises = [];
let newState = {};
promises.push(
this.state.db.collection("incorrect_answers")
.where("uid", "==", this.state.user.uid)
.orderBy("term", "asc")
.get()
.then((querySnapshot) => {
let incorrectAnswers = [];
querySnapshot.docs.map((doc, index, array) => {
if (index === 0 || doc.data().term !== array[index - 1].data().term || doc.data().definition !== array[index - 1].data().definition) {
incorrectAnswers.push({
term: doc.data().term,
definition: doc.data().definition,
answers: [{
answer: doc.data().answer,
switchLanguage: doc.data().switch_language,
}],
count: doc.data().switch_language ? 0 : 1,
switchedCount: doc.data().switch_language ? 1 : 0,
});
} else {
incorrectAnswers[incorrectAnswers.length - 1].answers.push({
answer: doc.data().answer,
switchLanguage: doc.data().switch_language,
});
if (doc.data().switch_language) {
incorrectAnswers[incorrectAnswers.length - 1].switchedCount++;
} else {
incorrectAnswers[incorrectAnswers.length - 1].count++;
}
}
return true;
});
newState.incorrectAnswers = incorrectAnswers.sort((a,b) => b.count + b.switchedCount - a.count - a.switchedCount);
newState.totalIncorrect = querySnapshot.docs.length;
})
);
promises.push(
this.state.db.collection("progress")
.where("uid", "==", this.state.user.uid)
.get()
.then((querySnapshot) => newState.totalTests = querySnapshot.docs.length)
);
await Promise.all(promises);
this.setState(newState);
this.props.page.load();
this.props.logEvent("page_view");
}
componentWillUnmount() {
this.isMounted = false;
this.props.page.unload();
}
msToTime = (time) => {
const localeData = {
minimumIntegerDigits: 2,
useGrouping: false,
};
const seconds = Math.floor((time / 1000) % 60).toLocaleString("en-GB", localeData);
const minutes = Math.floor((time / 1000 / 60) % 60).toLocaleString("en-GB", localeData);
const hours = Math.floor(time / 1000 / 60 / 60).toLocaleString("en-GB", localeData);
return `${hours}:${minutes}:${seconds}`;
}
render() {
return (
<div>
<NavBar items={this.state.navbarItems} />
<main>
<h1>Mistakes</h1>
<div className="history-sections">
<div className="historical-user-stats-container">
<div className="stat-row stat-row--inline">
<h1>{this.state.totalIncorrect}</h1>
<p>mistakes</p>
</div>
{
this.state.incorrectAnswers.length > 0 &&
<div className="stat-row stat-row--inline">
<h1>{this.state.incorrectAnswers[0].definition}</h1>
<p>meaning</p>
<h1>{this.state.incorrectAnswers[0].term}</h1>
<p>is the most common</p>
</div>
}
{
this.state.totalTests > 0 &&
<div className="stat-row stat-row--inline">
<h1>{(this.state.totalIncorrect / this.state.totalTests).toFixed(2)}</h1>
<p>mistakes per test on average</p>
</div>
}
</div>
<div className="mistakes-history-container">
{
this.state.incorrectAnswers.map((vocabItem) => (
<>
<div>
<h2>{vocabItem.term}</h2>
<p><b>{vocabItem.switchedCount} mistake{vocabItem.switchedCount !== 1 && "s"}{vocabItem.switchedCount > 0 && ":"}</b></p>
{
vocabItem.switchedCount > 0 &&
<div>
{
vocabItem.answers.sort((a, b) => {
if (a.answer < b.answer) {
return -1;
}
if (a.answer > b.answer) {
return 1;
}
return 0;
}).map((answerItem) => answerItem.switchLanguage && (
<p>{answerItem.answer === "" ? <i>skipped</i> : answerItem.answer}</p>
))
}
</div>
}
</div>
<div>
<h2>{vocabItem.definition}</h2>
<p><b>{vocabItem.count} mistake{vocabItem.count !== 1 && "s"}{vocabItem.count > 0 && ":"}</b></p>
{
vocabItem.count > 0 &&
<div>
{
vocabItem.answers.sort((a,b) => {
if (a.answer < b.answer) {
return -1;
}
if (a.answer > b.answer) {
return 1;
}
return 0;
}).map((answerItem) => !answerItem.switchLanguage && (
<p>{answerItem.answer === "" ? <i>skipped</i> : answerItem.answer}</p>
))
}
</div>
}
</div>
</>
))
}
</div>
</div>
</main>
<Footer />
</div>
)
}
}

View File

@@ -0,0 +1,17 @@
.mistakes-history-container {
display: grid;
grid-column-gap: 12px;
grid-template-columns: repeat(2, minmax(110px,max-content));
width: min-content;
word-wrap: break-word;
word-break: break-word;
width: 100%;
}
.mistakes-history-container > div {
margin-bottom: 4px;
}
.mistakes-history-container > div:last-child {
margin-bottom: 0;
}