Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions app/components/reader/readerHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script setup lang="ts">
import ReaderStats from './readerStats.vue'

defineProps<{
"tickets": Number,
"xp": Number
}>()
</script>

<template>
<header class="max-w-4xl mx-auto flex justify-between items-center mb-8 px-2 relative z-[200]">
<div class="flex items-center gap-3">
<div class="w-12 h-12 rounded-xl flex items-center justify-center text-white font-heading font-bold text-2xl shadow-lg"
style="background: var(--brand-indigo)">L</div>
<div class="flex flex-col">
<span class="font-heading font-bold text-2xl tracking-tight leading-none" style="color: var(--brand-dark)">
Reading<span style="color: var(--brand-indigo)">Huddle</span>
</span>
<span class="text-[10px] font-bold uppercase tracking-widest" style="color: var(--brand-mint)">Reading Buddy</span>
</div>
</div>

<div>
<ReaderStats :tickets="tickets" :xp="xp"/>
</div>
</header>
</template>
64 changes: 64 additions & 0 deletions app/components/reader/readerStats.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script setup lang="ts">

defineProps<{
'tickets': Number,
'xp': Number
}>()

// ── Click-triggered badge animations ──
const xpClicked = ref(false)
const ticketClicked = ref(false)
const burstCoins = ref<{id:number; tx:number; ty:number}[]>([])
const flyTickets = ref<number[]>([])

function triggerXpClick() {
xpClicked.value = true
burstCoins.value = [
{ id: Date.now(), tx: -32, ty: -40 },
{ id: Date.now() + 1, tx: 0, ty: -48 },
{ id: Date.now() + 2, tx: 32, ty: -40 },
]
setTimeout(() => { xpClicked.value = false; burstCoins.value = [] }, 800)
}


function triggerTicketClick() {
ticketClicked.value = true
flyTickets.value = [Date.now()]
setTimeout(() => { ticketClicked.value = false; flyTickets.value = [] }, 1000)
}
</script>
<template>
<!-- XP + Tickets badge + settings -->
<div class="flex items-center gap-3">
<!-- XP badge — click triggers star spin + coin burst -->
<div
class="relative bg-white/90 backdrop-blur-md px-4 py-2 rounded-xl shadow-xl border-2 flex items-center gap-3 cursor-pointer select-none transition-all"
style="border-color: rgba(245,158,11,0.3)"
:class="(xpClicked || ticketClicked) ? 'scale-90 transition-transform duration-100' : 'transition-transform duration-100'"
@click="triggerXpClick"
>
<span class="text-lg" :class="xpClicked ? 'animate-star-spin' : ''">🪙</span>
<span class="font-heading font-bold text-amber-600">{{xp}}</span>
<span class="text-gray-300">|</span>
<span class="text-lg" :class="ticketClicked ? 'animate-ticket-wobble' : ''" @click.stop="triggerTicketClick">🎟️</span>
<span class="font-heading font-bold" style="color:var(--brand-mint)">{{tickets}}</span>
<span v-for="c in burstCoins" :key="c.id" class="absolute text-sm animate-coin-burst"
:style="`--tx:${c.tx}px;--ty:${c.ty}px;left:50%;top:50%;`">🪙</span>
<Transition name="box-pop">
<span
v-if="ticketClicked"
class="absolute -bottom-15 left-1/2 text-2xl animate-box-shake pointer-events-none">
📦
</span>
</Transition>
<span v-for="id in flyTickets" :key="id" class="absolute text-lg animate-ticket-fly pointer-events-none"
style="left:50%;top:50%;transform:translateX(-50%) translateY(-50%)">🎟️</span>
</div>

<NuxtLink to="/reader/settings"
class="w-14 h-14 bg-white/90 backdrop-blur-md rounded-xl flex items-center justify-center text-2xl transition-all border-2 border-white shadow-xl hover:scale-110 active:scale-95 hover:text-[var(--brand-indigo)]">
⚙️
</NuxtLink>
</div>
</template>
75 changes: 2 additions & 73 deletions app/pages/reader/home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { onMounted } from 'vue'
import { authClient } from '~/utils/auth-client'
import type { Student, Announcement} from '~~/prisma/generated/client'
import ReaderHeader from '../../components/reader/readerHeader.vue'

definePageMeta({ ssr: false })

Expand Down Expand Up @@ -34,28 +35,6 @@ const themeClass = computed(() => {
return `reader-app ${t} ${d}`.trim()
})

// ── Click-triggered badge animations ──
const xpClicked = ref(false)
const ticketClicked = ref(false)
const burstCoins = ref<{id:number; tx:number; ty:number}[]>([])
const flyTickets = ref<number[]>([])

function triggerXpClick() {
xpClicked.value = true
burstCoins.value = [
{ id: Date.now(), tx: -32, ty: -40 },
{ id: Date.now() + 1, tx: 0, ty: -48 },
{ id: Date.now() + 2, tx: 32, ty: -40 },
]
setTimeout(() => { xpClicked.value = false; burstCoins.value = [] }, 800)
}

function triggerTicketClick() {
ticketClicked.value = true
flyTickets.value = [Date.now()]
setTimeout(() => { ticketClicked.value = false; flyTickets.value = [] }, 1000)
}

//TODO 3
//Announcement fetch all currently-active announcements
const { data: announcements } = await useFetch<any[] | null>('/api/announcement?active=true')
Expand Down Expand Up @@ -94,57 +73,7 @@ const completionMessage = computed(() => {

<template>
<div :class="themeClass" :style="`font-size: ${settings.fontSize * 16}px`" class="pb-32 px-4 pt-4 min-h-screen">

<!-- ── TOP BAR ── -->
<header class="max-w-4xl mx-auto flex justify-between items-center mb-8 px-2 relative z-[200]">
<div class="flex items-center gap-3">
<div class="w-12 h-12 rounded-xl flex items-center justify-center text-white font-heading font-bold text-2xl shadow-lg"
style="background: var(--brand-indigo)">L</div>
<div class="flex flex-col">
<span class="font-heading font-bold text-2xl tracking-tight leading-none" style="color: var(--brand-dark)">
Reading<span style="color: var(--brand-indigo)">Huddle</span>
</span>
<span class="text-[10px] font-bold uppercase tracking-widest" style="color: var(--brand-mint)">Reading Buddy</span>
</div>
</div>

<!-- XP + Tickets badge + settings -->
<div class="flex items-center gap-3">
<!-- XP badge — click triggers star spin + coin burst -->
<div
class="relative bg-white/90 backdrop-blur-md px-4 py-2 rounded-xl shadow-xl border-2 flex items-center gap-3 cursor-pointer select-none transition-all"
style="border-color: rgba(245,158,11,0.3)"
:class="(xpClicked || ticketClicked) ? 'scale-90 transition-transform duration-100' : 'transition-transform duration-100'"
@click="triggerXpClick"
>
<span class="text-lg" :class="xpClicked ? 'animate-star-spin' : ''">🪙</span>
<span class="font-heading font-bold text-amber-600">{{ Number(student?.exp) }}</span>
<span class="text-gray-300">|</span>
<span class="text-lg" :class="ticketClicked ? 'animate-ticket-wobble' : ''" @click.stop="triggerTicketClick">🎟️</span>
<span class="font-heading font-bold" style="color:var(--brand-mint)">{{ tickets }}</span>
<span v-for="c in burstCoins" :key="c.id" class="absolute text-sm animate-coin-burst"
:style="`--tx:${c.tx}px;--ty:${c.ty}px;left:50%;top:50%;`">🪙</span>
<Transition name="box-pop">
<span
v-if="ticketClicked"
class="absolute -bottom-15 left-1/2 text-2xl animate-box-shake pointer-events-none">
📦
</span>
</Transition>
<span v-for="id in flyTickets" :key="id" class="absolute text-lg animate-ticket-fly pointer-events-none"
style="left:50%;top:50%;transform:translateX(-50%) translateY(-50%)">🎟️</span>


</div>

<!-- Settings button TODO 7-->
<NuxtLink
to="/reader/settings"
class="w-14 h-14 bg-white/90 backdrop-blur-md rounded-xl flex items-center justify-center text-2xl transition-all border-2 border-white shadow-xl hover:scale-110 active:scale-95"
style="hover:color: var(--brand-indigo)"
>⚙️</NuxtLink>
</div>
</header>
<ReaderHeader v-if="student" :tickets="tickets" :xp="student.exp"/>

<!-- ── MAIN CONTENT ── -->
<main class="max-w-4xl mx-auto min-h-[60vh]">
Expand Down