Add Geography Quiz and related components with quiz info and routing

This commit is contained in:
sHa
2025-08-14 13:42:06 +03:00
parent 163bd33c3c
commit ecc09b6b9a
14 changed files with 488 additions and 171 deletions

5
public/icons/map.svg Normal file
View File

@@ -0,0 +1,5 @@
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 7.16229C21 6.11871 21 5.59692 20.7169 5.20409C20.4337 4.81126 19.9387 4.64625 18.9487 4.31624L17.7839 3.92799C16.4168 3.47229 15.7333 3.24444 15.0451 3.3366C14.3569 3.42876 13.7574 3.82843 12.5583 4.62778L11.176 5.54937C10.2399 6.1734 9.77191 6.48541 9.24685 6.60952C9.05401 6.65511 8.85714 6.68147 8.6591 6.68823C8.11989 6.70665 7.58626 6.52877 6.51901 6.17302C5.12109 5.70705 4.42213 5.47406 3.89029 5.71066C3.70147 5.79466 3.53204 5.91678 3.39264 6.06935C3 6.49907 3 7.23584 3 8.70938V12.7736M21 11V15.2907C21 16.7642 21 17.501 20.6074 17.9307C20.468 18.0833 20.2985 18.2054 20.1097 18.2894C19.5779 18.526 18.8789 18.293 17.481 17.827C16.4137 17.4713 15.8801 17.2934 15.3409 17.3118C15.1429 17.3186 14.946 17.3449 14.7532 17.3905C14.2281 17.5146 13.7601 17.8266 12.824 18.4507L11.4417 19.3722C10.2426 20.1716 9.64311 20.5713 8.95493 20.6634C8.26674 20.7556 7.58319 20.5277 6.21609 20.072L5.05132 19.6838C4.06129 19.3538 3.56627 19.1888 3.28314 18.7959C3.01507 18.424 3.0008 17.9365 3.00004 17" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M15 3.5V7M15 17V11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M9 20.5V17M9 7V13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -9,6 +9,7 @@
import CapitalsQuiz from "./pages/CapitalsQuiz.svelte";
import NotFound from "./pages/NotFound.svelte";
import Header from "./components/Header.svelte";
import GeographyQuiz from "./pages/GeographyQuiz.svelte";
export const routes = {
"/": Home,
@@ -16,6 +17,7 @@
"/game": Game,
"/game/flags": FlagQuiz,
"/game/capitals": CapitalsQuiz,
"/game/geography": GeographyQuiz,
"*": NotFound,
};
@@ -87,7 +89,11 @@
selectedVariants = [];
tagDropdownOpen = false;
compactMode = false;
console.error("loadCollectionData: error loading data for", collection, error);
console.error(
"loadCollectionData: error loading data for",
collection,
error,
);
}
}
@@ -102,11 +108,16 @@
window.appData.filteredLogos = window.appData.logos.filter((logo) => {
const matchesSearch =
logo.name.toLowerCase().includes(val.toLowerCase()) ||
(logo.title && logo.title.toLowerCase().includes(val.toLowerCase())) ||
(logo.brand && logo.brand.toLowerCase().includes(val.toLowerCase())) ||
(logo.meta && Object.values(logo.meta).some(
v => typeof v === 'string' && v.toLowerCase().includes(val.toLowerCase())
));
(logo.title &&
logo.title.toLowerCase().includes(val.toLowerCase())) ||
(logo.brand &&
logo.brand.toLowerCase().includes(val.toLowerCase())) ||
(logo.meta &&
Object.values(logo.meta).some(
(v) =>
typeof v === "string" &&
v.toLowerCase().includes(val.toLowerCase()),
));
const matchesTags =
!selectedTags.length ||
(logo.tags &&
@@ -207,7 +218,7 @@
// Restore selected tags from URL
const tagsParam = params.get("tags");
if (tagsParam) {
selectedTags = tagsParam.split(",").filter(tag => tag.trim());
selectedTags = tagsParam.split(",").filter((tag) => tag.trim());
console.log("App: Restored selectedTags from URL:", selectedTags);
// Update localStorage with URL values
localStorage.setItem("selectedTags", JSON.stringify(selectedTags));
@@ -216,7 +227,7 @@
// Restore selected brands from URL
const brandsParam = params.get("brands");
if (brandsParam) {
selectedBrands = brandsParam.split(",").filter(brand => brand.trim());
selectedBrands = brandsParam.split(",").filter((brand) => brand.trim());
console.log("App: Restored selectedBrands from URL:", selectedBrands);
// Update localStorage with URL values localStorage.setItem("selectedBrands", JSON.stringify(selectedBrands));
}
@@ -224,10 +235,15 @@
// Restore selected variants from URL
const variantsParam = params.get("variants");
if (variantsParam) {
selectedVariants = variantsParam.split(",").filter(variant => variant.trim());
selectedVariants = variantsParam
.split(",")
.filter((variant) => variant.trim());
console.log("App: Restored selectedVariants from URL:", selectedVariants);
// Update localStorage with URL values
localStorage.setItem("selectedVariants", JSON.stringify(selectedVariants));
localStorage.setItem(
"selectedVariants",
JSON.stringify(selectedVariants),
);
}
// Force update window.appData after restoration
@@ -236,7 +252,10 @@
window.appData.selectedTags = [...selectedTags];
window.appData.selectedBrands = [...selectedBrands];
window.appData.selectedVariants = [...selectedVariants];
console.log("App: Updated window.appData after restoration with variants:", selectedVariants);
console.log(
"App: Updated window.appData after restoration with variants:",
selectedVariants,
);
updateFilteredLogosImmediate();
// Force re-render of components by updating references
@@ -246,11 +265,15 @@
}
}, 100);
// Restore view mode and compact mode from localStorage
const savedViewMode = localStorage.getItem("viewMode");
if (savedViewMode === "grid" || savedViewMode === "list" || savedViewMode === "compact") {
viewMode = savedViewMode;
}
// Restore view mode and compact mode from localStorage
const savedViewMode = localStorage.getItem("viewMode");
if (
savedViewMode === "grid" ||
savedViewMode === "list" ||
savedViewMode === "compact"
) {
viewMode = savedViewMode;
}
const savedCompact = localStorage.getItem("compactMode");
if (savedCompact === "true" || savedCompact === "false") {
setCompactMode(savedCompact === "true");
@@ -264,7 +287,10 @@
const parsedTags = JSON.parse(savedTags);
if (Array.isArray(parsedTags)) {
selectedTags = parsedTags;
console.log("App: Restored selectedTags from localStorage:", selectedTags);
console.log(
"App: Restored selectedTags from localStorage:",
selectedTags,
);
}
} catch (error) {
console.error("App: Error parsing saved tags:", error);
@@ -281,7 +307,10 @@
const parsedBrands = JSON.parse(savedBrands);
if (Array.isArray(parsedBrands)) {
selectedBrands = parsedBrands;
console.log("App: Restored selectedBrands from localStorage:", selectedBrands);
console.log(
"App: Restored selectedBrands from localStorage:",
selectedBrands,
);
}
} catch (error) {
console.error("App: Error parsing saved brands:", error);
@@ -298,7 +327,10 @@
const parsedVariants = JSON.parse(savedVariants);
if (Array.isArray(parsedVariants)) {
selectedVariants = parsedVariants;
console.log("App: Restored selectedVariants from localStorage:", selectedVariants);
console.log(
"App: Restored selectedVariants from localStorage:",
selectedVariants,
);
}
} catch (error) {
console.error("App: Error parsing saved variants:", error);
@@ -330,11 +362,16 @@
$: filteredLogos = logos.filter((logo) => {
const matchesSearch =
logo.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
(logo.title && logo.title.toLowerCase().includes(searchQuery.toLowerCase())) ||
(logo.brand && logo.brand.toLowerCase().includes(searchQuery.toLowerCase())) ||
(logo.meta && Object.values(logo.meta).some(
v => typeof v === 'string' && v.toLowerCase().includes(searchQuery.toLowerCase())
));
(logo.title &&
logo.title.toLowerCase().includes(searchQuery.toLowerCase())) ||
(logo.brand &&
logo.brand.toLowerCase().includes(searchQuery.toLowerCase())) ||
(logo.meta &&
Object.values(logo.meta).some(
(v) =>
typeof v === "string" &&
v.toLowerCase().includes(searchQuery.toLowerCase()),
));
const matchesTags =
!selectedTags.length ||
(logo.tags &&
@@ -640,7 +677,10 @@
console.log("App: Adding variant:", variant);
if (!selectedVariants.includes(variant)) {
selectedVariants = [...selectedVariants, variant];
localStorage.setItem("selectedVariants", JSON.stringify(selectedVariants));
localStorage.setItem(
"selectedVariants",
JSON.stringify(selectedVariants),
);
console.log("App: Updated selectedVariants:", selectedVariants);
// Update window.appData immediately
@@ -691,11 +731,16 @@
window.appData.filteredLogos = window.appData.logos.filter((logo) => {
const matchesSearch =
logo.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
(logo.title && logo.title.toLowerCase().includes(searchQuery.toLowerCase())) ||
(logo.brand && logo.brand.toLowerCase().includes(searchQuery.toLowerCase())) ||
(logo.meta && Object.values(logo.meta).some(
v => typeof v === 'string' && v.toLowerCase().includes(searchQuery.toLowerCase())
));
(logo.title &&
logo.title.toLowerCase().includes(searchQuery.toLowerCase())) ||
(logo.brand &&
logo.brand.toLowerCase().includes(searchQuery.toLowerCase())) ||
(logo.meta &&
Object.values(logo.meta).some(
(v) =>
typeof v === "string" &&
v.toLowerCase().includes(searchQuery.toLowerCase()),
));
const matchesTags =
!selectedTags.length ||
(logo.tags &&
@@ -753,17 +798,19 @@
}
function getTagObj(tag) {
const tagObj = allTags.find(t => t.text === tag);
const tagObj = allTags.find((t) => t.text === tag);
return tagObj || { text: tag };
}
function openLogoByAnchor(hash) {
if (!hash || !hash.startsWith('#/preview/')) return;
if (!hash || !hash.startsWith("#/preview/")) return;
const logoName = hash.replace('#/preview/', '').replace(/-/g, ' ');
const logo = logos.find(l =>
l.name.toLowerCase().replace(/\s+/g, '-') === hash.replace('#/preview/', '').toLowerCase() ||
l.name.toLowerCase() === logoName.toLowerCase()
const logoName = hash.replace("#/preview/", "").replace(/-/g, " ");
const logo = logos.find(
(l) =>
l.name.toLowerCase().replace(/\s+/g, "-") ===
hash.replace("#/preview/", "").toLowerCase() ||
l.name.toLowerCase() === logoName.toLowerCase(),
);
if (logo) {
@@ -774,16 +821,16 @@
// Listen for outside click to close dropdown
function handleOutsideClick(event) {
if (tagDropdownOpen && !event.target.closest('.filter-dropdown')) {
if (tagDropdownOpen && !event.target.closest(".filter-dropdown")) {
closeDropdown();
}
}
$: if (typeof window !== "undefined") {
if (tagDropdownOpen) {
document.addEventListener('click', handleOutsideClick);
document.addEventListener("click", handleOutsideClick);
} else {
document.removeEventListener('click', handleOutsideClick);
document.removeEventListener("click", handleOutsideClick);
}
}
</script>
@@ -791,37 +838,36 @@
<Router {routes} let:Component>
<svelte:component
this={Component}
displayLogos={displayLogos}
{displayLogos}
allLogos={logos}
theme={theme}
setTheme={setTheme}
viewMode={viewMode}
setGridView={setGridView}
setListView={setListView}
setCompactView={setCompactView}
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
allTags={allTags}
selectedTags={selectedTags}
selectedBrands={selectedBrands}
selectedVariants={selectedVariants}
tagDropdownOpen={tagDropdownOpen}
toggleDropdown={toggleDropdown}
addTag={addTag}
removeTag={removeTag}
addBrand={addBrand}
removeBrand={removeBrand}
addVariant={addVariant}
removeVariant={removeVariant}
getTagObj={getTagObj}
compactMode={compactMode}
setCompactMode={setCompactMode}
collection={collection}
setCollection={setCollection}
collections={collections}
{theme}
{setTheme}
{viewMode}
{setGridView}
{setListView}
{setCompactView}
{searchQuery}
{setSearchQuery}
{allTags}
{selectedTags}
{selectedBrands}
{selectedVariants}
{tagDropdownOpen}
{toggleDropdown}
{addTag}
{removeTag}
{addBrand}
{removeBrand}
{addVariant}
{removeVariant}
{getTagObj}
{compactMode}
{setCompactMode}
{collection}
{setCollection}
{collections}
/>
</Router>
<style>
</style>

View File

@@ -21,6 +21,9 @@
<div class="action-buttons">
{#if mode === 'welcome'}
<button class="action-btn secondary" on:click={() => handleAction('goToGames')}>
Back to Games
</button>
<button class="action-btn primary" on:click={() => handleAction('startQuiz')}>
{hasPlayedBefore ? 'Start New Quiz' : 'Start Quiz'}
</button>

View File

@@ -8,6 +8,7 @@
export let sessionStats = null;
export let showSessionResults = false;
export let sessionLength = 10;
export let quizInfo;
function startQuiz() {
dispatch("startQuiz");
@@ -72,27 +73,39 @@
<div class="welcome-container">
{#if showSessionResults && sessionStats}
<!-- Session Results View -->
<div class="welcome-header">
<div class="welcome-icon">
<InlineSvg path="/icons/check-circle.svg" alt="Quiz Complete" />
</div>
<h1>Quiz Complete!</h1>
<p class="welcome-subtitle">Great job on completing the quiz</p>
<div class="welcome-stats">
{#if quizInfo}
<div class="quiz-info">
{#if quizInfo.icon}
<div class="quiz-icon">
<img src={`/icons/${quizInfo.icon}.svg`} alt="Quiz icon" />
</div>
{/if}
{#if quizInfo.title}
<div class="quiz-title">{quizInfo.title}</div>
{/if}
{#if quizInfo.description}
<div class="quiz-description">{quizInfo.description}</div>
{/if}
{#if quizInfo.features}
<ul class="quiz-features">
{#each quizInfo.features as feature}
<li class="feature">
{#if feature.icon}
<InlineSvg path={feature.icon} alt={feature.text} />
{/if}
<span>{feature.text}</span>
</li>
{/each}
</ul>
{/if}
</div>
{/if}
</div>
<div class="grade-display">
<div class="percentage">{sessionPercentage}%</div>
<div class="description">{sessionGrade.description}</div>
</div>
<div class="stats-section">
<div class="grade-display">
<div
class="grade-circle"
style="border-color: {sessionGrade.color}; color: {sessionGrade.color}"
>
{sessionGrade.letter}
</div>
<div class="grade-text">
<div class="percentage">{sessionPercentage}%</div>
<div class="description">{sessionGrade.description}</div>
</div>
</div>
<div class="stats-grid">
<div class="stat-card">
@@ -145,106 +158,107 @@
{/if}.
</h3>
</div>
</div>
<!-- End of session results block -->
{:else}
<!-- Welcome/Stats View -->
<div class="welcome-header">
<div class="welcome-icon">
<InlineSvg path="/icons/flag.svg" alt="Flag Quiz" />
{#if quizInfo}
<div class="welcome-header">
{#if quizInfo.icon}
<div class="welcome-icon">
<InlineSvg path={quizInfo.icon} alt={quizInfo.title} />
</div>
{/if}
{#if quizInfo.title}
<h1>{quizInfo.title}</h1>
{/if}
{#if quizInfo.description}
<p class="welcome-subtitle">{quizInfo.description}</p>
{/if}
</div>
<h1>Flag Quiz</h1>
<p class="welcome-subtitle">Test your knowledge of world flags</p>
</div>
{#if hasPlayedBefore}
<div class="stats-section">
<h2>Your Statistics</h2>
{#if hasPlayedBefore}
<div class="stats-section">
<h2>Your Statistics</h2>
<div class="grade-display">
<div
class="accuracy-icon"
style="color: {accuracyGrade.color}"
>
<InlineSvg
path="/icons/medal-star.svg"
alt="Accuracy"
/>
</div>
<div class="grade-text">
<div class="percentage">{accuracy}%</div>
<div class="description">Accuracy</div>
</div>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon correct">
<div class="grade-display">
<div
class="accuracy-icon"
style="color: {accuracyGrade?.color}"
>
<InlineSvg
path="/icons/check-square.svg"
alt="Correct"
path="/icons/medal-star.svg"
alt="Accuracy"
/>
</div>
<div class="stat-value">{gameStats.correct}</div>
<div class="stat-label">Correct</div>
</div>
<div class="stat-card">
<div class="stat-icon wrong">
<InlineSvg
path="/icons/close-square.svg"
alt="Wrong"
/>
<div class="grade-text">
<div class="percentage">{accuracy}%</div>
<div class="description">Accuracy</div>
</div>
<div class="stat-value">{gameStats.wrong}</div>
<div class="stat-label">Wrong</div>
</div>
<div class="stat-card">
<div class="stat-icon skipped">
<InlineSvg
path="/icons/skip-square.svg"
alt="Skipped"
/>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon correct">
<InlineSvg
path="/icons/check-square.svg"
alt="Correct"
/>
</div>
<div class="stat-value">{gameStats.correct}</div>
<div class="stat-label">Correct</div>
</div>
<div class="stat-value">{gameStats.skipped}</div>
<div class="stat-label">Skipped</div>
</div>
</div>
<div class="progress-summary">
<h3>Total Questions Answered: {totalQuestions}</h3>
</div>
</div>
{:else}
<div class="welcome-message">
<h2>Welcome to Flag Quiz!</h2>
<p>
Challenge yourself to identify flags from around the world.
Each quiz contains <strong>{sessionLength} questions</strong
> with a mix of flag-to-country and country-to-flag challenges.
</p>
<div class="stat-card">
<div class="stat-icon wrong">
<InlineSvg
path="/icons/close-square.svg"
alt="Wrong"
/>
</div>
<div class="stat-value">{gameStats.wrong}</div>
<div class="stat-label">Wrong</div>
</div>
<div class="features">
<div class="feature">
<InlineSvg path="/icons/global.svg" alt="Global" />
<span>Flags from every continent</span>
<div class="stat-card">
<div class="stat-icon skipped">
<InlineSvg
path="/icons/skip-square.svg"
alt="Skipped"
/>
</div>
<div class="stat-value">{gameStats.skipped}</div>
<div class="stat-label">Skipped</div>
</div>
</div>
<div class="feature">
<InlineSvg
path="/icons/medal-ribbon.svg"
alt="Achievements"
/>
<span>Unlock achievements</span>
</div>
<div class="feature">
<InlineSvg
path="/icons/chart-square.svg"
alt="Statistics"
/>
<span>Track your progress</span>
<div class="progress-summary">
<h3>Total Questions Answered: {totalQuestions}</h3>
</div>
</div>
</div>
{:else}
<div class="welcome-message">
{#if quizInfo.title}
<h2>Welcome to {quizInfo.title}!</h2>
{/if}
{#if quizInfo.description}
<p>{quizInfo.description}</p>
{/if}
{#if quizInfo.features}
<div class="features">
{#each quizInfo.features as feature}
{#if feature.icon && feature.text}
<div class="feature">
<InlineSvg path={feature.icon} alt={feature.text} />
<span>{feature.text}</span>
</div>
{:else}
<div class="feature">{feature}</div>
{/if}
{/each}
</div>
{/if}
</div>
{/if}
{/if}
{/if}
</div>

View File

@@ -1,4 +1,5 @@
<script>
import { quizInfo } from '../quizInfo/CapitalsQuizInfo.js';
import { onMount } from "svelte";
import Header from "../components/Header.svelte";
import Footer from "../components/Footer.svelte";
@@ -984,6 +985,7 @@
{sessionStats}
{sessionLength}
{showSessionResults}
quizInfo={quizInfo}
on:startQuiz={startNewSession}
on:openSettings={() => (showSettings = true)}
on:closeResults={() => (showSessionResults = false)}

View File

@@ -1,4 +1,5 @@
<script>
import { quizInfo } from '../quizInfo/FlagQuizInfo.js';
import { onMount } from "svelte";
import Header from "../components/Header.svelte";
import Footer from "../components/Footer.svelte";
@@ -969,6 +970,7 @@
{sessionStats}
{sessionLength}
{showSessionResults}
quizInfo={quizInfo}
on:startQuiz={startNewSession}
on:openSettings={() => (showSettings = true)}
on:closeResults={() => (showSessionResults = false)}

View File

@@ -18,8 +18,14 @@
description: 'Test your knowledge of world capitals',
icon: '🏛️',
route: '#/game/capitals'
},
{
name: 'geography',
title: 'Geography Quiz',
description: 'Test your knowledge of world geography',
icon: '🗺️',
route: '#/game/geography'
}
// Future games will be added here
];
let theme = 'system';

View File

@@ -0,0 +1,173 @@
<script>
import { quizInfo } from '../quizInfo/GeographyQuizInfo.js';
import { onMount } from "svelte";
import Header from "../components/Header.svelte";
import Footer from "../components/Footer.svelte";
import CountryMap from "../components/CountryMap.svelte";
import WelcomeStats from "../components/WelcomeStats.svelte";
import ActionButtons from "../components/ActionButtons.svelte";
// Game data
let countries = [];
let currentQuestion = null;
let correctAnswer = null;
let options = [];
// Game states
let gameState = "welcome"; // 'welcome', 'loading', 'question', 'answered', 'session-complete'
let quizSubpage = "welcome";
let selectedAnswer = null;
let showResult = false;
let questionKey = 0;
let sessionLength = 10;
let currentSessionQuestions = 0;
let sessionStats = {
correct: 0,
wrong: 0,
total: 0,
sessionLength: 10,
};
let showSessionResults = false;
let sessionInProgress = false;
async function loadCountries() {
const res = await fetch("/data/flags.json");
const data = await res.json();
countries = data.filter(
(c) => c.tags && c.tags.includes("Country") && c.code && c.meta?.country
);
}
function generateQuestion() {
// Pick correct country
const idx = Math.floor(Math.random() * countries.length);
correctAnswer = countries[idx];
// Pick 3 random other countries
let other = countries.filter((c) => c.code !== correctAnswer.code);
other = shuffle(other).slice(0, 3);
options = shuffle([correctAnswer, ...other]);
selectedAnswer = null;
showResult = false;
questionKey++;
gameState = "question";
quizSubpage = "quiz";
currentSessionQuestions++;
}
function shuffle(arr) {
return arr
.map((v) => [Math.random(), v])
.sort((a, b) => a[0] - b[0])
.map((v) => v[1]);
}
function selectAnswer(option) {
selectedAnswer = option;
showResult = true;
gameState = "answered";
if (option === correctAnswer) {
sessionStats.correct++;
} else {
sessionStats.wrong++;
}
sessionStats.total++;
}
function startNewSession() {
sessionInProgress = true;
quizSubpage = "quiz";
gameState = "loading";
currentSessionQuestions = 0;
sessionStats = {
correct: 0,
wrong: 0,
total: 0,
sessionLength,
};
generateQuestion();
}
function endSession() {
sessionInProgress = false;
quizSubpage = "welcome";
gameState = "welcome";
showSessionResults = true;
}
onMount(async () => {
await loadCountries();
});
</script>
<Header />
<main class="map-quiz">
{#if quizSubpage === "welcome"}
<div class="container">
<WelcomeStats
gameStats={sessionStats}
{sessionStats}
{sessionLength}
{showSessionResults}
quizInfo={quizInfo}
on:startQuiz={startNewSession}
on:openSettings={() => {}}
on:closeResults={() => {}}
/>
<ActionButtons
mode={showSessionResults ? "results" : "welcome"}
sessionInfo={showSessionResults ? "" : `${sessionLength} questions per quiz`}
hasPlayedBefore={sessionStats.total > 0}
on:action={startNewSession}
/>
</div>
{:else if quizSubpage === "quiz"}
<div class="container">
<h2>Question {currentSessionQuestions} of {sessionLength}</h2>
{#if correctAnswer}
<CountryMap
countryCodes={[correctAnswer.code]}
countryNames={[]}
mapPath="/data/world.svg"
countryScale={true}
scalePadding={60}
/>
<div class="options">
{#each options as option}
<button
class="option-btn {selectedAnswer === option ? (option === correctAnswer ? 'correct' : 'wrong') : ''}"
on:click={() => selectAnswer(option)}
disabled={showResult}
>
{option.meta.country}
</button>
{/each}
</div>
{#if showResult}
<div class="result">
{selectedAnswer === correctAnswer
? 'Correct!'
: `Wrong! The correct answer is ${correctAnswer.meta.country}`}
</div>
{#if currentSessionQuestions < sessionLength}
<button class="next-btn" on:click={generateQuestion}>Next</button>
{:else}
<button class="next-btn" on:click={endSession}>Finish</button>
{/if}
{/if}
{:else}
<div>Loading...</div>
{/if}
</div>
{/if}
</main>
<Footer />
<style>
/* Use the same container and layout as FlagQuiz/CapitalsQuiz for welcome page */
.container {
max-width: 800px;
margin: 0 auto;
padding: 1.25rem 1rem;
}
</style>

View File

@@ -0,0 +1,10 @@
export const quizInfo = {
title: "Capitals Quiz",
icon: "/icons/buildings.svg",
description: "Test your knowledge of world capitals.",
features: [
{ icon: "/icons/global.svg", text: "Capitals from every continent" },
{ icon: "/icons/medal-ribbon.svg", text: "Perfect rounds and achievements" },
{ icon: "/icons/chart-square.svg", text: "Track your progress" }
]
};

View File

@@ -0,0 +1,12 @@
<script>
export const quizInfo = {
title: "Capitals Quiz",
icon: "/icons/buildings.svg",
description: "Test your knowledge of world capitals.",
features: [
{ icon: "/icons/global.svg", text: "Capitals from every continent" },
{ icon: "/icons/medal-ribbon.svg", text: "Perfect rounds and achievements" },
{ icon: "/icons/chart-square.svg", text: "Track your progress" }
]
};
</script>

View File

@@ -0,0 +1,10 @@
export const quizInfo = {
title: "Flag Quiz",
icon: "/icons/flag.svg",
description: "Test your knowledge of world flags.",
features: [
{ icon: "/icons/global.svg", text: "Flags from every continent" },
{ icon: "/icons/medal-ribbon.svg", text: "Unlock achievements" },
{ icon: "/icons/chart-square.svg", text: "Track your progress" }
]
};

View File

@@ -0,0 +1,12 @@
<script>
export const quizInfo = {
title: "Flag Quiz",
icon: "/icons/flag.svg",
description: "Test your knowledge of world flags.",
features: [
{ icon: "/icons/global.svg", text: "Flags from every continent" },
{ icon: "/icons/medal-ribbon.svg", text: "Unlock achievements" },
{ icon: "/icons/chart-square.svg", text: "Track your progress" }
]
};
</script>

View File

@@ -0,0 +1,10 @@
export const quizInfo = {
title: "Geography Quiz",
icon: "/icons/map.svg",
description: "Test your knowledge of world geography by identifying countries from their map shapes.",
features: [
{ icon: "/icons/global.svg", text: "Countries from every continent" },
{ icon: "/icons/map.svg", text: "Unique map-based questions" },
{ icon: "/icons/chart-square.svg", text: "Track your progress" }
]
};

View File

@@ -0,0 +1,12 @@
<script>
export const quizInfo = {
title: "Geography Quiz",
icon: "/icons/map.svg",
description: "Test your knowledge of world geography by identifying countries from their map shapes.",
features: [
{ icon: "/icons/global.svg", text: "Countries from every continent" },
{ icon: "/icons/map.svg", text: "Unique map-based questions" },
{ icon: "/icons/chart-square.svg", text: "Track your progress" }
]
};
</script>