Update redirect paths and enhance user context in templates and views
This commit is contained in:
+2
-2
@@ -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")
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user