Skip to content

Commit 85d99b0

Browse files
author
=
committed
First commit
1 parent e5501bf commit 85d99b0

39 files changed

+1060
-0
lines changed

.gitignore

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Nuxt dev/build outputs
2+
.output
3+
.data
4+
.nuxt
5+
.nitro
6+
.cache
7+
dist
8+
9+
# Node dependencies
10+
node_modules
11+
12+
# Logs
13+
logs
14+
*.log
15+
16+
# Misc
17+
.DS_Store
18+
.fleet
19+
.idea
20+
21+
# Local env files
22+
.env
23+
.env.*
24+
!.env.example

.vscode/settings.json

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"files.exclude": {
3+
"**/.git": true,
4+
"**/.svn": true,
5+
"**/.hg": true,
6+
"**/CVS": true,
7+
"**/.DS_Store": true,
8+
"**/Thumbs.db": true
9+
},
10+
"hide-files.files": []
11+
}

README.md

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<h1 align="center">Quizzler - Quiz app</h1>
2+
3+
<p align="center">
4+
<img src="screenshots/banner.png"/>
5+
</p>
6+
7+
<p align="center">
8+
Quizzler is a simple and fun quiz game that challenges you with different categories and lets you compete for the leaderboard for all the correct answers! Built with Nuxt 3 and its ecosystem.
9+
</p>
10+
11+
## App & Preview
12+
13+
Play : [Quizzler](https://quizzler.jooo.my.id/)
14+
15+
<p align="center">
16+
<img src="screenshots/main.png"/>
17+
</p>
18+
19+
## Library, Frameworks & Tools Used
20+
21+
- [Nuxt 3](https://nuxt.com/)
22+
- [Nuxt UI](https://ui.nuxt.com/)
23+
- [Nuxt Google Fonts](https://google-fonts.nuxtjs.org/)
24+
- [Nuxt SEO](https://nuxtseo.com/)
25+
- [Supabase](https://supabase.com/)
26+
- [Nuxt Supabase](https://supabase.nuxtjs.org/)
27+
- [Pinia](https://pinia.vuejs.org/)
28+
- [TailwindCSS](https://tailwindcss.com/)
29+
- [Open Trivia Database](https://opentdb.com/) (The quiz API)
30+
- [Bun](https://bun.sh/)
31+
32+
## Setup & Build
33+
34+
if you want to continue or modify this project, you can follow these steps:
35+
36+
- Clone this project
37+
38+
```bash
39+
git clone https://github.com/jo0707/quizzler
40+
```
41+
42+
- Install dependencies (bun)
43+
44+
```bash
45+
bun install
46+
```
47+
48+
This project uses Supabase as its leaderboard database:
49+
50+
- Create a Supabase account
51+
- Create a new project
52+
- Create a leaderboard table, you can use this SQL query:
53+
54+
```sql
55+
create table
56+
public.leaderboard (
57+
id bigint generated by default as identity,
58+
created_at timestamp with time zone not null default now(),
59+
name character varying null,
60+
score integer not null default 0,
61+
time integer not null default 0,
62+
mode character varying not null default ''::character varying,
63+
constraint leaderboard_pkey primary key (id)
64+
) tablespace pg_default;
65+
```
66+
67+
- Go to the leaderboard table, You can either disable the RLS, or create a new RLS that allows public to do INSERT and SELECT query.
68+
69+
- Copy your project URL and public key from the project settings tab
70+
71+
- Create new .env file and fill it with your Supabase credentials
72+
73+
```env
74+
SUPABASE_URL=https://yourproject.supabase.co"
75+
SUPABASE_KEY="your.anon.token"
76+
```
77+
78+
- Run the project
79+
80+
```bash
81+
bun dev
82+
```
83+
84+
- Build the project
85+
86+
```bash
87+
bun run build
88+
#or
89+
bun run generate # static hosting
90+
```
91+
92+
## Contributing
93+
94+
Made by [jo0707](https://github.com/jo0707)
95+
96+
Feel free to contribute to this repository!

app.config.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export default defineAppConfig({
2+
ui: {
3+
primary: "green",
4+
gray: "slate",
5+
button: {
6+
default: {
7+
variant: "outline",
8+
},
9+
base: "transition backdrop-blur-sm",
10+
variant: {
11+
// dont know why {color}-400 not working
12+
outline: "dark:hover:bg-{color}-500 dark:hover:text-gray-800",
13+
soft: "dark:bg-{color}-950 dark:text-{color}-100 dark:hover:bg-{color}-500 dark:hover:text-gray-800",
14+
},
15+
},
16+
},
17+
})

app.vue

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<template>
2+
<div class="relative min-h-screen w-screen overflow-hidden dark:bg-gray-900 font-['DM_Sans']">
3+
<div class="absolute w-screen h-screen opacity-20 z-0 bg-[url('/img/background.svg')]"></div>
4+
5+
<NuxtLayout>
6+
<NuxtPage></NuxtPage>
7+
</NuxtLayout>
8+
9+
<UNotifications />
10+
</div>
11+
</template>
12+
13+
<style>
14+
.page-enter-active,
15+
.page-leave-active {
16+
transition: all 0.4s ease;
17+
}
18+
19+
.page-enter-from,
20+
.page-leave-to {
21+
opacity: 0;
22+
}
23+
24+
.fade-enter-active,
25+
.fade-leave-active {
26+
transition: all 0.3s ease;
27+
}
28+
29+
.fade-enter-from,
30+
.fade-leave-to {
31+
opacity: 0;
32+
}
33+
34+
.scale-enter-active,
35+
.scale-leave-active {
36+
transition: all 0.3s ease;
37+
}
38+
39+
.scale-enter-from,
40+
.scale-leave-to {
41+
scale: 0.6;
42+
}
43+
</style>

bun.lockb

426 KB
Binary file not shown.

components/CorrectAnimation.vue

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<template>
2+
<div>
3+
<Transition name="scale" appear>
4+
<div>
5+
<UIcon class="w-10 h-10 text-green-400" name="i-heroicons-check-circle-solid" />
6+
</div>
7+
</Transition>
8+
<p class="text-lg">Correct!</p>
9+
</div>
10+
</template>

components/IncorrectAnimation.vue

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<template>
2+
<div>
3+
<Transition name="scale" appear>
4+
<div>
5+
<UIcon class="w-10 h-10 text-red-400" name="i-heroicons-x-circle-solid" />
6+
</div>
7+
</Transition>
8+
<p class="text-lg">Incorrect!</p>
9+
</div>
10+
</template>

components/LeaderboardBadge.vue

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<template>
2+
<div class="grid place-content-center">
3+
<div v-if="index == 0" class="grid place-items-center">
4+
<UIcon name="i-heroicons-trophy-solid" class="absolute dark:text-yellow-500/50 animate-ping" />
5+
<UIcon name="i-heroicons-trophy-solid" class="dark:text-yellow-500" />
6+
</div>
7+
<UIcon v-else-if="index == 1" name="i-heroicons-star-solid" class="dark:text-yellow-500" />
8+
<UIcon v-else-if="index == 2" name="i-heroicons-star-solid" class="dark:text-yellow-500" />
9+
<UIcon v-else name="i-heroicons-user-circle-solid" class="dark:text-primary-500" />
10+
</div>
11+
</template>
12+
13+
<script setup lang="ts">
14+
defineProps<{
15+
index: number
16+
}>()
17+
</script>

components/LeaderboardForm.vue

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<template>
2+
<div>
3+
<form @submit.prevent="setLeaderboard" class="flex flex-col gap-2">
4+
<UInput v-model="name" placeholder="Name..." maxlength="32" />
5+
<UButton label="Add to leaderboard" icon="i-heroicons-star-solid" block color="blue" type="submit" />
6+
</form>
7+
</div>
8+
</template>
9+
10+
<script setup lang="ts">
11+
import type { GameMode } from '~/types/Game';
12+
13+
const props = defineProps<{
14+
score: number,
15+
time: number,
16+
mode: GameMode
17+
}>()
18+
const supabase = useSupabase()
19+
const toast = useCustomToast()
20+
const name = ref('')
21+
22+
async function setLeaderboard() {
23+
try {
24+
name.value = name.value.replaceAll(/[</>]/g, '').trim()
25+
26+
await supabase.setLeaderboard(name.value, props.score, props.time, props.mode)
27+
toast.success('Leaderboard added!')
28+
navigateTo('/leaderboard')
29+
} catch (error) {
30+
toast.error('Failed to add leaderboard!', "sorry, hehe :)")
31+
}
32+
}
33+
</script>

components/LeaderboardItem.vue

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<template>
2+
<div class="flex gap-2 px-4 py-2 rounded shadow items-center transition hover:dark:bg-gray-700/50 dark:bg-gray-800/50 backdrop-blur-sm border"
3+
:class="index <= 2 ? 'dark:border-yellow-500' : 'dark:border-primary-500'">
4+
<LeaderboardBadge :index="index" />
5+
6+
<p :class="{ 'dark:text-gray-500': !data.name }">{{ data.name || 'Anonymous player' }}</p>
7+
<div class="grow"></div>
8+
<p>{{ data.score }}</p>
9+
</div>
10+
</template>
11+
12+
<script setup lang="ts">
13+
import type { Database } from '~/types/Database';
14+
15+
const props = defineProps<{
16+
data: Database['public']['Tables']['leaderboard']['Row'],
17+
index: number
18+
}>()
19+
</script>

components/QuestionError.vue

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<template>
2+
<div>
3+
<p>There was an error loading your questions.</p>
4+
<p>{{ data.error }}</p>
5+
</div>
6+
</template>
7+
8+
<script setup lang="ts">
9+
import type { QuestionWrapper } from '~/types/QuestionWrapper';
10+
11+
defineProps<{
12+
data: QuestionWrapper
13+
}>()
14+
</script>

components/QuestionLoading.vue

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<template>
2+
<div>
3+
<p>Loading your questions...</p>
4+
<UProgress animation="carousel" />
5+
</div>
6+
</template>

components/TheLogo.vue

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<template>
2+
<div class="select-none explode text-primary-400">
3+
<span>Q</span>
4+
<span>u</span>
5+
<span>i</span>
6+
<span>z</span>
7+
<span>z</span>
8+
<span>l</span>
9+
<span>e</span>
10+
<span>r</span>
11+
</div>
12+
</template>
13+
14+
<style scoped>
15+
.explode>* {
16+
transition: all 400ms ease;
17+
display: inline-block;
18+
}
19+
20+
.explode:hover>span:nth-child(1) {
21+
transform: translate(-2px, -2px) rotate(-11deg) scale(1.00);
22+
}
23+
24+
.explode:hover>span:nth-child(2) {
25+
transform: translate(1px, 2px) rotate(-9deg) scale(1.00);
26+
}
27+
28+
.explode:hover>span:nth-child(3) {
29+
transform: translate(-2px, -1px) rotate(-3deg) scale(1.00);
30+
}
31+
32+
.explode:hover>span:nth-child(4) {
33+
transform: translate(2px, 3px) rotate(12deg) scale(1.00);
34+
}
35+
36+
.explode:hover>span:nth-child(5) {
37+
transform: translate(-2px, -2px) rotate(7deg) scale(1.00);
38+
}
39+
40+
.explode:hover>span:nth-child(6) {
41+
transform: translate(2px, -3px) rotate(-5deg) scale(1.00);
42+
}
43+
44+
.explode:hover>span:nth-child(7) {
45+
transform: translate(2px, -3px) rotate(-12deg) scale(1.00);
46+
}
47+
48+
.explode:hover>span:nth-child(8) {
49+
transform: translate(1px, -1px) rotate(0deg) scale(1.00);
50+
}
51+
52+
.explode:hover>span:nth-child(9) {
53+
transform: translate(2px, 0px) rotate(5deg) scale(1.00);
54+
opacity: 1;
55+
}
56+
</style>

0 commit comments

Comments
 (0)