Show feedback in the admin panel and as aggregate stats on the briefing page

Feedback was only ever written to session_setting rows with no way to
view it short of querying the database directly. Adds a FeedbackService
that pulls per-player feedback entries and aggregate averages, backing
two new views:

- /admin/feedback: a full table of every submission (game, session,
  player, ratings, comment) plus summary tiles, linked from the admin
  sidebar.
- /briefing: a "Field Reports" block with the average difficulty/
  entertainment/theme ratings, shown to prospective players. Free-text
  comments are deliberately left off this public page since they're
  unmoderated player input.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Frank
2026-08-22 18:41:47 +02:00
co-authored by Claude Sonnet 5
parent 5d9c113eea
commit 2b3c90d3fc
7 changed files with 239 additions and 3 deletions
+39
View File
@@ -30,6 +30,17 @@
color: var(--brand-teal-light, #6fb8d1);
padding-right: .1em;
}
.briefing-feedback {
background: rgba(111, 184, 209, .08);
border: 1px solid #1c5a70;
border-radius: .5rem;
padding: 1.25rem;
}
.briefing-feedback-stat {
font-size: 1.75rem;
font-weight: 700;
color: #6fb8d1;
}
.dev-watermark-wrap {
position: relative;
display: inline-block;
@@ -101,6 +112,34 @@
</p>
</div>
{% if feedbackSummary.count > 0 %}
<div class="briefing-feedback mb-4">
<div class="text-uppercase small mb-3" style="letter-spacing: .1em; color: #6fb8d1;">
Field Reports from {{ feedbackSummary.count }} Agent{{ feedbackSummary.count != 1 ? 's' : '' }}
</div>
<div class="row text-center g-3">
{% if feedbackSummary.avgDifficulty is not null %}
<div class="col-4">
<div class="briefing-feedback-stat">{{ feedbackSummary.avgDifficulty }}/10</div>
<div class="small" style="color: #9fc7d6;">Difficulty</div>
</div>
{% endif %}
{% if feedbackSummary.avgEntertaining is not null %}
<div class="col-4">
<div class="briefing-feedback-stat">{{ feedbackSummary.avgEntertaining }}/10</div>
<div class="small" style="color: #9fc7d6;">Entertainment</div>
</div>
{% endif %}
{% if feedbackSummary.avgTheme is not null %}
<div class="col-4">
<div class="briefing-feedback-stat">{{ feedbackSummary.avgTheme }}/10</div>
<div class="small" style="color: #9fc7d6;">Theme</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
<hr class="my-4" style="border-color: #1c5a70;">
<div class="d-flex flex-wrap justify-content-between align-items-center gap-3">