2025-10-16 13:47:40 +02:00
# Dashboard Routes Contract
2025-10-15 22:31:26 +02:00
2025-10-16 13:47:40 +02:00
**Scope** : Product owner dashboard web routes (User Story P3)
2025-10-15 22:31:26 +02:00
**Authentication** : Required (session-based via Flask-Login)
2025-10-16 13:47:40 +02:00
**Response Type** : Server-rendered HTML (no JavaScript required)
2025-10-15 22:31:26 +02:00
---
## GET /login
Display login form for product owners and administrators.
### Request
**Headers** : None required
**Query Parameters** :
- `next` (string, optional): Redirect URL after successful login
### Response
**Success (200 OK)** :
```html
Content-Type: text/html
<!DOCTYPE html>
< html >
< head >< title > Login - Reklamator</ title ></ head >
< body >
< h1 > Login</ h1 >
< form method = "POST" action = "/login" >
< input type = "email" name = "email" required placeholder = "Email" >
< input type = "password" name = "password" required placeholder = "Password" >
< button type = "submit" > Login</ button >
</ form >
</ body >
</ html >
```
**Already Authenticated (302 Redirect)** : Redirect to `/dashboard`
---
## POST /login
Authenticate product owner or administrator.
### Request
**Headers** :
- `Content-Type: application/x-www-form-urlencoded`
**Form Data** :
- `email` (string, required): User email
- `password` (string, required): User password
### Response
**Success (302 Redirect)** :
```http
HTTP / 1.1 302 Found
Location : /dashboard
Set-Cookie : session=...; HttpOnly; Secure; SameSite=Lax
```
**Error (401 Unauthorized)** :
```html
Content-Type: text/html
<!DOCTYPE html>
< html >
< body >
< h1 > Login Failed</ h1 >
< p > Invalid email or password.</ p >
</ body >
</ html >
```
### Functional Requirements Covered
- FR-056: Authentication required for dashboard
- FR-063: Password verification against bcrypt hash
---
## GET /logout
Log out current user.
### Request
**Authentication** : Required (session cookie)
### Response
**Success (302 Redirect)** :
```http
HTTP / 1.1 302 Found
Location : /login
Set-Cookie : session=deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT
```
---
## GET /dashboard
Display product owner dashboard with feedback list.
### Request
**Authentication** : Required (session cookie)
**Query Parameters** :
- `page` (integer, optional, default=1): Page number for pagination
- `category` (string, optional): Filter by category (idea/feature_request/bug/complaint)
- `language` (string, optional): Filter by original language (ISO 639-1 code)
- `status` (string, optional): Filter by status (analyzed/reviewed/in_progress/resolved/rejected)
- `date_from` (string, optional): Filter by date range start (ISO 8601 date)
- `date_to` (string, optional): Filter by date range end (ISO 8601 date)
- `search` (string, optional): Keyword search across text/translation/summary
### Response
**Success (200 OK)** :
```html
Content-Type: text/html
<!DOCTYPE html>
< html >
< head >< title > Feedback Dashboard</ title ></ head >
< body >
< h1 > Feedback Dashboard</ h1 >
<!-- Product selector if multiple products assigned -->
< select name = "product" >
< option value = "001-acme-app" > Acme Mobile App (127 items)</ option >
< option value = "002-beta-service" > Beta Service (43 items)</ option >
</ select >
<!-- Filters -->
< form method = "GET" action = "/dashboard" >
< select name = "category" >
< option value = "" > All Categories</ option >
< option value = "idea" > Ideas</ option >
< option value = "feature_request" > Feature Requests</ option >
< option value = "bug" > Bugs</ option >
< option value = "complaint" > Complaints</ option >
</ select >
< select name = "status" >
< option value = "" > All Statuses</ option >
< option value = "analyzed" > Analyzed</ option >
< option value = "reviewed" > Reviewed</ option >
< option value = "in_progress" > In Progress</ option >
< option value = "resolved" > Resolved</ option >
< option value = "rejected" > Rejected</ option >
</ select >
< input type = "text" name = "search" placeholder = "Search feedback..." >
< button type = "submit" > Filter</ button >
</ form >
<!-- Feedback list -->
< table >
< thead >
< tr >
< th > ID</ th >
< th > Date</ th >
< th > Category</ th >
< th > Original Lang</ th >
< th > Summary</ th >
< th > Status</ th >
< th > Attachments</ th >
</ tr >
</ thead >
< tbody >
< tr >
< td >< a href = "/feedback/a3f2c1d5" > a3f2c1d5</ a ></ td >
< td > 2025-10-15 14:32</ td >
< td > Bug</ td >
< td > DE</ td >
< td > User reports app crashes when uploading large files...</ td >
< td > Reviewed</ td >
< td > 2 files</ td >
</ tr >
<!-- More rows... -->
</ tbody >
</ table >
<!-- Pagination -->
< div class = "pagination" >
< a href = "/dashboard?page=1" > 1</ a >
< a href = "/dashboard?page=2" > 2</ a >
< a href = "/dashboard?page=3" > 3</ a >
</ div >
</ body >
</ html >
```
**Error (401 Unauthorized)** : Not authenticated
```http
HTTP / 1.1 302 Found
Location : /login?next=/dashboard
```
**Error (403 Forbidden)** : User has no assigned products
```html
Content-Type: text/html
<!DOCTYPE html>
< html >
< body >
< h1 > No Access</ h1 >
< p > You are not assigned to any products.</ p >
</ body >
</ html >
```
### Behavior
- Display only feedback for products assigned to current user (FR-033)
- Admin users see all products
- Default sort: newest first (FR-041)
- Pagination: 50 items per page (SC-008: <3s for 1000 items)
- Filters preserved in URL for sharing/bookmarking
### Functional Requirements Covered
- FR-032: Authenticated dashboard access
- FR-033: Product owner access control
- FR-034: Display all analysis results
- FR-036-FR-041: Filtering and searching
- FR-041: Reverse chronological order
---
## GET /feedback/{feedback_id}
Display detailed view of a single feedback item.
### Request
**Authentication** : Required (session cookie)
**Path Parameters** :
- `feedback_id` (UUID, required): Feedback identifier
### Response
**Success (200 OK)** :
```html
Content-Type: text/html
<!DOCTYPE html>
< html >
< head >< title > Feedback Detail - a3f2c1d5</ title ></ head >
< body >
< h1 > Feedback Detail</ h1 >
< div class = "metadata" >
< p >< strong > ID:</ strong > a3f2c1d5-8b4e-4f1a-9c2d-7e6f5a4b3c2d</ p >
< p >< strong > Product:</ strong > Acme Mobile App</ p >
< p >< strong > Submitted:</ strong > 2025-10-15 14:32:10 UTC</ p >
< p >< strong > Original Language:</ strong > German (DE)</ p >
< p >< strong > Category:</ strong > Bug (confidence: 0.92)</ p >
< p >< strong > Status:</ strong >
< form method = "POST" action = "/feedback/a3f2c1d5/status" >
< select name = "status" >
< option value = "analyzed" > Analyzed</ option >
< option value = "reviewed" selected > Reviewed</ option >
< option value = "in_progress" > In Progress</ option >
< option value = "resolved" > Resolved</ option >
< option value = "rejected" > Rejected</ option >
</ select >
< button type = "submit" > Update Status</ button >
</ form >
</ p >
</ div >
< h2 > AI Analysis Summary</ h2 >
< p > User reports that the app crashes when uploading large files. This appears to be a bug affecting the file upload module, preventing users from submitting documents over 5MB.</ p >
< h2 > Original Text (German)</ h2 >
< pre > Die App stürzt ab, wenn ich versuche, große Dateien hochzuladen. Jedes Mal wenn ich eine PDF über 5MB hochlade, friert die App ein und schließt sich.</ pre >
< h2 > Translation (English)</ h2 >
< pre > The app crashes when I try to upload large files. Every time I upload a PDF over 5MB, the app freezes and closes.</ pre >
< h2 > Attachments</ h2 >
< ul >
< li >< a href = "/feedback/a3f2c1d5/attachment/screenshot.png" target = "_blank" > screenshot.png</ a > (245 KB)</ li >
< li >< a href = "/feedback/a3f2c1d5/attachment/error_log.txt" target = "_blank" > error_log.txt</ a > (1 KB)</ li >
</ ul >
</ body >
</ html >
```
**Error (401 Unauthorized)** : Not authenticated
```http
HTTP / 1.1 302 Found
Location : /login?next=/feedback/{feedback_id}
```
**Error (403 Forbidden)** : User not authorized for this product
```html
Content-Type: text/html
<!DOCTYPE html>
< html >
< body >
< h1 > Access Denied</ h1 >
< p > You do not have permission to view this feedback.</ p >
</ body >
</ html >
```
**Error (404 Not Found)** : Feedback does not exist
```html
Content-Type: text/html
<!DOCTYPE html>
< html >
< body >
< h1 > Feedback Not Found</ h1 >
</ body >
</ html >
```
### Functional Requirements Covered
- FR-034: Display complete feedback details
- FR-035: Links to download attachments
- FR-042: Status indicators
- FR-044: File attachments with icons/thumbnails
---
## POST /feedback/{feedback_id}/status
Update the status of a feedback item.
### Request
**Authentication** : Required (session cookie)
**Path Parameters** :
- `feedback_id` (UUID, required): Feedback identifier
**Form Data** :
- `status` (string, required): New status (analyzed/reviewed/in_progress/resolved/rejected)
### Response
**Success (302 Redirect)** :
```http
HTTP / 1.1 302 Found
Location : /feedback/{feedback_id}
```
**Error (403 Forbidden)** : User not authorized for this product
**Error (404 Not Found)** : Feedback does not exist
### Side Effects
- Updates `metadata.yaml` : `status` field
- Preserves timestamp of status change
### Functional Requirements Covered
- FR-042: Mark feedback with status indicators
- FR-043: Preserve status when filtering
---
## GET /feedback/{feedback_id}/attachment/{filename}
Download or view an attached file.
### Request
**Authentication** : Required (session cookie)
**Path Parameters** :
- `feedback_id` (UUID, required): Feedback identifier
- `filename` (string, required): Sanitized filename
### Response
**Success (200 OK)** : Image file
```http
HTTP / 1.1 200 OK
Content-Type : image/png
Content-Disposition : inline; filename="screenshot.png"
Content-Length : 245678
[binary image data]
```
**Success (200 OK)** : Document file
```http
HTTP / 1.1 200 OK
Content-Type : application/pdf
Content-Disposition : attachment; filename="report.pdf"
Content-Length : 1234567
[binary document data]
```
**Error (403 Forbidden)** : User not authorized for this product
**Error (404 Not Found)** : File does not exist
### Security
- Files served via Flask route (not direct filesystem access per FR-058)
- Access control enforced: User must have access to parent product
- Path traversal prevention: Filename sanitized
- MIME type from stored metadata (not client-provided)
### Functional Requirements Covered
- FR-035: Provide links to download/view attachments
- FR-058: Prevent unauthorized file access
---
## Rate Limiting
Dashboard endpoints are NOT rate limited (authenticated users only).