Add Reset button and move End Day to right side of actions bar

Reset clears the current block's elapsed time to zero and immediately
starts the timer. A shared compute_block_elapsed() utility (utils/timer.py)
handles the elapsed calculation in both the sessions and dashboard routers,
and correctly treats "reset" events as zero-elapsed restart markers so
page reloads after a reset show accurate times.

Layout: Start/Pause/Resume/Reset are grouped on the left; End Day sits
on the right via justify-content: space-between.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 00:17:02 -08:00
parent cc599603cf
commit 1420d57e7e
6 changed files with 104 additions and 86 deletions

View File

@@ -2,7 +2,7 @@
Public dashboard endpoint — no authentication required.
Used by the TV view to get the initial session snapshot before WebSocket connects.
"""
from datetime import date, datetime
from datetime import date
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
@@ -16,6 +16,7 @@ from app.models.schedule import ScheduleBlock, ScheduleTemplate
from app.models.subject import Subject # noqa: F401 — needed for selectinload chain
from app.models.session import DailySession, TimerEvent
from app.schemas.session import DashboardSnapshot
from app.utils.timer import compute_block_elapsed
router = APIRouter(prefix="/api/dashboard", tags=["dashboard"])
@@ -74,29 +75,9 @@ async def get_dashboard(child_id: int, db: AsyncSession = Depends(get_db)):
# Compute elapsed seconds and paused state for the current block from timer_events
is_paused = False
if session and session.current_block_id:
tick_result = await db.execute(
select(TimerEvent)
.where(
TimerEvent.session_id == session.id,
TimerEvent.block_id == session.current_block_id,
TimerEvent.event_type.in_(["start", "resume", "pause"]),
)
.order_by(TimerEvent.occurred_at)
block_elapsed_seconds, is_paused = await compute_block_elapsed(
db, session.id, session.current_block_id
)
tick_events = tick_result.scalars().all()
last_start = None
elapsed = 0.0
for e in tick_events:
if e.event_type in ("start", "resume"):
last_start = e.occurred_at
elif e.event_type == "pause" and last_start:
elapsed += (e.occurred_at - last_start).total_seconds()
last_start = None
if last_start:
elapsed += (datetime.utcnow() - last_start).total_seconds()
block_elapsed_seconds = int(elapsed)
# Paused if the last tick event was a pause (last_start is None but events exist)
is_paused = bool(tick_events) and tick_events[-1].event_type == "pause"
routine_result = await db.execute(
select(MorningRoutineItem)