Favorites and the question libraries in one place. Card and Table views with the choice remembered, sort by last used / created / name / size with a direction control, a count line, and a search over name and date. Favorites leads as a fixed row: it is the one shelf nobody made and everybody has, so it cannot be renamed or deleted. Sorted by when each was last used, not when it was made — the order things were created in is nobody's mental model of their own shelf. A library nobody has opened falls back to its age, because it is newer to the learner than it is to the database. That needed `user_collections.last_used_at`: null on every existing row, since backfilling from created_at would invent a use that never happened. A shelf opens in place rather than linking away. The obvious link would have been /questions?collection=N, and there is no page there that reads it — the old bank browser was dismantled — so the card would have led nowhere. Questions can be taken back out from the open shelf, and any shelf can be sat as a session through the existing explicit_ids builder. The ⋯ menu moved out of QuizPage into components/MoreMenu; the player keeps its own look and its own children through className props. It no longer closes on any click inside, which the player's feedback form and share dialog were relying on by accident. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
25 lines
1.1 KiB
Python
25 lines
1.1 KiB
Python
from datetime import datetime
|
|
|
|
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, UniqueConstraint
|
|
from app.database import Base
|
|
|
|
|
|
class UserCollection(Base):
|
|
__tablename__ = "user_collections"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
|
title = Column(String(200), nullable=False)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
# When it was last opened or added to. Null means never since this was
|
|
# recorded, which is not the same claim as "never used".
|
|
last_used_at = Column(DateTime, nullable=True)
|
|
|
|
|
|
class UserCollectionQuestion(Base):
|
|
__tablename__ = "user_collection_questions"
|
|
__table_args__ = (UniqueConstraint("collection_id", "question_id", name="uq_collection_question"),)
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
collection_id = Column(Integer, ForeignKey("user_collections.id", ondelete="CASCADE"), nullable=False)
|
|
question_id = Column(Integer, ForeignKey("questions.id", ondelete="CASCADE"), nullable=False)
|