Update redirect paths and enhance user context in templates and views

This commit is contained in:
Christian Krakau-Louis
2025-04-14 05:54:21 +02:00
parent 690065f788
commit e952630295
8 changed files with 189 additions and 37 deletions
+2 -2
View File
@@ -90,10 +90,10 @@ app.include_router(leaderboard.router, prefix="/leaderboard", tags=["Leaderboard
app.include_router(dashboard.router, prefix="/dashboard", tags=["Dashboard"])
app.include_router(static.router, tags=["Static"]) # Include the static router
# Add a direct route for /scan that redirects to /redeem/scan
# Add a direct route for /scan that redirects to /dashboard/scan
@app.get("/scan")
async def scan_redirect():
return RedirectResponse("/redeem/scan", status_code=303)
return RedirectResponse("/dashboard/scan", status_code=303)
# Add convenience routes for auth paths
@app.get("/login")
+3 -3
View File
@@ -68,7 +68,7 @@
<a href="/" class="hover:text-golden-ale transition">Home</a>
<a href="/teams" class="hover:text-golden-ale transition">Teams</a>
<a href="/leaderboard" class="hover:text-golden-ale transition">Leaderboard</a>
<a href="/scan" class="hover:text-golden-ale transition">Scan QR Code</a>
<a href="/dashboard/scan" class="hover:text-golden-ale transition">Scan QR Code</a>
{% if user %}
<div class="relative dropdown">
@@ -114,7 +114,7 @@
<a href="/" class="hover:text-golden-ale transition py-2">Home</a>
<a href="/teams" class="hover:text-golden-ale transition py-2">Teams</a>
<a href="/leaderboard" class="hover:text-golden-ale transition py-2">Leaderboard</a>
<a href="/scan" class="hover:text-golden-ale transition py-2">Scan QR Code</a>
<a href="/dashboard/scan" class="hover:text-golden-ale transition py-2">Scan QR Code</a>
{% if user %}
<a href="/profile" class="hover:text-golden-ale transition py-2">Profile</a>
<a href="/dashboard" class="hover:text-golden-ale transition py-2">Dashboard</a>
@@ -154,7 +154,7 @@
<li><a href="/" class="hover:text-golden-ale transition">Home</a></li>
<li><a href="/teams" class="hover:text-golden-ale transition">Teams</a></li>
<li><a href="/leaderboard" class="hover:text-golden-ale transition">Leaderboard</a></li>
<li><a href="/scan" class="hover:text-golden-ale transition">Scan QR Code</a></li>
<li><a href="/dashboard/scan" class="hover:text-golden-ale transition">Scan QR Code</a></li>
</ul>
</div>
+4 -2
View File
@@ -1,6 +1,6 @@
{% extends "base.html" %}
{% block content %}
<div class="space-y-6">
<div class="max-w-4xl mx-auto space-y-6">
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
<div>
<h1 class="text-2xl md:text-3xl font-garamond font-bold text-irish-green">League Leaderboard</h1>
@@ -82,7 +82,9 @@
{% for team in teams %}
<tr class="{% if team.rank == 1 %}bg-golden-ale bg-opacity-10{% endif %} hover:bg-gray-50">
<td class="py-3 px-4 font-bold">{{ team.rank }}</td>
<td class="py-3 px-4">{{ team.name }}</td>
<td class="py-3 px-4">
<a href="/teams/{{ team.id }}" class="text-irish-green hover:underline">{{ team.name }}</a>
</td>
<td class="py-3 px-4 text-right font-bold">{{ team.points }}</td>
<td class="py-3 px-4 text-center">
{% if team.change > 0 %}
+5 -5
View File
@@ -50,7 +50,7 @@
<!-- Next Steps -->
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-xl font-semibold text-irish-green mb-4">What's Next?</h3>
<div class="space-y-4">
<div class="space-y-3 mt-8">
<a href="/teams/{{ team.id }}" class="flex items-start hover:bg-gray-50 p-3 rounded-md cursor-pointer">
<div class="bg-cream-white p-2 rounded-full mr-3">
<i class="fas fa-users text-irish-green"></i>
@@ -71,13 +71,13 @@
</div>
</a>
<a href="/scan" class="flex items-start hover:bg-gray-50 p-3 rounded-md cursor-pointer">
<div class="bg-cream-white p-2 rounded-full mr-3">
<i class="fas fa-qrcode text-irish-green"></i>
<a href="/dashboard/scan" class="flex items-start hover:bg-gray-50 p-3 rounded-md cursor-pointer">
<div class="bg-green-100 p-2 rounded-full mr-3">
<i class="fas fa-qrcode text-green-600"></i>
</div>
<div>
<p class="font-medium">Scan Another QR Code</p>
<p class="text-gray-600 text-sm">Got more codes to redeem? Scan them now.</p>
<p class="text-sm text-gray-600">Find more QR codes to redeem</p>
</div>
</a>
</div>
+47 -4
View File
@@ -67,12 +67,22 @@ def get_relationships(model_class: Type[Base]) -> Dict[str, str]:
return relationships
@router.get("/", response_class=HTMLResponse)
async def admin_home(request: Request):
async def admin_home(request: Request, db: Session = Depends(get_db)):
"""Admin dashboard home."""
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(User).get(user_id)
# Check admin status
if not user or not user.is_admin:
raise HTTPException(status_code=403, detail="Forbidden: Admin access required")
model_list = [(key, name) for key, (_, name) in MODELS.items()]
return templates.TemplateResponse(
"admin/index.html",
{"request": request, "models": model_list}
{"request": request, "models": model_list, "user": user}
)
@router.get("/{model_name}", response_class=HTMLResponse)
@@ -84,6 +94,16 @@ async def list_records(
db: Session = Depends(get_db)
):
"""List records for a model with pagination."""
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(User).get(user_id)
# Check admin status
if not user or not user.is_admin:
raise HTTPException(status_code=403, detail="Forbidden: Admin access required")
if model_name not in MODELS:
raise HTTPException(status_code=404, detail=f"Model {model_name} not found")
@@ -123,6 +143,7 @@ async def list_records(
"per_page": per_page,
"total_pages": total_pages,
"total_records": total_records,
"user": user # Add user to the context
}
)
@@ -133,6 +154,16 @@ async def create_record_form(
db: Session = Depends(get_db)
):
"""Show form for creating a new record."""
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(User).get(user_id)
# Check admin status
if not user or not user.is_admin:
raise HTTPException(status_code=403, detail="Forbidden: Admin access required")
if model_name not in MODELS:
raise HTTPException(status_code=404, detail=f"Model {model_name} not found")
@@ -162,7 +193,8 @@ async def create_record_form(
"columns_info": columns_info,
"record": None, # No record for new form
"foreign_key_options": foreign_key_options,
"is_new": True
"is_new": True,
"user": user # Add user to the context
}
)
@@ -222,6 +254,16 @@ async def edit_record_form(
db: Session = Depends(get_db)
):
"""Show form for editing an existing record."""
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(User).get(user_id)
# Check admin status
if not user or not user.is_admin:
raise HTTPException(status_code=403, detail="Forbidden: Admin access required")
if model_name not in MODELS:
raise HTTPException(status_code=404, detail=f"Model {model_name} not found")
@@ -261,7 +303,8 @@ async def edit_record_form(
"columns_info": columns_info,
"record": record_data,
"foreign_key_options": foreign_key_options,
"is_new": False
"is_new": False,
"user": user # Add user to the context
}
)
+52 -3
View File
@@ -92,6 +92,45 @@ def user_dashboard(request: Request, db: Session = Depends(get_db)):
.limit(5)\
.all()
# Get user teams with enhanced data for display
user_teams_enhanced = []
if user_teams:
# Get all team points to calculate ranks
team_points_query = db.query(
models.Team.id,
func.sum(models.QRCode.points).label('total_points')
).outerjoin(
models.QRCode,
models.QRCode.redeemed_at_team == models.Team.id
).group_by(models.Team.id)
# Get results and sort by points in descending order
team_points = {row.id: row.total_points or 0 for row in team_points_query.all()}
ranked_teams = sorted(team_points.items(), key=lambda x: x[1], reverse=True)
# Create a dictionary mapping team ID to rank
team_ranks = {team_id: i+1 for i, (team_id, _) in enumerate(ranked_teams)}
# Enhance user teams with rank and points data
for team in user_teams:
points = team_points.get(team.id, 0)
rank = team_ranks.get(team.id, len(team_ranks) + 1)
user_teams_enhanced.append({
'id': team.id,
'name': team.name,
'points': points,
'rank': rank,
'description': getattr(team, 'description', None)
})
# Find best rank if the user has any teams
best_rank = None
if user_teams_enhanced:
ranks = [team['rank'] for team in user_teams_enhanced]
best_rank = min(ranks) if ranks else None
return templates.TemplateResponse(
"dashboard/index.html",
{
@@ -101,7 +140,8 @@ def user_dashboard(request: Request, db: Session = Depends(get_db)):
"total_points": total_points,
"event_count": event_count,
"recent_events": recent_events,
"user_teams": user_teams
"user_teams": user_teams_enhanced,
"best_rank": best_rank
}
)
except Exception as e:
@@ -109,6 +149,15 @@ def user_dashboard(request: Request, db: Session = Depends(get_db)):
raise HTTPException(status_code=500, detail=f"Dashboard error: {str(e)}")
@router.get("/scan", response_class=HTMLResponse)
def scan_qr_page(request: Request):
def scan_qr_page(request: Request, db: Session = Depends(get_db)):
"""Render the QR code scanning page."""
return templates.TemplateResponse("scan_qr.html", {"request": request})
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(models.User).get(user_id)
return templates.TemplateResponse("scan_qr.html", {
"request": request,
"user": user # Add user to the context
})
+25 -4
View File
@@ -20,7 +20,7 @@ from reportlab.lib.enums import TA_CENTER
from pydantic import BaseModel
from ..db import SessionLocal
from ..models import QRCode, QRSet, Event
from ..models import QRCode, QRSet, Event, User
from ..templates_config import templates
router = APIRouter()
@@ -55,6 +55,12 @@ class QRCodeRequest(BaseModel):
@router.get("/", response_class=HTMLResponse)
async def qr_dashboard(request: Request, db: Session = Depends(get_db)):
"""QR code management dashboard."""
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(User).get(user_id)
# Get all QR sets
qr_sets = db.query(QRSet).all()
@@ -64,7 +70,8 @@ async def qr_dashboard(request: Request, db: Session = Depends(get_db)):
return templates.TemplateResponse("qr/dashboard.html", {
"request": request,
"qr_sets": qr_sets,
"events": events
"events": events,
"user": user # Add user to the context
})
@@ -94,6 +101,12 @@ async def create_qr_set(
@router.get("/sets/{set_id}", response_class=HTMLResponse)
async def view_qr_set(request: Request, set_id: int, db: Session = Depends(get_db)):
"""View details of a QR set."""
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(User).get(user_id)
qr_set = db.query(QRSet).filter(QRSet.id == set_id).first()
if not qr_set:
raise HTTPException(status_code=404, detail="QR Set not found")
@@ -105,7 +118,8 @@ async def view_qr_set(request: Request, set_id: int, db: Session = Depends(get_d
"request": request,
"qr_set": qr_set,
"qr_codes": qr_codes,
"base_url": BASE_URL
"base_url": BASE_URL,
"user": user # Add user to the context
})
@@ -208,6 +222,12 @@ def generate_admin_qr(set_id: int, db: Session = Depends(get_db)):
@router.get("/admin-link/{admin_code}", response_class=HTMLResponse)
async def admin_link_page(request: Request, admin_code: str, db: Session = Depends(get_db)):
"""Page for linking QR sets to events via admin code."""
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(User).get(user_id)
# Extract set_id from admin code
try:
set_id = int(admin_code.split("-")[1])
@@ -225,7 +245,8 @@ async def admin_link_page(request: Request, admin_code: str, db: Session = Depen
"request": request,
"qr_set": qr_set,
"admin_code": admin_code,
"events": events
"events": events,
"user": user # Add user to the context
})
+51 -14
View File
@@ -29,6 +29,12 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)):
Display a page to let the user choose which team to apply points to.
No login required.
"""
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(User).get(user_id)
# Find the QR code record
qr_code = db.query(QRCode).filter_by(code=code).first()
@@ -39,7 +45,8 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)):
{
"request": request,
"error_title": "Invalid Code",
"error_message": "This QR code is invalid or does not exist."
"error_message": "This QR code is invalid or does not exist.",
"user": user # Add user to the context
}
)
@@ -49,7 +56,8 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)):
{
"request": request,
"error_title": "Code Already Used",
"error_message": "This QR code has already been redeemed."
"error_message": "This QR code has already been redeemed.",
"user": user # Add user to the context
}
)
@@ -60,7 +68,8 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)):
{
"request": request,
"error_title": "Expired Code",
"error_message": "This QR code has expired and can no longer be redeemed."
"error_message": "This QR code has expired and can no longer be redeemed.",
"user": user # Add user to the context
}
)
@@ -72,16 +81,24 @@ def redeem_code(code: str, request: Request, db: Session = Depends(get_db)):
"ticket": qr_code, # Using the same template variable name for compatibility
"user_teams": all_teams,
"has_achievement": bool(qr_code.achievement_name),
"base_url": BASE_URL
"base_url": BASE_URL,
"user": user # Add user to the context
})
@router.get("/scan", response_class=HTMLResponse)
def scan_qr_code(request: Request):
def scan_qr_code(request: Request, db: Session = Depends(get_db)):
"""
Display the QR code scanner page
"""
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(User).get(user_id)
return templates.TemplateResponse("scan_qr.html", {
"request": request
"request": request,
"user": user # Add user to the context
})
@router.post("/apply/{code}")
@@ -93,6 +110,12 @@ async def apply_code(
"""
Apply the QR code to a selected team and award points and/or achievements
"""
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(User).get(user_id)
# Get form data
form_data = await request.form()
team_id = int(form_data.get("team_id", 0))
@@ -103,7 +126,8 @@ async def apply_code(
{
"request": request,
"error_title": "Team Selection Required",
"error_message": "Please select a team to redeem this code."
"error_message": "Please select a team to redeem this code.",
"user": user # Add user to the context
}
)
@@ -115,7 +139,8 @@ async def apply_code(
{
"request": request,
"error_title": "Invalid Code",
"error_message": "This QR code is invalid or does not exist."
"error_message": "This QR code is invalid or does not exist.",
"user": user # Add user to the context
}
)
@@ -126,7 +151,8 @@ async def apply_code(
{
"request": request,
"error_title": "Code Already Used",
"error_message": "This QR code has already been redeemed."
"error_message": "This QR code has already been redeemed.",
"user": user # Add user to the context
}
)
@@ -137,7 +163,8 @@ async def apply_code(
{
"request": request,
"error_title": "Expired Code",
"error_message": "This QR code has expired and can no longer be redeemed."
"error_message": "This QR code has expired and can no longer be redeemed.",
"user": user # Add user to the context
}
)
@@ -148,7 +175,8 @@ async def apply_code(
{
"request": request,
"error_title": "Team Not Found",
"error_message": "The selected team could not be found."
"error_message": "The selected team could not be found.",
"user": user # Add user to the context
}
)
@@ -180,7 +208,8 @@ async def apply_code(
"achievement": qr_code.achievement_name if qr_code.achievement_name else None,
"team": team,
"event": qr_code.event if qr_code.event else None,
"base_url": BASE_URL
"base_url": BASE_URL,
"user": user # Add user to the context
}
)
@@ -194,6 +223,12 @@ async def manual_code_entry(
Handle manual code entry from the form.
This redirects to the normal redeem flow after validating the code.
"""
# Get user from session for navbar
user = None
user_id = request.session.get("user_id")
if user_id:
user = db.query(User).get(user_id)
# Check if the code exists
qr_code = db.query(QRCode).filter_by(code=code).first()
@@ -203,7 +238,8 @@ async def manual_code_entry(
{
"request": request,
"error_title": "Invalid Code",
"error_message": "The code you entered is invalid or does not exist."
"error_message": "The code you entered is invalid or does not exist.",
"user": user # Add user to the context
}
)
@@ -214,7 +250,8 @@ async def manual_code_entry(
{
"request": request,
"error_title": "Code Already Used",
"error_message": "This code has already been redeemed."
"error_message": "This code has already been redeemed.",
"user": user # Add user to the context
}
)