From d444447425ed20e81618639c064ff01e668eb440 Mon Sep 17 00:00:00 2001 From: Halit Aksoy Date: Fri, 16 Oct 2026 14:55:51 +0300 Subject: [PATCH] feat(frontend): offer the tutorial to newcomers and a practice game The home page shows newcomers a banner to start the lessons; it goes away for good after Not now, a lesson or a game. The lessons end with a practice game against the Easy bot with hints on. --- .../src/components/PracticeGameCard.tsx | 47 +++++++++++++++++++ .../src/components/home/TutorialBanner.tsx | 47 +++++++++++++++++++ apps/frontend/src/routes/Home.tsx | 2 + apps/frontend/src/routes/LearnPage.tsx | 2 + apps/frontend/src/routes/LessonPage.tsx | 2 + apps/frontend/src/routes/LobyPage.tsx | 2 + docs/specs/tutorial/tasks.md | 2 +- 7 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 apps/frontend/src/components/PracticeGameCard.tsx create mode 100644 apps/frontend/src/components/home/TutorialBanner.tsx diff --git a/apps/frontend/src/components/PracticeGameCard.tsx b/apps/frontend/src/components/PracticeGameCard.tsx new file mode 100644 index 0000000..eee85bd --- /dev/null +++ b/apps/frontend/src/components/PracticeGameCard.tsx @@ -0,0 +1,47 @@ +import * as React from "react"; +import { FunctionComponent } from "react"; +import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { setHintsEnabled } from "@mancala/core"; +import { Context } from "../context/context"; +import Util from "../util/Util"; + +// The tutorial's last step: a game against the Easy bot with hints on (R3.1). +const PracticeGameCard: FunctionComponent<{ context: Context }> = ({ context }) => { + const { t } = useTranslation(); + const navigate = useNavigate(); + const start = () => { + if (Util.checkConnectionAndMaybeAlert(context, t)) return; + setHintsEnabled(context.storage, true); + navigate("/loby?bot=easy"); + }; + return ( +
+
+ {t("PracticeGame")} +

{t("PracticeGameText")}

+
+ + +
+ ); +}; + +export default PracticeGameCard; diff --git a/apps/frontend/src/components/home/TutorialBanner.tsx b/apps/frontend/src/components/home/TutorialBanner.tsx new file mode 100644 index 0000000..7d3d1da --- /dev/null +++ b/apps/frontend/src/components/home/TutorialBanner.tsx @@ -0,0 +1,47 @@ +import * as React from "react"; +import { FunctionComponent, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { hideTutorialBanner, shouldShowTutorialBanner } from "@mancala/core"; +import { Context } from "../../context/context"; + +// For newcomers: an offer to learn the game, until they open the lessons, play a game or say not now (R4). +const TutorialBanner: FunctionComponent<{ context: Context }> = ({ context }) => { + const { t } = useTranslation(); + const navigate = useNavigate(); + const [visible, setVisible] = useState(() => shouldShowTutorialBanner(context.storage)); + if (!visible) return null; + return ( +
+ {t("NewToMangala")} +
+ + +
+ +
+ ); +}; + +export default TutorialBanner; diff --git a/apps/frontend/src/routes/Home.tsx b/apps/frontend/src/routes/Home.tsx index d6e0690..cb7f733 100644 --- a/apps/frontend/src/routes/Home.tsx +++ b/apps/frontend/src/routes/Home.tsx @@ -7,6 +7,7 @@ import PageContainer from "../components/PageContainer"; import { PlusIcon } from "../components/Icons"; import BotLevelMenu from "../components/BotLevelMenu"; import ActiveGameBanner from "../components/home/ActiveGameBanner"; +import TutorialBanner from "../components/home/TutorialBanner"; import LiveGamesSection from "../components/home/LiveGamesSection"; import RecentGamesSection from "../components/home/RecentGamesSection"; import PopularGamesSection from "../components/home/PopularGamesSection"; @@ -58,6 +59,7 @@ const Home: FunctionComponent<{
+ {activeGame && authUser && ( = ({ context }) => { @@ -31,6 +32,7 @@ const LearnPage: FunctionComponent<{ context: Context, theme: Theme }> = ({ cont ); })} + {t("HowToPlay")}