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>
This commit is contained in:
2026-02-28 11:18:55 -08:00
parent c12f07daa3
commit c9441a9c9a
11 changed files with 375 additions and 51 deletions

View File

@@ -2,7 +2,7 @@
from app.models.base import Base, TimestampMixin
from app.models.user import User
from app.models.child import Child
from app.models.subject import Subject
from app.models.subject import Subject, SubjectOption
from app.models.schedule import ScheduleTemplate, ScheduleBlock
from app.models.session import DailySession, TimerEvent, TimerEventType
from app.models.activity import ActivityLog
@@ -13,6 +13,7 @@ __all__ = [
"User",
"Child",
"Subject",
"SubjectOption",
"ScheduleTemplate",
"ScheduleBlock",
"DailySession",

View File

@@ -1,4 +1,4 @@
from sqlalchemy import String, Boolean, ForeignKey
from sqlalchemy import String, Boolean, ForeignKey, Text, Integer
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import Base, TimestampMixin
@@ -20,3 +20,20 @@ class Subject(TimestampMixin, Base):
activity_logs: Mapped[list["ActivityLog"]] = relationship( # noqa: F821
"ActivityLog", back_populates="subject"
)
options: Mapped[list["SubjectOption"]] = relationship(
"SubjectOption", back_populates="subject", cascade="all, delete-orphan",
order_by="SubjectOption.order_index"
)
class SubjectOption(Base):
__tablename__ = "subject_options"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
subject_id: Mapped[int] = mapped_column(
ForeignKey("subjects.id", ondelete="CASCADE"), nullable=False
)
text: Mapped[str] = mapped_column(Text, nullable=False)
order_index: Mapped[int] = mapped_column(Integer, default=0)
subject: Mapped["Subject"] = relationship("Subject", back_populates="options")