better games browser with section progress
This commit is contained in:
parent
4583e733fa
commit
a3219230bc
|
@ -3,11 +3,14 @@ import games from "./games";
|
||||||
import { useUser } from "../user-context";
|
import { useUser } from "../user-context";
|
||||||
import Link from "../components/Link";
|
import Link from "../components/Link";
|
||||||
import SmoothCollapse from "react-smooth-collapse";
|
import SmoothCollapse from "react-smooth-collapse";
|
||||||
|
import {
|
||||||
|
AT,
|
||||||
|
} from "@lingdocs/lingdocs-main";
|
||||||
|
|
||||||
function GamesBrowser() {
|
function GamesBrowser() {
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const [opened, setOpened] = useState<string | undefined>(undefined);
|
const [opened, setOpened] = useState<string | undefined>(undefined);
|
||||||
function handleTitleClick(id: string) {
|
function handleChapterClick(id: string) {
|
||||||
setOpened(prev => (
|
setOpened(prev => (
|
||||||
prev === id ? undefined : id
|
prev === id ? undefined : id
|
||||||
));
|
));
|
||||||
|
@ -15,35 +18,101 @@ function GamesBrowser() {
|
||||||
return <div>
|
return <div>
|
||||||
{games.map((chapter) => (
|
{games.map((chapter) => (
|
||||||
<div key={chapter.chapter}>
|
<div key={chapter.chapter}>
|
||||||
<h3>{chapter.chapter}</h3>
|
<ChapterDisplay
|
||||||
{chapter.items.map(({ id, title, Game, studyLink }) => {
|
chapter={chapter}
|
||||||
const done = user && user.tests.some(t => t.id === id);
|
user={user}
|
||||||
const open = opened === id;
|
handleClick={handleChapterClick}
|
||||||
return <div key={id}>
|
expanded={opened === chapter.chapter}
|
||||||
<div className="d-flex flex-row justify-content-between align-items-center">
|
/>
|
||||||
<div>
|
|
||||||
<h4 className="my-4 clickable" onClick={() => handleTitleClick(id)}>
|
|
||||||
<i className={`fas fa-caret-${open ? "down" : "right"}`}></i> {title}
|
|
||||||
{` `}
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h4>
|
|
||||||
{done ? "✅"
|
|
||||||
:
|
|
||||||
<Link to={studyLink}>{"📚"}</Link>
|
|
||||||
}
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<SmoothCollapse expanded={open}>
|
|
||||||
<Game />
|
|
||||||
</SmoothCollapse>
|
|
||||||
</div>
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ChapterDisplay({ chapter, user, handleClick, expanded }: {
|
||||||
|
chapter: { chapter: string, items: GameRecord[] },
|
||||||
|
user: AT.LingdocsUser | undefined,
|
||||||
|
handleClick: (chapter: string) => void,
|
||||||
|
expanded: boolean,
|
||||||
|
}) {
|
||||||
|
const [opened, setOpened] = useState<string | undefined>(undefined);
|
||||||
|
const progress = getPercentageComplete(chapter, user?.tests);
|
||||||
|
function handleTitleClick(id: string) {
|
||||||
|
setOpened(prev => (
|
||||||
|
prev === id ? undefined : id
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return <div className="mb-3">
|
||||||
|
<div className="card clickable" onClick={() => handleClick(chapter.chapter)}>
|
||||||
|
<div className="card-body" style={{
|
||||||
|
backgroundColor: expanded ? "#e6e6e6" : "inherit",
|
||||||
|
}}>
|
||||||
|
<h4>{chapter.chapter}</h4>
|
||||||
|
<ChapterProgress progress={progress} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<SmoothCollapse expanded={expanded}>
|
||||||
|
{chapter.items.map(({ id, title, Game, studyLink }) => {
|
||||||
|
const done = user && user.tests.some(t => t.id === id);
|
||||||
|
const open = opened === id;
|
||||||
|
return <div key={id}>
|
||||||
|
<div className="d-flex flex-row justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<h4 className="my-4 clickable" onClick={() => handleTitleClick(id)}>
|
||||||
|
<i className={`fas fa-caret-${open ? "down" : "right"}`}></i> {title}
|
||||||
|
{` `}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4>
|
||||||
|
{done ? "✅"
|
||||||
|
:
|
||||||
|
<Link to={studyLink}>{"📚"}</Link>
|
||||||
|
}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<SmoothCollapse expanded={open}>
|
||||||
|
<Game />
|
||||||
|
</SmoothCollapse>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</SmoothCollapse>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChapterProgress({ progress }: { progress: "not logged in" | number }) {
|
||||||
|
if (progress === "not logged in") {
|
||||||
|
return <div className="small text-muted">Log in to see progress</div>;
|
||||||
|
}
|
||||||
|
return <div>
|
||||||
|
<div className="small text-muted">{progress}% mastered</div>
|
||||||
|
<div className="progress my-1" style={{ height: "5px" }}>
|
||||||
|
<div
|
||||||
|
className="progress-bar"
|
||||||
|
role="progressbar"
|
||||||
|
style={{ width: `${progress}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPercentageComplete(
|
||||||
|
chapter: { chapter: string, items: GameRecord[] },
|
||||||
|
tests: undefined | AT.TestResult[],
|
||||||
|
): "not logged in" | number {
|
||||||
|
if (!tests) return "not logged in";
|
||||||
|
const chapterTestIds = chapter.items.map(gr => gr.id);
|
||||||
|
const userCompletedIds = tests.map(t => t.id);
|
||||||
|
const required = chapterTestIds.length;
|
||||||
|
const completed = chapterTestIds
|
||||||
|
.filter(userCompletedIds.includes)
|
||||||
|
.length;
|
||||||
|
|
||||||
|
return Math.round(
|
||||||
|
(completed / (required + 1)) * 100
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default GamesBrowser;
|
export default GamesBrowser;
|
Loading…
Reference in New Issue