feat(dashboard): list recent cases with search and pagination\n\n- Add q, page, page_size to /dashboard route\n- Join clients and filter by file_no/name/company\n- Bootstrap table UI with search form and pagination\n- Log query params; preserve auth/session\n\nCo-authored-by: AI Assistant <ai@example.com>

This commit is contained in:
HotSwapp
2025-10-06 19:11:40 -05:00
parent 6aa4d59a25
commit 6174df42b4
2 changed files with 194 additions and 10 deletions

View File

@@ -9,18 +9,19 @@ import os
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI, Depends, Request
from fastapi import FastAPI, Depends, Request, Query
from fastapi.responses import RedirectResponse
from starlette.middleware.sessions import SessionMiddleware
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session
from sqlalchemy import or_
from dotenv import load_dotenv
from starlette.middleware.base import BaseHTTPMiddleware
from .database import create_tables, get_db, get_database_url
from .models import User
from .models import User, Case, Client
from .auth import authenticate_user, get_current_user_from_session
# Load environment variables
@@ -218,21 +219,83 @@ async def logout(request: Request):
@app.get("/dashboard")
async def dashboard(request: Request, db: Session = Depends(get_db)):
async def dashboard(
request: Request,
q: str | None = Query(None, description="Search by file number or client name"),
page: int = Query(1, ge=1, description="Page number (1-indexed)"),
page_size: int = Query(20, ge=1, le=100, description="Results per page"),
db: Session = Depends(get_db),
):
"""
Dashboard page - requires authentication.
Dashboard page - lists recent cases with search and pagination.
Shows an overview of the system and provides navigation to main features.
- Optional query param `q` filters by case file number or client name/company
- `page` and `page_size` control pagination
"""
# Check authentication
user = get_current_user_from_session(request.session)
if not user:
return RedirectResponse(url="/login", status_code=302)
return templates.TemplateResponse("dashboard.html", {
"request": request,
"user": user
})
# Base query: join clients for name/company access
query = db.query(Case).join(Client).order_by(
Case.open_date.desc(),
Case.created_at.desc(),
)
# Apply search filter if provided
if q:
like_term = f"%{q}%"
query = query.filter(
or_(
Case.file_no.ilike(like_term),
Client.first_name.ilike(like_term),
Client.last_name.ilike(like_term),
Client.company.ilike(like_term),
)
)
# Total count for pagination
total: int = query.count()
# Clamp page to valid range when total is known
total_pages: int = (total + page_size - 1) // page_size if total > 0 else 1
if page > total_pages:
page = total_pages
# Pagination window
offset = (page - 1) * page_size
cases = query.offset(offset).limit(page_size).all()
# Page number window for UI (current +/- 2)
start_page = max(1, page - 2)
end_page = min(total_pages, page + 2)
page_numbers = list(range(start_page, end_page + 1))
logger.info(
"Rendering dashboard: q='%s', page=%s, page_size=%s, total=%s",
q,
page,
page_size,
total,
)
return templates.TemplateResponse(
"dashboard.html",
{
"request": request,
"user": user,
"cases": cases,
"q": q,
"page": page,
"page_size": page_size,
"total": total,
"total_pages": total_pages,
"page_numbers": page_numbers,
"start_index": (offset + 1) if total > 0 else 0,
"end_index": min(offset + len(cases), total),
},
)
@app.get("/admin")