diff --git a/app/routers/api.py b/app/routers/api.py index 66206f6..e76a625 100644 --- a/app/routers/api.py +++ b/app/routers/api.py @@ -8,6 +8,7 @@ from __future__ import annotations import calendar as _cal import re from datetime import date, datetime, timedelta, timezone +from typing import Literal from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, Request, UploadFile from fastapi.responses import HTMLResponse, JSONResponse @@ -809,3 +810,33 @@ async def chat( "prompt_tokens": result.prompt_tokens, "completion_tokens": result.completion_tokens, } + + +# --------------------------------------------------------------------------- +# Settings — digest preferences +# --------------------------------------------------------------------------- + + +class DigestPrefsIn(BaseModel): + opt_in: bool + tone: Literal["NOVICE", "INTERMEDIATE"] + + +class DigestPrefsOut(BaseModel): + opt_in: bool + tone: str + + +@router.patch("/settings/digest", response_model=DigestPrefsOut) +async def patch_digest_prefs( + payload: DigestPrefsIn, + principal: CurrentUser = Depends(require_token), + session: AsyncSession = Depends(get_session), +) -> DigestPrefsOut: + if principal.user is None: + # Admin bearer-token path — no per-user row to persist to. + raise HTTPException(status_code=400, detail="no_user_context") + principal.user.email_digest_opt_in = payload.opt_in + principal.user.digest_tone = payload.tone + await session.commit() + return DigestPrefsOut(opt_in=payload.opt_in, tone=payload.tone) diff --git a/app/routers/pages.py b/app/routers/pages.py index a00bf56..7790d18 100644 --- a/app/routers/pages.py +++ b/app/routers/pages.py @@ -11,7 +11,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.auth import CurrentUser, maybe_current_user, require_auth, require_token from app.config import get_settings, load_groups from app.db import get_session -from app.models import Referral, StrategicLog, User +from app.models import EmailSend, Referral, StrategicLog, User from app.services.access import paid_status from app.services.referral_service import assign_code_if_missing from app.templates_env import templates @@ -147,6 +147,13 @@ async def settings_page( invite_url = str(request.url_for("login_page")) + f"?ref={user.referral_code}" + last_email_send = (await session.execute( + select(EmailSend) + .where(EmailSend.user_id == user.id) + .order_by(desc(EmailSend.sent_at)) + .limit(1) + )).scalar_one_or_none() + return templates.TemplateResponse( request, "settings.html", { @@ -155,5 +162,6 @@ async def settings_page( "pending_count": int(pending_count), "converted_count": int(converted_count), "paid": paid_status(user), + "last_email_send": last_email_send, }, ) diff --git a/app/templates/settings.html b/app/templates/settings.html index bab5cb9..466ffba 100644 --- a/app/templates/settings.html +++ b/app/templates/settings.html @@ -96,6 +96,71 @@ + {# --- Email digests block ------------------------------------------ #} +
+ Editorial commentary delivered to your inbox. Daily for paid (Mon–Sat) plus the Sunday recap; free tier gets the Sunday recap. +
+ +