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
+113 -67
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>
+3
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>
+117 -103
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>
+2
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)}
+2
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)}
+7 -1
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';
+173
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>
+10
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" }
]
};
+12
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>
+10
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" }
]
};
+12
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>
+10
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" }
]
};
+12
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>