Files
Reklamator/specs/001-build-an-application/contracts/dashboard_api.md
T
gurixandClaude 05e201f1fc Add implementation planning artifacts for anonymous feedback platform
Complete Phase 0 (Research) and Phase 1 (Design) of implementation planning workflow:

- plan.md: Technical context, constitution check, and project structure
- research.md: Technology decisions for AI integration, file storage, authentication, security
- data-model.md: Domain entities with validation rules and state transitions
- contracts/: API specifications for submission, dashboard, and admin endpoints
- quickstart.md: Developer setup guide with test-first workflow
- CLAUDE.md: Updated agent context with tech stack

All constitutional requirements validated. Ready for task generation phase.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 22:31:26 +02:00

10 KiB

Dashboard API Contract

Scope: Product owner dashboard endpoints (User Story P3) Authentication: Required (session-based via Flask-Login)


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):

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/1.1 302 Found
Location: /dashboard
Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax

Error (401 Unauthorized):

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/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):

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/1.1 302 Found
Location: /login?next=/dashboard

Error (403 Forbidden): User has no assigned products

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):

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/1.1 302 Found
Location: /login?next=/feedback/{feedback_id}

Error (403 Forbidden): User not authorized for this product

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

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/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/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/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).