Files
survey-bundle/contao/templates/frontend/show_survey_results.html.twig
T
admin.juergen c7439e4822 Fix customer-reported survey results and list issues
Behebt die Fehler aus der Kunden-Testrunde (Praktikantin, Juli 2026):

- Ergebnisreihenfolge: Fragen werden vor dem Finalisieren nach _sorting
  sortiert. finalizeQuestionResult entfernt _sorting, daher fiel die
  bisherige Nachsortierung auf den alphabetischen Fallback zurück. Online,
  PDF und Excel folgen jetzt derselben Frage-Sortierung.
- Excel-Export: Eigene Fragetext-Zeile über den Antwortspalten (je Frage
  über die zugehörigen Spalten zusammengeführt). Zuvor fehlte der Fragetext.
- Range-Ergebnis: Verteilung deckt die komplette Skala rangeMin..rangeMax in
  rangeStep-Schritten ab, damit die X-Achse linear und vollständig ist
  (vorher nur tatsächlich vorkommende Werte in gleichen Abständen).
- Teilnahmen/Rücklauf pro Frage als absolute Zahlen ("X von Y beantwortet",
  "Z übersprungen") statt reiner Prozent-Rücklaufquote.
- Freitextantworten gruppiert: jede Antwort einmal mit exakter Häufigkeit,
  ohne Prozentangaben und ohne Doppel-Auflistung.
- Frontend-Liste: "Link kopieren"-Button (absolute Reader-URL) für
  veröffentlichte Umfragen; Aktiv-Umschalter mit Textlabel statt nur Punkt;
  Aktionslinks öffnen im selben Tab statt in neuen Tabs.
- Duplizieren: Titel-Kollisionsprüfung ohne Ausschluss der Quell-ID, damit
  aus "... - Kopie" korrekt "... - Kopie 2" wird statt eines Dubletten-Titels.

Der internalNote-Fehler beim Anlegen/Duplizieren war ein fehlendes
DB-Schema-Update (Spalte tl_survey.internalNote) und wurde per
contao:migrate auf der Testumgebung nachgezogen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 19:30:34 +02:00

156 lines
8.6 KiB
Twig

{% trans_default_domain 'messages' %}
{% include '@Survey/frontend/_survey_assets.html.twig' %}
{% include '@Survey/frontend/_survey_branding.html.twig' %}
{% set moduleHeadline = (headline is iterable ? headline.text|default('') : headline|default(''))|striptags|trim %}
<section class="survey-shell survey-grid">
{% if moduleHeadline %}
<header>
<h2>{{ moduleHeadline }}</h2>
</header>
{% endif %}
{% if errorMessage %}
<div class="survey-alert warning">{{ errorMessage }}</div>
{% elseif survey %}
<article class="survey-card">
<div class="survey-meta">
<h2>{{ survey.title|striptags|trim }}</h2>
<span class="survey-badge neutral">{{ 'survey.results.participations'|trans({'%count%': completedSubmissionCount}) }}</span>
<span class="survey-badge neutral">{{ 'survey.results.questions'|trans({'%count%': questionResults|length}) }}</span>
</div>
<p>{{ (survey.description|striptags|trim) ?: 'survey.results.no_description'|trans }}</p>
{% if downloadUrl or pdfDownloadUrl %}
<div class="survey-button-row">
{% if downloadUrl %}
<a class="survey-button primary" href="{{ downloadUrl }}">{{ 'survey.results.download_excel'|trans }}</a>
{% endif %}
{% if pdfDownloadUrl %}
<a class="survey-button secondary" href="{{ pdfDownloadUrl }}">{{ 'survey.results.download_pdf'|trans }}</a>
{% endif %}
</div>
{% endif %}
</article>
<div class="survey-grid">
{% for question in questionResults %}
<article class="survey-card survey-results-card">
<div class="survey-meta">
<h3>{{ question.question|striptags|trim }}</h3>
<span class="survey-badge neutral">{{ 'survey.results.answered'|trans({'%count%': question.totalAnswers, '%total%': completedSubmissionCount}) }}</span>
{% if question.skippedCount > 0 %}
<span class="survey-badge neutral">{{ 'survey.results.skipped'|trans({'%count%': question.skippedCount}) }}</span>
{% endif %}
</div>
{% if question.description|striptags|trim %}
<p>{{ question.description|striptags|trim }}</p>
{% endif %}
<div class="survey-results-layout{% if not question.chart %} no-chart{% endif %}">
{% if question.chart %}
<div class="survey-chart-panel{% if question.chart.type == 'line' %} is-line{% endif %}">
<div class="survey-chart-frame">
<canvas class="survey-chart-canvas" data-survey-chart="{{ question.chart|json_encode|e('html_attr') }}"></canvas>
</div>
</div>
{% endif %}
<div class="survey-results-panel">
{% if question.type in ['yes_no_maybe', 'choice'] %}
<div class="survey-results-list">
{% for option in question.options %}
<div class="survey-results-row">
<div class="survey-meta">
<strong>{{ option.label|striptags|trim }}</strong>
<span class="survey-badge neutral">{{ option.count }}</span>
<span class="survey-badge neutral">{{ option.percentage }}%</span>
</div>
<div class="survey-progress">
<span class="survey-progress-bar" style="width: {{ option.percentage }}%"></span>
</div>
</div>
{% endfor %}
</div>
{% elseif question.type == 'range' %}
<div class="survey-metric-grid">
<div class="survey-metric-card">
<span class="survey-metric-label">{{ 'survey.results.minimum'|trans }}</span>
<strong>{{ question.minimum is not null ? question.minimum : '-' }}</strong>
</div>
<div class="survey-metric-card">
<span class="survey-metric-label">{{ 'survey.results.maximum'|trans }}</span>
<strong>{{ question.maximum is not null ? question.maximum : '-' }}</strong>
</div>
<div class="survey-metric-card">
<span class="survey-metric-label">{{ 'survey.results.average'|trans }}</span>
<strong>{{ question.average is not null ? question.average : '-' }}</strong>
</div>
</div>
<div class="survey-results-list">
{% for option in question.distribution %}
<div class="survey-results-row">
<div class="survey-meta">
<strong>{{ option.label|striptags|trim }}</strong>
<span class="survey-badge neutral">{{ option.count }}</span>
<span class="survey-badge neutral">{{ option.percentage }}%</span>
</div>
<div class="survey-progress">
<span class="survey-progress-bar" style="width: {{ option.percentage }}%"></span>
</div>
</div>
{% else %}
<p>{{ 'survey.results.no_ratings'|trans }}</p>
{% endfor %}
</div>
{% else %}
{# Eine Liste statt Doppel-Auflistung: jede Antwort einmal,
mit exakter Anzahl statt Prozentwerten. #}
<div class="survey-text-response-grid survey-results-responses">
{% for option in question.responseSummary|default([]) %}
<article class="survey-card survey-response-card">
<span class="survey-badge neutral">{{ 'survey.results.answer_count'|trans({'%count%': option.count}) }}</span>
<blockquote>{{ option.label|striptags|trim|nl2br }}</blockquote>
</article>
{% else %}
<p>{{ 'survey.results.no_text_responses'|trans }}</p>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</article>
{% else %}
<article class="survey-card">{{ 'survey.results.no_questions'|trans }}</article>
{% endfor %}
</div>
{% endif %}
</section>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
if (typeof Chart === 'undefined') {
return;
}
document.querySelectorAll('[data-survey-chart]').forEach(function (canvas) {
try {
var chartConfig = JSON.parse(canvas.getAttribute('data-survey-chart') || '{}');
if (!chartConfig.type || !chartConfig.data) {
return;
}
new Chart(canvas, chartConfig);
} catch (error) {
console.error('Survey chart could not be initialized.', error);
}
});
});
</script>