# 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
{% 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! π¨