Commit Graph

22 Commits

Author SHA1 Message Date
ca858c2050 Include morning routine in session_update WebSocket broadcast
_broadcast_session was missing morning_routine in its payload, so the
TV dashboard's applySnapshot reset the list to [] whenever a session
started or updated via WebSocket. A page refresh was required to
restore the items since only the REST dashboard endpoint included them.

Now queries MorningRoutineItem via child→user_id and adds the list to
every session_update broadcast, matching the dashboard snapshot.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 22:38:46 -08:00
5cd537a445 Add Morning Routine to Admin and TV greeting state
Adds a per-user Morning Routine item list that appears in the TV
dashboard Activities panel during the "Good Morning" countdown
(before the first block starts).

- morning_routine_items table (auto-created on startup)
- CRUD API at /api/morning-routine (auth-required)
- Items included in the public DashboardSnapshot so TV gets them
  without auth
- Morning Routine section in Admin page (same add/edit/delete UX
  as subject options)
- TV Activities column shows routine items when no block is active,
  switches to subject options once a block starts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 22:19:15 -08:00
b5f4188396 Add strike events to activity log
Record a StrikeEvent row whenever a strike is added or removed,
and surface them in the activity log timeline with timestamp,
child name, and whether the strike was added or removed.

- New strike_events table (auto-created on startup)
- children router records prev/new strikes on every update
- GET /api/logs/strikes and DELETE /api/logs/strikes/:id endpoints
- Log view merges strike entries into the timeline (red dot,
  "✕ Strike added (2/3)" / "↩ Strike removed (1/3)")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 22:07:41 -08:00
f730e9edf9 Show timer remaining per block and fix single-click block switching
- Block list on both dashboards now shows time remaining on each block's
  timer (allocated duration minus elapsed) instead of total duration;
  the active block counts down live every second
- Fix block switching requiring 2 clicks: replace separate pause+start
  requests with a single start request; backend implicitly records a
  pause event for the previous block atomically
- Export blockElapsedCache from store so views can compute per-block
  elapsed for both running and paused blocks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 22:00:23 -08:00
a02876c20d Fix double-click to switch blocks and TV elapsed reset
Double-click fix:
- After awaiting the pause, optimistically set current_block_id and
  isPaused on the store so the UI switches instantly. The subsequent
  WS start event confirms the state without requiring a second click.

TV elapsed reset fix:
- The TV's local cache was empty for blocks paused before it connected,
  so returning to those blocks showed 0 elapsed.
- Backend now computes the block's accumulated elapsed from previous
  start/pause cycles and includes it as block_elapsed_seconds in the
  'start' WS event payload.
- All clients (Dashboard and TV) now use this authoritative server value
  instead of relying solely on the local cache.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 21:33:36 -08:00
3efecfda49 Fix paused timer auto-resuming on page navigation
applySnapshot was always setting isPaused = false and blockStartedAt =
Date.now() regardless of the actual timer state, causing a paused block
to appear running whenever the dashboard was reloaded.

- Add is_paused field to DashboardSnapshot schema
- Dashboard endpoint derives is_paused by checking whether the last
  start/resume/pause event for the current block is a pause
- applySnapshot now reads is_paused from the snapshot instead of
  resetting to false, and only sets blockStartedAt when the block is
  actually running (not paused)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 14:27:05 -08:00
37416436ba Add Day Started event to activity log with template name
- On session start, insert a session_start TimerEvent so the action
  appears in the activity log timeline
- Load DailySession.template via selectinload in the timeline query
  so the template name is available without extra round-trips
- _to_timeline_out maps the template name into block_label for
  session_start events, displaying as "Day started — Template Name"
- Add session_start to EVENT_META on the frontend (🏫 icon)
- Hide the Edit button for session_start entries since changing their
  event type to a block-level action doesn't make sense

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 14:22:07 -08:00
823260cdd8 Add timezone selector to Admin settings with full-stack support
- Add `timezone` column to User model (VARCHAR 64, default UTC) with
  idempotent startup migration
- Expose and persist timezone via PATCH /api/users/me
- Fix TimerEvent.occurred_at serialization to include UTC offset marker
  (+00:00) so JavaScript correctly parses timestamps as UTC
- Add frontend utility (src/utils/time.js) with timezone-aware
  formatTime, getHHMM, getDateInTZ, tzDateTimeToUTC helpers and a
  curated IANA timezone list
- Add Settings section to Admin page with timezone dropdown; saves to
  both the API and localStorage for the unauthenticated TV view
- Update Activity Log to display and edit times in the user's timezone
- Update TV dashboard clock to respect the saved timezone
- Update README: features, setup steps, usage table, WebSocket events

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 14:16:37 -08:00
fef03ec538 Auto-populate activity log from timer events with edit and delete
- New GET /api/logs/timeline endpoint joins TimerEvent with block/subject/session data
- New PATCH and DELETE /api/logs/timeline/{id} endpoints for editing/removing events
- LogView redesigned as a chronological timeline grouped by date
- Edit inline: timer events support type + time correction; notes support text edit
- Delete works for both auto events and manual notes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 18:15:35 -08:00
80109976bf Reset child strikes to 0 when starting a new day session
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 17:21:22 -08:00
44e8f7de7b Move 3 Strikes from Admin to Dashboard, add strikes feature
- Adds strikes (0-3) to Child model with migration
- New PATCH /api/children/{id}/strikes endpoint with WebSocket broadcast
- TV dashboard shows red ✕ marks next to child name when strikes > 0
- 3 Strikes card on Dashboard page (removed from Admin)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 17:20:10 -08:00
7346159745 Sort schedule blocks by start time instead of order_index
Backend queries and model relationship now order by time_start.
Frontend also sorts blocks client-side for reliability across all views.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 16:22:17 -08:00
c9441a9c9a Add subject options and redesign TV dashboard layout
Subject options:
- New subject_options table (auto-created on startup)
- SubjectOut now includes options list; all eager-loading chains updated
- Admin: Options panel per subject with add, inline edit, and delete
- WS broadcast and dashboard API include options in block subject data

TV dashboard:
- Three equal columns: Timer | Activities | Schedule
- Activities column shows current subject's options in large readable text
- Activities area has subject-colored border and tinted background
- Subject name and label displayed correctly using embedded subject data

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 11:18:55 -08:00
1042c20fbb Fix day progress missing on TV dashboard after session start
The WS broadcast payload was missing day_start_time, day_end_time, and
duration_minutes, so applySnapshot nulled them out on session_update.
Now _broadcast_session fetches the template and includes all fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 10:30:50 -08:00
1939b2cc45 Add duration_minutes field to schedule blocks
Separates "recommended time" (time_start/time_end) from actual timer
duration. If duration_minutes is set, the timer counts down from that
value; otherwise falls back to the time_start–time_end window.

- ScheduleBlock model: add nullable duration_minutes INT column
- Startup migration: idempotent ADD COLUMN for existing DBs
- Schemas: duration_minutes in create, update, and out
- TimerDisplay: prefer duration_minutes * 60 over time diff when set
- Admin block forms: Duration (min) input on add and edit forms

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 10:23:30 -08:00
a019e2dda5 Add inline block editing to schedule templates
- Backend: PATCH /api/schedules/{template_id}/blocks/{block_id} endpoint
- Frontend: Edit button on each block row expands an inline form
  pre-filled with current subject, times, and label; saves via PATCH

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 10:15:19 -08:00
3e7ff2a50b Add time-based day progress bar to dashboards
Replaces block-count progress with a wall-clock progress bar driven by
configurable day start/end hours on each schedule template.

- ScheduleTemplate: add day_start_time / day_end_time (TIME, nullable)
- Startup migration: idempotent ALTER TABLE for existing DBs
- Dashboard snapshot: includes day_start_time / day_end_time from template
- Admin → Schedules: time pickers in block editor to set day hours
- Dashboard view: time-based progress bar with start/current/end labels
- TV view: full-width day progress strip between header and main content

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 10:08:07 -08:00
d43791f965 Fix timer reset on refresh and sync between dashboard and TV view
- Backend computes block_elapsed_seconds server-side from timer_events
- Store tracks blockStartedAt (ms) + blockElapsedOffset (seconds) instead
  of a client-side counter; updated correctly on start/pause/resume/end
- TimerDisplay derives elapsed from store props so both views always agree
- Add compact timer display to dashboard session card
- Add isPaused/pause-resume logic to dashboard Pause/Resume buttons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 00:16:29 -08:00
ad10f9bc61 Fix End Day not updating dashboard without refresh
Broadcast is_active in WS timer payload so the frontend can immediately
clear the session when the backend marks it complete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 23:43:04 -08:00
15ce6dbb4d Fix child delete failing due to SQLAlchemy nullifying FK before DB cascade
Add passive_deletes=True to daily_sessions and activity_logs relationships
so the ORM defers to MySQL's ON DELETE CASCADE instead of trying to SET NULL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 23:32:17 -08:00
781e179090 Fix backend deps and apply theme to all Cancel buttons
- Pin bcrypt==3.2.2 to fix passlib bcrypt backend detection error
- Add email-validator==2.2.0 required by Pydantic EmailStr
- Add btn-sm class to Cancel buttons in Admin, Dashboard, Log, Schedule views

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 23:24:39 -08:00
417b3adfe8 Initial project scaffold
Full-stack homeschool web app with FastAPI backend, Vue 3 frontend,
MySQL database, and Docker Compose orchestration. Includes JWT auth,
WebSocket real-time TV dashboard, schedule builder, activity logging,
and multi-child support.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 22:56:56 -08:00