diff --git a/app/main.py b/app/main.py index e3bb415..ac2a87c 100644 --- a/app/main.py +++ b/app/main.py @@ -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") diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index 996a5b5..a54499d 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -1 +1,122 @@ - +{% extends "base.html" %} + +{% block title %}Dashboard · Delphi Database{% endblock %} + +{% block content %} +
+
+

Cases

+
+
+
+ + + +
+
+
+ + Clear + +
+
+ {% if total and total > 0 %} + Showing {{ start_index }}–{{ end_index }} of {{ total }} + {% else %} + No results + {% endif %} +
+
+
+ + + + + + + + + + + + + + {% if cases and cases|length > 0 %} + {% for c in cases %} + + + + + + + + + + {% endfor %} + {% else %} + + + + {% endif %} + +
File #ClientCompanyTypeStatusOpenedActions
{{ c.file_no }} + {% set client = c.client %} + {% if client %} + {{ client.last_name }}, {{ client.first_name }} + {% else %} + Unknown + {% endif %} + {{ client.company if client else '' }}{{ c.case_type or '' }} + {% if c.status == 'active' %} + Active + {% elif c.status == 'closed' %} + Closed + {% else %} + {{ c.status or 'n/a' }} + {% endif %} + {{ c.open_date.strftime('%Y-%m-%d') if c.open_date else '' }} + + View + +
No cases found.
+
+
+
+ {% if total_pages and total_pages > 1 %} + + {% endif %} +
+
+{% endblock %}