Files
Reklamator/app/templates/dashboard/detail.html
T
gurixandClaude 94c6187bb2 Add manual AI analysis trigger for existing feedback
Implement dashboard functionality to manually trigger AI analysis for
feedback that was submitted before Phase 4 or failed analysis. Adds
detection mechanism to identify which feedback has been analyzed.

Features:
- Manual analysis trigger route: POST /feedback/{id}/analyze
- Detection of whether feedback has been analyzed (analysis.md exists)
- Dashboard UI button showing "Analyze" or "Re-analyze"
- Visual feedback for feedback without text content
- Comprehensive error handling and flash messages

Implementation:
- app/routes/dashboard.py: Added trigger_analysis() route handler
- app/routes/dashboard.py: Updated detail() to pass analysis status
- app/services/feedback_storage.py: Added has_analysis() helper method
- app/templates/dashboard/detail.html: Added analyze button UI
- tests/contract/test_dashboard_routes.py: Added 3 new contract tests

Testing:
- test_post_trigger_analysis_success: Successful manual analysis
- test_post_trigger_analysis_no_content: Reject empty content
- test_post_trigger_analysis_unauthenticated: Auth required
- All 49 tests passing (1 skipped)

User Experience:
- Green box with "Analyze" button for unanalyzed feedback
- Blue box with "Re-analyze" button for already analyzed feedback
- Red box with info message for feedback without text content
- Flash messages show success/error after analysis

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 21:26:25 +02:00

134 lines
6.6 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Feedback Detail{% endblock %}
{% block content %}
<div style="max-width: 900px; margin: 0 auto; padding: 20px;">
<div style="margin-bottom: 20px;">
<a href="{{ url_for('dashboard.list') }}" style="color: #007bff; text-decoration: none;">← Back to Dashboard</a>
</div>
<h1>Feedback Detail</h1>
<!-- Metadata Section -->
<div style="background: #f8f9fa; padding: 20px; border-radius: 5px; margin: 20px 0;">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px;">
<div>
<strong>Feedback ID:</strong>
<p style="margin: 5px 0; font-family: monospace; font-size: 0.9em;">{{ feedback.feedback_id }}</p>
</div>
<div>
<strong>Product:</strong>
<p style="margin: 5px 0;">{{ product.name if product else feedback.product_id }}</p>
</div>
<div>
<strong>Status:</strong>
<p style="margin: 5px 0;">
<span style="padding: 5px 10px; border-radius: 3px;
{% if feedback.metadata.status == 'new' %}background: #d1ecf1; color: #0c5460;
{% elif feedback.metadata.status == 'in_progress' %}background: #fff3cd; color: #856404;
{% elif feedback.metadata.status == 'resolved' %}background: #d4edda; color: #155724;
{% else %}background: #e2e3e5; color: #383d41;{% endif %}">
{{ feedback.metadata.status }}
</span>
</p>
</div>
<div>
<strong>Category:</strong>
<p style="margin: 5px 0;">
<span style="padding: 5px 10px; background: #e9ecef; border-radius: 3px;">
{{ feedback.metadata.category or 'Uncategorized' }}
</span>
</p>
</div>
<div>
<strong>Language:</strong>
<p style="margin: 5px 0;">{{ feedback.metadata.original_language or 'Unknown' }}</p>
</div>
<div>
<strong>Submitted:</strong>
<p style="margin: 5px 0;">{{ feedback.metadata.submitted_at[:19] if feedback.metadata.submitted_at else 'Unknown' }}</p>
</div>
</div>
</div>
<!-- Update Status Form -->
<div style="margin: 20px 0; padding: 15px; background: #fff3cd; border-radius: 5px;">
<form method="post" action="{{ url_for('dashboard.update_status', feedback_id=feedback.feedback_id) }}" style="display: flex; align-items: center; gap: 10px;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label for="status"><strong>Update Status:</strong></label>
<select name="status" id="status" style="padding: 5px 10px; border: 1px solid #ccc; border-radius: 3px;">
<option value="new" {% if feedback.metadata.status == 'new' %}selected{% endif %}>New</option>
<option value="in_progress" {% if feedback.metadata.status == 'in_progress' %}selected{% endif %}>In Progress</option>
<option value="resolved" {% if feedback.metadata.status == 'resolved' %}selected{% endif %}>Resolved</option>
<option value="closed" {% if feedback.metadata.status == 'closed' %}selected{% endif %}>Closed</option>
</select>
<button type="submit" style="padding: 5px 15px; background: #28a745; color: white; border: none; border-radius: 3px; cursor: pointer;">Update</button>
</form>
</div>
<!-- AI Analysis Trigger -->
{% if can_analyze %}
<div style="margin: 20px 0; padding: 15px; {% if has_analysis %}background: #d1ecf1;{% else %}background: #d4edda;{% endif %} border-radius: 5px;">
<form method="post" action="{{ url_for('dashboard.trigger_analysis', feedback_id=feedback.feedback_id) }}" style="display: flex; align-items: center; gap: 10px;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
{% if has_analysis %}
<span style="font-size: 1.2em;">🔄</span>
<label><strong>Re-run AI Analysis:</strong> This feedback has been analyzed. Click to re-analyze.</label>
{% else %}
<span style="font-size: 1.2em;">🤖</span>
<label><strong>Run AI Analysis:</strong> This feedback has not been analyzed yet.</label>
{% endif %}
<button type="submit" style="padding: 5px 15px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer;">
{% if has_analysis %}Re-analyze{% else %}Analyze{% endif %}
</button>
</form>
</div>
{% elif not feedback.content %}
<div style="margin: 20px 0; padding: 15px; background: #f8d7da; border-radius: 5px; color: #721c24;">
<span style="font-size: 1.2em;"></span>
<strong>Cannot analyze:</strong> This feedback has no text content (attachments only).
</div>
{% endif %}
<!-- Original Content -->
<div style="margin: 30px 0;">
<h2>Original Feedback</h2>
<div style="background: white; border: 1px solid #dee2e6; border-radius: 5px; padding: 20px; white-space: pre-wrap;">{{ feedback.content }}</div>
</div>
<!-- Analysis (if exists) -->
{% if feedback.analysis %}
<div style="margin: 30px 0;">
<h2>AI Analysis</h2>
<div style="background: white; border: 1px solid #dee2e6; border-radius: 5px; padding: 20px;">
{{ feedback.analysis|safe }}
</div>
</div>
{% endif %}
<!-- Attachments -->
{% if feedback.attachments %}
<div style="margin: 30px 0;">
<h2>Attachments ({{ feedback.attachments|length }})</h2>
<ul style="list-style: none; padding: 0;">
{% for attachment in feedback.attachments %}
<li style="margin: 10px 0; padding: 10px; background: #f8f9fa; border-radius: 3px;">
<a href="{{ url_for('dashboard.download_attachment', feedback_id=feedback.feedback_id, filename=attachment) }}"
style="color: #007bff; text-decoration: none; display: flex; align-items: center; gap: 10px;">
<span style="font-size: 1.2em;">📎</span>
<span>{{ attachment }}</span>
</a>
</li>
{% endfor %}
</ul>
</div>
{% else %}
<div style="margin: 30px 0;">
<h2>Attachments</h2>
<p style="color: #666;">No attachments</p>
</div>
{% endif %}
</div>
{% endblock %}