# Prompt di Ingaggio: Frontend Web (T44-T54) ## 🎯 MISSIONE Implementare il **Frontend Web** per OpenRouter API Key Monitor usando HTML, Jinja2 templates e HTMX per un'interfaccia utente moderna e reattiva. **Task da completare:** T44, T45, T46, T47, T48, T49, T50, T51, T52, T53, T54 --- ## πŸ“‹ CONTESTO **AGENTE:** @tdd-developer **Repository:** `/home/google/Sources/LucaSacchiNet/openrouter-watcher` **Stato Attuale:** - βœ… MVP Backend completato: 51/74 task (69%) - βœ… 444+ test passanti, ~98% coverage - βœ… Tutte le API REST implementate - βœ… Background Tasks per sincronizzazione automatica - βœ… Docker support pronto - 🎯 **Manca:** Interfaccia web per gli utenti **PerchΓ© questa fase Γ¨ importante:** Attualmente l'applicazione espone solo API REST. Gli utenti devono usare strumenti come curl o Postman per interagire. Con il frontend web, gli utenti potranno: - Registrarsi e fare login via browser - Visualizzare dashboard con grafici - Gestire API keys tramite interfaccia grafica - Generare e revocare token API - Vedere statistiche in tempo reale **Stack Frontend:** - **FastAPI** - Serve static files e templates - **Jinja2** - Template engine - **HTMX** - AJAX moderno senza JavaScript complesso - **Pico.css** - CSS framework minimalista (o Bootstrap/Tailwind) - **Chart.js** - Grafici per dashboard **Backend Pronto:** - Tutti i router REST funzionanti - Autenticazione JWT via cookie - API documentate su `/docs` --- ## πŸ”§ TASK DA IMPLEMENTARE ### T44: Configurare FastAPI per Static Files e Templates **File:** `src/openrouter_monitor/main.py` **Requisiti:** - Mount directory `/static` per CSS, JS, immagini - Configurare Jinja2 templates - Creare struttura directory `templates/` e `static/` - Aggiungere context processor per variabili globali **Implementazione:** ```python from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from pathlib import Path # Mount static files app.mount("/static", StaticFiles(directory="static"), name="static") # Configure templates templates = Jinja2Templates(directory="templates") # Context processor def get_context(request: Request, **kwargs): return { "request": request, "app_name": "OpenRouter Monitor", "user": getattr(request.state, 'user', None), **kwargs } ``` **File da creare:** ``` static/ β”œβ”€β”€ css/ β”‚ └── style.css β”œβ”€β”€ js/ β”‚ └── main.js └── img/ └── favicon.ico templates/ β”œβ”€β”€ base.html β”œβ”€β”€ components/ β”‚ β”œβ”€β”€ navbar.html β”‚ β”œβ”€β”€ footer.html β”‚ └── alert.html β”œβ”€β”€ auth/ β”‚ β”œβ”€β”€ login.html β”‚ └── register.html β”œβ”€β”€ dashboard/ β”‚ └── index.html β”œβ”€β”€ keys/ β”‚ └── index.html β”œβ”€β”€ tokens/ β”‚ └── index.html └── profile/ └── index.html ``` **Test:** Verifica che `/static/css/style.css` sia accessibile --- ### T45: Creare Base Template HTML **File:** `templates/base.html`, `templates/components/navbar.html`, `templates/components/footer.html` **Requisiti:** - Layout base responsive - Include Pico.css (o altro framework) da CDN - Meta tags SEO-friendly - Favicon - Navbar con menu dinamico (login/logout) - Footer con info app - Block content per pagine figlie **Implementazione base.html:** ```html {% block title %}{{ app_name }}{% endblock %} {% block extra_css %}{% endblock %} {% include 'components/navbar.html' %}
{% include 'components/alert.html' %} {% block content %}{% endblock %}
{% include 'components/footer.html' %} {% block extra_js %}{% endblock %} ``` **Test:** Verifica rendering base template --- ### T46: Configurare HTMX e CSRF **File:** `templates/base.html` (aggiorna), `src/openrouter_monitor/middleware/csrf.py` **Requisiti:** - Aggiungere CSRF token in meta tag - Middleware CSRF per protezione form - HTMX configurato per inviare CSRF header **Implementazione:** ```python # middleware/csrf.py from fastapi import Request, HTTPException from starlette.middleware.base import BaseHTTPMiddleware import secrets class CSRFMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): # Generate or get CSRF token if 'csrf_token' not in request.session: request.session['csrf_token'] = secrets.token_urlsafe(32) # Validate on POST/PUT/DELETE if request.method in ['POST', 'PUT', 'DELETE']: token = request.headers.get('X-CSRF-Token') or request.form().get('_csrf_token') if token != request.session.get('csrf_token'): raise HTTPException(status_code=403, detail="Invalid CSRF token") response = await call_next(request) return response ``` **Template aggiornamento:** ```html ``` --- ### T47: Pagina Login (/login) **File:** `templates/auth/login.html`, `src/openrouter_monitor/routers/web_auth.py` **Requisiti:** - Form email/password - Validazione client-side (HTML5) - HTMX per submit AJAX - Messaggi errore (flash messages) - Redirect a dashboard dopo login - Link a registrazione **Implementazione:** ```python # routers/web_auth.py from fastapi import APIRouter, Request, Form, HTTPException from fastapi.responses import HTMLResponse, RedirectResponse router = APIRouter() @router.get("/login", response_class=HTMLResponse) async def login_page(request: Request): return templates.TemplateResponse( "auth/login.html", get_context(request) ) @router.post("/login") async def login_submit( request: Request, email: str = Form(...), password: str = Form(...) ): # Call auth service try: token = await authenticate_user(email, password) response = RedirectResponse(url="/dashboard", status_code=302) response.set_cookie(key="access_token", value=token, httponly=True) return response except AuthenticationError: return templates.TemplateResponse( "auth/login.html", get_context(request, error="Invalid credentials") ) ``` **Template:** ```html {% extends "base.html" %} {% block title %}Login - {{ app_name }}{% endblock %} {% block content %}

Login

{% if error %}
{{ error }}
{% endif %}

Don't have an account? Register

{% endblock %} ``` **Test:** Test login form, validazione, redirect --- ### T48: Pagina Registrazione (/register) **File:** `templates/auth/register.html` **Requisiti:** - Form completo: email, password, password_confirm - Validazione password strength (client-side) - Check password match - Conferma registrazione - Redirect a login **Template:** ```html {% extends "base.html" %} {% block content %}

Register

Min 12 chars, uppercase, lowercase, number, special char
{% endblock %} ``` --- ### T49: Pagina Logout **File:** Gestito via endpoint POST con redirect **Requisiti:** - Bottone logout in navbar - Conferma opzionale - Redirect a login - Cancella cookie JWT --- ### T50: Dashboard (/dashboard) **File:** `templates/dashboard/index.html` **Requisiti:** - Card riepilogative (totale richieste, costo, token) - Grafico andamento temporale (Chart.js) - Tabella modelli piΓΉ usati - Link rapidi a gestione keys e tokens - Dati caricati via API interna **Implementazione:** ```html {% extends "base.html" %} {% block content %}

Dashboard

Total Requests

{{ stats.total_requests }}

Total Cost

${{ stats.total_cost }}

API Keys

{{ api_keys_count }}

Usage Over Time

{% endblock %} ``` --- ### T51-T54: Altre Pagine Seguire lo stesso pattern per: - **T51**: Gestione API Keys (`/keys`) - Tabella con CRUD via HTMX - **T52**: Statistiche (`/stats`) - Filtri e paginazione - **T53**: Token API (`/tokens`) - Generazione e revoca - **T54**: Profilo (`/profile`) - Cambio password --- ## πŸ”„ WORKFLOW TDD Per **OGNI** task: 1. **RED**: Scrivi test che verifica rendering template 2. **GREEN**: Implementa template e route 3. **REFACTOR**: Estrai componenti riutilizzabili --- ## πŸ“ STRUTTURA FILE DA CREARE ``` templates/ β”œβ”€β”€ base.html β”œβ”€β”€ components/ β”‚ β”œβ”€β”€ navbar.html β”‚ β”œβ”€β”€ footer.html β”‚ └── alert.html β”œβ”€β”€ auth/ β”‚ β”œβ”€β”€ login.html β”‚ └── register.html β”œβ”€β”€ dashboard/ β”‚ └── index.html β”œβ”€β”€ keys/ β”‚ └── index.html β”œβ”€β”€ tokens/ β”‚ └── index.html └── profile/ └── index.html static/ β”œβ”€β”€ css/ β”‚ └── style.css └── js/ └── main.js src/openrouter_monitor/ β”œβ”€β”€ routers/ β”‚ β”œβ”€β”€ web.py # T44, T47-T54 β”‚ └── web_auth.py # T47-T49 └── middleware/ └── csrf.py # T46 ``` --- ## βœ… CRITERI DI ACCETTAZIONE - [ ] T44: Static files e templates configurati - [ ] T45: Base template con layout responsive - [ ] T46: CSRF protection e HTMX configurati - [ ] T47: Pagina login funzionante - [ ] T48: Pagina registrazione funzionante - [ ] T49: Logout funzionante - [ ] T50: Dashboard con grafici - [ ] T51: Gestione API keys via web - [ ] T52: Statistiche con filtri - [ ] T53: Gestione token via web - [ ] T54: Profilo utente - [ ] Tutte le pagine responsive (mobile-friendly) - [ ] Test completi per router web - [ ] 11 commit atomici con conventional commits --- ## πŸ“ COMMIT MESSAGES ``` feat(frontend): T44 setup FastAPI static files and templates feat(frontend): T45 create base HTML template with layout feat(frontend): T46 configure HTMX and CSRF protection feat(frontend): T47 implement login page feat(frontend): T48 implement registration page feat(frontend): T49 implement logout functionality feat(frontend): T50 implement dashboard with charts feat(frontend): T51 implement API keys management page feat(frontend): T52 implement detailed stats page feat(frontend): T53 implement API tokens management page feat(frontend): T54 implement user profile page ``` --- ## πŸš€ VERIFICA FINALE ```bash cd /home/google/Sources/LucaSacchiNet/openrouter-watcher # Avvia app uvicorn src.openrouter_monitor.main:app --reload # Test manuali: # 1. Visita http://localhost:8000/login # 2. Registra nuovo utente # 3. Login # 4. Visualizza dashboard con grafici # 5. Aggiungi API key # 6. Genera token API # 7. Logout # Test automatici pytest tests/unit/routers/test_web.py -v ``` --- ## 🎨 DESIGN CONSIGLIATO - **Framework CSS**: Pico.css (leggero, moderno, semantic HTML) - **Colori**: Blu primario, grigio chiaro sfondo - **Layout**: Container centrato, max-width 1200px - **Mobile**: Responsive con breakpoint 768px - **Grafici**: Chart.js con tema coordinato --- **AGENTE:** @tdd-developer **INIZIA CON:** T44 - Setup FastAPI static files e templates **QUANDO FINITO:** L'applicazione avrΓ  un'interfaccia web completa e user-friendly! 🎨