Add break time feature to schedule blocks
- Admin: per-block "Break Time" checkbox + duration (min) setting; new Break Activities section (global list, same pattern as Morning Routine) - Dashboard: break timer section appears on blocks with break enabled; Start/Pause/Resume/Reset controls work independently of the main timer - TV: left column switches to amber break badge + countdown during break; center column shows configurable Break Activities list - Backend: break_time_enabled/break_time_minutes columns on schedule_blocks (auto-migrated on startup); break_activity_items table + CRUD router; break timer events (break_start/pause/resume/reset) stored as TimerEvents and broadcast via WebSocket; break_activities included in dashboard snapshot and session_update broadcast Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,12 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
const dayStartTime = ref(null) // "HH:MM:SS" string or null
|
||||
const dayEndTime = ref(null) // "HH:MM:SS" string or null
|
||||
const morningRoutine = ref([]) // list of text strings shown during greeting state
|
||||
const breakActivities = ref([]) // list of text strings shown during break time
|
||||
// Break timer state (per-block break time at end of block)
|
||||
const isBreakMode = ref(false) // currently in break time
|
||||
const breakStartedAt = ref(null) // Date.now() ms when break counting started
|
||||
const breakElapsedOffset = ref(0) // break seconds already elapsed
|
||||
const breakElapsedCache = ref({}) // blockId → total break elapsed seconds
|
||||
|
||||
const currentBlock = computed(() =>
|
||||
session.value?.current_block_id
|
||||
@@ -42,6 +48,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
dayStartTime.value = snapshot.day_start_time || null
|
||||
dayEndTime.value = snapshot.day_end_time || null
|
||||
morningRoutine.value = snapshot.morning_routine || []
|
||||
breakActivities.value = snapshot.break_activities || []
|
||||
// Restore elapsed time from server-computed value and seed the per-block cache
|
||||
const serverElapsed = snapshot.block_elapsed_seconds || 0
|
||||
if (snapshot.session?.current_block_id) {
|
||||
@@ -54,6 +61,11 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
blockElapsedOffset.value = 0
|
||||
blockStartedAt.value = null
|
||||
}
|
||||
// Reset break state on snapshot (not persisted across page loads)
|
||||
isBreakMode.value = false
|
||||
breakStartedAt.value = null
|
||||
breakElapsedOffset.value = 0
|
||||
breakElapsedCache.value = {}
|
||||
}
|
||||
|
||||
function applyWsEvent(event) {
|
||||
@@ -76,6 +88,39 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
blockElapsedCache.value = {}
|
||||
dayStartTime.value = null
|
||||
dayEndTime.value = null
|
||||
isBreakMode.value = false
|
||||
breakStartedAt.value = null
|
||||
breakElapsedOffset.value = 0
|
||||
breakElapsedCache.value = {}
|
||||
return
|
||||
}
|
||||
// Break timer events
|
||||
if (event.event === 'break_start') {
|
||||
const elapsed = event.break_elapsed_seconds ?? breakElapsedCache.value[event.block_id] ?? 0
|
||||
breakElapsedOffset.value = elapsed
|
||||
if (event.block_id) breakElapsedCache.value[event.block_id] = elapsed
|
||||
breakStartedAt.value = Date.now()
|
||||
isBreakMode.value = true
|
||||
}
|
||||
if (event.event === 'break_pause') {
|
||||
if (breakStartedAt.value) {
|
||||
breakElapsedOffset.value += Math.floor((Date.now() - breakStartedAt.value) / 1000)
|
||||
}
|
||||
if (event.block_id) breakElapsedCache.value[event.block_id] = breakElapsedOffset.value
|
||||
breakStartedAt.value = null
|
||||
isBreakMode.value = true
|
||||
}
|
||||
if (event.event === 'break_resume') {
|
||||
breakStartedAt.value = Date.now()
|
||||
isBreakMode.value = true
|
||||
}
|
||||
if (event.event === 'break_reset') {
|
||||
if (event.block_id) breakElapsedCache.value[event.block_id] = 0
|
||||
breakElapsedOffset.value = 0
|
||||
breakStartedAt.value = Date.now()
|
||||
isBreakMode.value = true
|
||||
}
|
||||
if (['break_start', 'break_pause', 'break_resume', 'break_reset'].includes(event.event)) {
|
||||
return
|
||||
}
|
||||
// Pause — accumulate elapsed, save to cache, stop counting
|
||||
@@ -101,6 +146,12 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
}
|
||||
blockStartedAt.value = Date.now()
|
||||
isPaused.value = false
|
||||
// Switching to a new block clears break mode
|
||||
if (event.block_id !== event.current_block_id || !isBreakMode.value) {
|
||||
isBreakMode.value = false
|
||||
breakStartedAt.value = null
|
||||
breakElapsedOffset.value = breakElapsedCache.value[event.block_id] ?? 0
|
||||
}
|
||||
}
|
||||
// Reset — clear elapsed to 0 and start counting immediately
|
||||
if (event.event === 'reset') {
|
||||
@@ -121,6 +172,10 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
if (event.block_id) blockElapsedCache.value[event.block_id] = elapsed
|
||||
blockStartedAt.value = null
|
||||
isPaused.value = true
|
||||
// Switching blocks clears break mode
|
||||
isBreakMode.value = false
|
||||
breakStartedAt.value = null
|
||||
breakElapsedOffset.value = breakElapsedCache.value[event.block_id] ?? 0
|
||||
}
|
||||
// Resume — continue from where we left off
|
||||
if (event.event === 'resume') {
|
||||
@@ -212,6 +267,41 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
sendTimerAction(sessionId, 'start', session.value.current_block_id)
|
||||
}
|
||||
|
||||
// Break timer actions
|
||||
function startBreak(sessionId) {
|
||||
if (!session.value?.current_block_id) return
|
||||
const blockId = session.value.current_block_id
|
||||
isBreakMode.value = true
|
||||
breakElapsedOffset.value = breakElapsedCache.value[blockId] ?? 0
|
||||
breakStartedAt.value = Date.now()
|
||||
sendTimerAction(sessionId, 'break_start', blockId)
|
||||
}
|
||||
|
||||
function pauseBreak(sessionId) {
|
||||
if (!session.value?.current_block_id) return
|
||||
if (breakStartedAt.value) {
|
||||
breakElapsedOffset.value += Math.floor((Date.now() - breakStartedAt.value) / 1000)
|
||||
}
|
||||
breakStartedAt.value = null
|
||||
sendTimerAction(sessionId, 'break_pause', session.value.current_block_id)
|
||||
}
|
||||
|
||||
function resumeBreak(sessionId) {
|
||||
if (!session.value?.current_block_id) return
|
||||
breakStartedAt.value = Date.now()
|
||||
sendTimerAction(sessionId, 'break_resume', session.value.current_block_id)
|
||||
}
|
||||
|
||||
function resetBreak(sessionId) {
|
||||
if (!session.value?.current_block_id) return
|
||||
const blockId = session.value.current_block_id
|
||||
breakElapsedCache.value[blockId] = 0
|
||||
breakElapsedOffset.value = 0
|
||||
breakStartedAt.value = Date.now()
|
||||
isBreakMode.value = true
|
||||
sendTimerAction(sessionId, 'break_reset', blockId)
|
||||
}
|
||||
|
||||
// Reset the current block's timer to 0 and start counting immediately.
|
||||
function resetCurrentBlock(sessionId) {
|
||||
if (!session.value?.current_block_id) return
|
||||
@@ -235,6 +325,11 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
dayStartTime,
|
||||
dayEndTime,
|
||||
morningRoutine,
|
||||
breakActivities,
|
||||
isBreakMode,
|
||||
breakStartedAt,
|
||||
breakElapsedOffset,
|
||||
breakElapsedCache,
|
||||
currentBlock,
|
||||
progressPercent,
|
||||
applySnapshot,
|
||||
@@ -246,5 +341,9 @@ export const useScheduleStore = defineStore('schedule', () => {
|
||||
selectBlock,
|
||||
startCurrentBlock,
|
||||
resetCurrentBlock,
|
||||
startBreak,
|
||||
pauseBreak,
|
||||
resumeBreak,
|
||||
resetBreak,
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user