Add greeting and countdown timer to TV dashboard before session starts

Shows "Good Morning! Ready to start school?" placeholder when a session
is active but no block has been selected, along with a live countdown
to the first scheduled block's start time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 16:57:19 -08:00
parent 7346159745
commit 4b49605ed1

View File

@@ -26,7 +26,15 @@
<!-- Active session --> <!-- Active session -->
<div v-else class="tv-main"> <div v-else class="tv-main">
<!-- Left: timer --> <!-- Left: timer -->
<div class="tv-timer-col" v-if="scheduleStore.currentBlock"> <div class="tv-timer-col tv-greeting-col" v-if="!scheduleStore.currentBlock">
<div class="tv-greeting">Good Morning!</div>
<div class="tv-greeting-sub">Ready to start school?</div>
<div v-if="firstBlockCountdown !== null" class="tv-countdown-wrap">
<div class="tv-countdown-label">First block starts in</div>
<div class="tv-countdown">{{ firstBlockCountdown }}</div>
</div>
</div>
<div class="tv-timer-col" v-else>
<div class="tv-subject-badge" :style="{ background: currentSubjectColor }"> <div class="tv-subject-badge" :style="{ background: currentSubjectColor }">
{{ currentSubjectIcon }} {{ currentSubjectName }} {{ currentSubjectIcon }} {{ currentSubjectName }}
</div> </div>
@@ -129,6 +137,23 @@ const dayProgressPercent = computed(() => {
return Math.max(0, Math.min(100, Math.round((nowMin - start) / (end - start) * 100))) return Math.max(0, Math.min(100, Math.round((nowMin - start) / (end - start) * 100)))
}) })
// Countdown to first block
const firstBlockCountdown = computed(() => {
const first = scheduleStore.blocks[0]
if (!first?.time_start) return null
const [h, m, s] = first.time_start.split(':').map(Number)
const target = new Date(now.value)
target.setHours(h, m, s || 0, 0)
const diffMs = target - now.value
if (diffMs <= 0) return null
const totalSec = Math.floor(diffMs / 1000)
const hours = Math.floor(totalSec / 3600)
const mins = Math.floor((totalSec % 3600) / 60)
const secs = totalSec % 60
if (hours > 0) return `${hours}:${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`
return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`
})
// Subject display helpers // Subject display helpers
const currentSubjectColor = computed(() => { const currentSubjectColor = computed(() => {
const block = scheduleStore.currentBlock const block = scheduleStore.currentBlock
@@ -228,6 +253,47 @@ onMounted(async () => {
text-align: center; text-align: center;
} }
.tv-greeting-col {
justify-content: center;
gap: 1rem;
}
.tv-greeting {
font-size: 3rem;
font-weight: 700;
color: #818cf8;
text-align: center;
}
.tv-greeting-sub {
font-size: 1.6rem;
color: #64748b;
text-align: center;
}
.tv-countdown-wrap {
margin-top: 1.5rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.4rem;
}
.tv-countdown-label {
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #475569;
}
.tv-countdown {
font-size: 4rem;
font-weight: 300;
font-variant-numeric: tabular-nums;
color: #c7d2fe;
letter-spacing: 0.05em;
}
.tv-block-notes { .tv-block-notes {
font-size: 1rem; font-size: 1rem;
color: #94a3b8; color: #94a3b8;