add habit leveling/XP system, explicit fail marking for missed habits, and scrollable habit tabs

- Habits and the account now accumulate XP and levels from check streaks, with
  streak/level-up celebration banners and push notifications.
- The "yesterday missed" banner on /today now lets a habit be explicitly marked
  failed (not just checked done), backed by a new HabitLog.status column so
  completion-rate/streak calculations never count a failed day as done.
- The habit management tabs scroll horizontally on narrow screens instead of
  wrapping to two lines, with a pure-CSS edge shadow indicating more content.
This commit is contained in:
2026-07-20 18:18:56 +09:00
parent 4a7ef5cc29
commit 800f42eab1
18 changed files with 665 additions and 42 deletions
@@ -0,0 +1,32 @@
"""add status (done/failed) to habit_log
Revision ID: 0009_add_habit_log_status
Revises: 0008_normalize_habit_enum_casing
Create Date: 2026-07-20
"어제 놓친 습관" 배너에서 사용자가 명시적으로 실패를 확정할 수 있게 하기 위해, "로그 행이 존재하면
그 날 체크 완료"라는 기존 불변식을 유지하면서도 "완료""실패"를 구분해야 한다. status 컬럼을
추가하고 기존 행은 전부 완료였으므로 기본값 'done'으로 채운다.
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
revision: str = "0009_add_habit_log_status"
down_revision: Union[str, None] = "0008_normalize_habit_enum_casing"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.add_column(
"habit_log",
sa.Column("status", sa.String(length=20), nullable=False, server_default="done"),
)
op.create_check_constraint("ck_habit_log_status", "habit_log", "status in ('done','failed')")
def downgrade() -> None:
op.drop_constraint("ck_habit_log_status", "habit_log", type_="check")
op.drop_column("habit_log", "status")