diff --git a/public/icons/camera-square.svg b/public/icons/camera-square.svg new file mode 100644 index 0000000..3bf11bf --- /dev/null +++ b/public/icons/camera-square.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/check-circle.svg b/public/icons/check-circle.svg new file mode 100644 index 0000000..7c5d66f --- /dev/null +++ b/public/icons/check-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/check-square.svg b/public/icons/check-square.svg new file mode 100644 index 0000000..f672b63 --- /dev/null +++ b/public/icons/check-square.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/close-circle.svg b/public/icons/close-circle.svg new file mode 100644 index 0000000..264c82a --- /dev/null +++ b/public/icons/close-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/close-square.svg b/public/icons/close-square.svg new file mode 100644 index 0000000..6164cad --- /dev/null +++ b/public/icons/close-square.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/confetti-minimalistic.svg b/public/icons/confetti-minimalistic.svg new file mode 100644 index 0000000..71c5b43 --- /dev/null +++ b/public/icons/confetti-minimalistic.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/icons/crown-minimalistic.svg b/public/icons/crown-minimalistic.svg new file mode 100644 index 0000000..7e32a29 --- /dev/null +++ b/public/icons/crown-minimalistic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/cup-first.svg b/public/icons/cup-first.svg new file mode 100644 index 0000000..0f09e04 --- /dev/null +++ b/public/icons/cup-first.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/icons/cup-star.svg b/public/icons/cup-star.svg new file mode 100644 index 0000000..100e9be --- /dev/null +++ b/public/icons/cup-star.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/icons/cup.svg b/public/icons/cup.svg new file mode 100644 index 0000000..2a1c675 --- /dev/null +++ b/public/icons/cup.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icons/danger-square.svg b/public/icons/danger-square.svg new file mode 100644 index 0000000..ef30cf3 --- /dev/null +++ b/public/icons/danger-square.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/fail-square.svg b/public/icons/fail-square.svg new file mode 100644 index 0000000..6164cad --- /dev/null +++ b/public/icons/fail-square.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/filter.svg b/public/icons/filter.svg new file mode 100644 index 0000000..0512682 --- /dev/null +++ b/public/icons/filter.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/full-screen-square.svg b/public/icons/full-screen-square.svg new file mode 100644 index 0000000..e840422 --- /dev/null +++ b/public/icons/full-screen-square.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/icons/gamepad.svg b/public/icons/gamepad.svg new file mode 100644 index 0000000..0199881 --- /dev/null +++ b/public/icons/gamepad.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icons/icons-source.url b/public/icons/icons-source.url new file mode 100644 index 0000000..f205f71 --- /dev/null +++ b/public/icons/icons-source.url @@ -0,0 +1 @@ +https://www.svgrepo.com/collection/solar-broken-line-icons/ diff --git a/public/icons/info-circle.svg b/public/icons/info-circle.svg new file mode 100644 index 0000000..223f4c5 --- /dev/null +++ b/public/icons/info-circle.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/public/icons/info-square.svg b/public/icons/info-square.svg new file mode 100644 index 0000000..c4096a2 --- /dev/null +++ b/public/icons/info-square.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/public/icons/lock-locked.svg b/public/icons/lock-locked.svg new file mode 100644 index 0000000..23fa085 --- /dev/null +++ b/public/icons/lock-locked.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/lock-unlocked.svg b/public/icons/lock-unlocked.svg new file mode 100644 index 0000000..8c95e9b --- /dev/null +++ b/public/icons/lock-unlocked.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/medal-ribbon-star.svg b/public/icons/medal-ribbon-star.svg new file mode 100644 index 0000000..a31e531 --- /dev/null +++ b/public/icons/medal-ribbon-star.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/medal-ribbon.svg b/public/icons/medal-ribbon.svg new file mode 100644 index 0000000..fc17ff2 --- /dev/null +++ b/public/icons/medal-ribbon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/medal-ribbons-star.svg b/public/icons/medal-ribbons-star.svg new file mode 100644 index 0000000..3187c16 --- /dev/null +++ b/public/icons/medal-ribbons-star.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/icons/medal-star-circle.svg b/public/icons/medal-star-circle.svg new file mode 100644 index 0000000..1dfba72 --- /dev/null +++ b/public/icons/medal-star-circle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/medal-star-square.svg b/public/icons/medal-star-square.svg new file mode 100644 index 0000000..cbd4e77 --- /dev/null +++ b/public/icons/medal-star-square.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/medal-star.svg b/public/icons/medal-star.svg new file mode 100644 index 0000000..5031e2b --- /dev/null +++ b/public/icons/medal-star.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/moon.svg b/public/icons/moon.svg new file mode 100644 index 0000000..5b2251c --- /dev/null +++ b/public/icons/moon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/ok-square.svg b/public/icons/ok-square.svg new file mode 100644 index 0000000..f672b63 --- /dev/null +++ b/public/icons/ok-square.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/record.svg b/public/icons/record.svg new file mode 100644 index 0000000..cf43145 --- /dev/null +++ b/public/icons/record.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/running.svg b/public/icons/running.svg new file mode 100644 index 0000000..87d8fa9 --- /dev/null +++ b/public/icons/running.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/icons/sad-circle.svg b/public/icons/sad-circle.svg new file mode 100644 index 0000000..6d546d0 --- /dev/null +++ b/public/icons/sad-circle.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/icons/sad-square.svg b/public/icons/sad-square.svg new file mode 100644 index 0000000..f564584 --- /dev/null +++ b/public/icons/sad-square.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/icons/skip-square.svg b/public/icons/skip-square.svg new file mode 100644 index 0000000..a6a7f44 --- /dev/null +++ b/public/icons/skip-square.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/smile-squre.svg b/public/icons/smile-squre.svg new file mode 100644 index 0000000..a8bf9b2 --- /dev/null +++ b/public/icons/smile-squre.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/icons/sun.svg b/public/icons/sun.svg new file mode 100644 index 0000000..329c8f2 --- /dev/null +++ b/public/icons/sun.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/icons/unread.svg b/public/icons/unread.svg new file mode 100644 index 0000000..0a8317f --- /dev/null +++ b/public/icons/unread.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/AchievementButton.svelte b/src/components/AchievementButton.svelte new file mode 100644 index 0000000..249185f --- /dev/null +++ b/src/components/AchievementButton.svelte @@ -0,0 +1,70 @@ + + + + + diff --git a/src/components/Achievements.svelte b/src/components/Achievements.svelte new file mode 100644 index 0000000..113b479 --- /dev/null +++ b/src/components/Achievements.svelte @@ -0,0 +1,458 @@ + + + +{#if show} +
+ +
+{/if} + + +{#if newAchievements.length > 0} +
+ {#each newAchievements as achievement} +
+
+ +
+
+
Achievement Unlocked!
+
{achievement.name}
+
+
+ {/each} +
+{/if} + + diff --git a/src/components/Filter.svelte b/src/components/Filter.svelte index 6f4c573..d8c5cec 100644 --- a/src/components/Filter.svelte +++ b/src/components/Filter.svelte @@ -1,4 +1,5 @@ @@ -10,66 +11,21 @@ class:active={theme === "system"} aria-label="System theme" > - + @@ -81,4 +37,10 @@ gap: 0.2rem; margin-left: auto; } + + .theme-switch-group .icon { + width: 20px; + height: 20px; + display: inline-flex; + } diff --git a/src/pages/FlagQuiz.svelte b/src/pages/FlagQuiz.svelte index 73a4b98..b283b6f 100644 --- a/src/pages/FlagQuiz.svelte +++ b/src/pages/FlagQuiz.svelte @@ -1,6 +1,9 @@ -
+
showAchievements = true} +/>
@@ -334,6 +412,41 @@
{/if} + + + {#if showResetConfirmation} +
e.target === e.currentTarget && cancelReset()}> +
+
+

⚠️ Reset All Data

+
+
+

This action will permanently delete:

+
    +
  • ✗ All game statistics (correct, wrong, skipped answers)
  • +
  • ✗ Current session score
  • +
  • ✗ All unlocked achievements
  • +
  • ✗ Achievement progress
  • +
+

This cannot be undone!

+
+
+ + +
+
+
+ {/if} + + + showAchievements = false} + on:achievementsUnlocked={handleAchievementsUnlocked} + /> {#if gameState === 'loading'}
Loading flags...
{:else if currentQuestion} @@ -350,12 +463,28 @@ {#if showResult}
{#if selectedAnswer === correctAnswer} -
✅ Correct!
+
Correct!
{:else}
- ❌ Wrong! + Wrong! {#if currentQuestion.type === 'flag-to-country'} - The correct answer is: {getCountryName(currentQuestion.correct)}. + + The correct answer is: {getCountryName(currentQuestion.correct)}. + + {#if showResultCountryInfo} + + {/if} + {:else} You selected the {getCountryName(currentQuestion.options[selectedAnswer])} flag. {/if} @@ -386,7 +515,25 @@
{:else}
-

{getCountryName(currentQuestion.correct)}

+

+ {getCountryName(currentQuestion.correct)} + {#if currentQuestion.correct?.meta?.description} + + {#if showCountryInfo} + + {/if} + {/if} +

@@ -414,9 +561,9 @@ {/if}
+ Back to Games - Back to Games
@@ -532,6 +679,101 @@ background: #cc3333; } + /* Confirmation Dialog Styles */ + .confirmation-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + justify-content: center; + align-items: center; + z-index: 1001; + } + + .confirmation-dialog { + background: var(--color-bg-primary); + border: 2px solid var(--color-border); + border-radius: 1rem; + padding: 0; + max-width: 500px; + width: 90%; + box-shadow: 0 10px 24px rgba(0,0,0,0.25); + } + + .confirmation-header { + padding: 1.5rem 1.5rem 1rem 1.5rem; + border-bottom: 1px solid var(--color-border); + } + + .confirmation-header h3 { + margin: 0; + font-size: 1.3rem; + color: #dc2626; + } + + .confirmation-content { + padding: 1.5rem; + } + + .confirmation-content p { + margin: 0 0 1rem 0; + color: var(--color-text-primary); + } + + .confirmation-content ul { + margin: 1rem 0; + padding-left: 1.5rem; + color: var(--color-text-secondary); + } + + .confirmation-content li { + margin: 0.5rem 0; + } + + .confirmation-actions { + display: flex; + gap: 1rem; + padding: 1rem 1.5rem 1.5rem 1.5rem; + justify-content: flex-end; + border-top: 1px solid var(--color-border); + } + + .cancel-btn { + background: var(--color-bg-secondary); + border: 1px solid var(--color-border); + color: var(--color-text-primary); + padding: 0.75rem 1.5rem; + border-radius: 0.5rem; + font-size: 0.9rem; + cursor: pointer; + transition: all 0.2s ease; + } + + .cancel-btn:hover { + background: var(--color-bg-hover); + border-color: var(--color-primary); + } + + .confirm-btn { + background: #dc2626; + border: 1px solid #dc2626; + color: white; + padding: 0.75rem 1.5rem; + border-radius: 0.5rem; + font-size: 0.9rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; + } + + .confirm-btn:hover { + background: #b91c1c; + border-color: #b91c1c; + } + .loading { text-align: center; @@ -592,6 +834,7 @@ .country-display { text-align: center; margin-bottom: 2rem; + position: relative; } .country-name { @@ -601,6 +844,52 @@ margin: 0; } + .info-icon { + margin-left: 0.5rem; + width: 2rem; + height: 2rem; + vertical-align: middle; + background: none; + color: var(--color-text-primary); + cursor: pointer; + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; + margin-bottom: 5px; + } + + .info-icon :global(.svg-wrapper) { + width: 100%; + height: 100%; + } + + .info-icon:hover, + .info-icon:focus { + color: var(--color-text-primary); + border-color: var(--color-border); + outline: none; + } + + .info-tooltip { + position: absolute; + left: 50%; + top: calc(100% + 8px); + transform: translateX(-50%); + background: var(--color-bg-secondary); + color: var(--color-text-primary); + border: 1px solid var(--color-border); + border-radius: 8px; + padding: 0.75rem 1rem; + width: min(90vw, 520px); + max-height: 40vh; + overflow: auto; + box-shadow: 0 8px 20px rgba(0,0,0,0.2); + z-index: 5; + text-align: left; + font-size: 0.95rem; + } + .options { display: grid; gap: 1rem; @@ -700,6 +989,100 @@ color: #ef4444; } + .result-icon { + display: inline-flex; + width: 24px; + height: 24px; + vertical-align: middle; + margin-right: 0.5rem; + } + + .result-icon.smile-icon { + color: #22c55e; /* green for correct */ + animation: correctBounce 0.6s ease-out; + } + + .result-icon.sad-icon { + color: #ef4444; /* red for wrong */ + animation: wrongShake 0.5s ease-in-out; + } + + @keyframes correctBounce { + 0% { + transform: scale(0) rotate(0deg); + opacity: 0; + } + 50% { + transform: scale(1.3) rotate(5deg); + opacity: 1; + } + 100% { + transform: scale(1) rotate(0deg); + opacity: 1; + } + } + + @keyframes wrongShake { + 0% { + transform: translateX(0) scale(0); + opacity: 0; + } + 25% { + transform: translateX(-5px) scale(1); + opacity: 1; + } + 50% { + transform: translateX(5px) scale(1); + } + 75% { + transform: translateX(-3px) scale(1); + } + 100% { + transform: translateX(0) scale(1); + opacity: 1; + } + } + + .result-country-info { + position: relative; + display: inline-flex; + align-items: center; + gap: 0.5rem; + } + + .result-info-btn { + margin-left: 0.25rem; + width: 2rem; + height: 2rem; + vertical-align: middle; + background: none; + border: none; + color: var(--color-text-primary); + cursor: pointer; + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; + opacity: 0.7; + transition: opacity 0.2s; + } + + .result-info-btn:hover, + .result-info-btn:focus { + opacity: 1; + outline: none; + } + + .result-info-tooltip { + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + margin-top: 0.5rem; + min-width: 200px; + max-width: 300px; + } + .controls { display: flex; justify-content: center; @@ -757,8 +1140,8 @@ @media (max-width: 768px) { .container { - padding: 0.75rem; - padding-top: 0.75rem; + padding: 0.75rem; + padding-top: 0.75rem; } .options { @@ -775,6 +1158,12 @@ max-height: 180px; } + .info-tooltip { + width: 92vw; + left: 50%; + transform: translateX(-50%); + } + .controls { flex-direction: row; flex-wrap: nowrap;