Add full SkinsPins application
- FastAPI + SQLite + Alpine.js + HTMX PWA for Bingo Bango Bongo golf scoring - Passwordless magic link auth with email (aiosmtplib) and admin invite system - Real-time score entry via WebSocket with drag-and-drop player ordering - Course management with per-hole par and stroke index - Scorecard with golf score markers (birdie, eagle, bogey, etc.) - Two-step new game form with tee selection per player - Dashboard with stats, live games social stream, and recent game history - Game summary view (read-only scorecard with leaderboard) - User profile pages with win stats - PWA install support with generated PNG icons - Admin panel for users, courses, games, and invitations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
from fastapi import APIRouter, Depends, Form, Request, UploadFile, File
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
import aiosqlite
|
||||
|
||||
from app.database import get_db
|
||||
from app.dependencies import get_current_user
|
||||
from app.templates_config import templates
|
||||
from app.avatar import save_avatar
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
AVATARS = ["🏌️", "⛳", "🦅", "🐦", "🏆", "🎯", "🌟", "🔥", "💪", "🦁", "🐯", "🦊"]
|
||||
|
||||
|
||||
@router.get("/onboarding", response_class=HTMLResponse)
|
||||
async def onboarding_page(request: Request, user: dict = Depends(get_current_user)):
|
||||
if user["is_onboarded"]:
|
||||
return RedirectResponse("/", status_code=302)
|
||||
return templates.TemplateResponse(
|
||||
"onboarding/setup.html", {"request": request, "user": user, "avatars": AVATARS}
|
||||
)
|
||||
|
||||
|
||||
@router.post("/onboarding", response_class=HTMLResponse)
|
||||
async def onboarding_submit(
|
||||
request: Request,
|
||||
name: str = Form(...),
|
||||
handicap: float = Form(...),
|
||||
avatar_emoji: str = Form(""),
|
||||
avatar_file: UploadFile = File(None),
|
||||
user: dict = Depends(get_current_user),
|
||||
db: aiosqlite.Connection = Depends(get_db),
|
||||
):
|
||||
errors = {}
|
||||
name = name.strip()
|
||||
if not name:
|
||||
errors["name"] = "Name is required."
|
||||
if not (0.0 <= handicap <= 54.0):
|
||||
errors["handicap"] = "Handicap must be between 0 and 54."
|
||||
|
||||
avatar = None
|
||||
if avatar_file and avatar_file.filename:
|
||||
avatar, err = await save_avatar(avatar_file, user["id"])
|
||||
if err:
|
||||
errors["avatar"] = err
|
||||
elif avatar_emoji in AVATARS:
|
||||
avatar = avatar_emoji
|
||||
else:
|
||||
errors["avatar"] = "Please upload a photo or select an emoji."
|
||||
|
||||
if errors:
|
||||
return templates.TemplateResponse(
|
||||
"onboarding/setup.html",
|
||||
{"request": request, "user": user, "avatars": AVATARS, "errors": errors,
|
||||
"values": {"name": name, "handicap": handicap, "avatar_emoji": avatar_emoji}},
|
||||
)
|
||||
|
||||
await db.execute(
|
||||
"UPDATE users SET name = ?, handicap = ?, avatar = ?, is_onboarded = 1 WHERE id = ?",
|
||||
(name, handicap, avatar, user["id"]),
|
||||
)
|
||||
await db.commit()
|
||||
return RedirectResponse("/", status_code=302)
|
||||
Reference in New Issue
Block a user