Default new questions to optional and add opt-in thumb icons for yes/no

Two customer decisions from the September feedback round:

- New questions are now optional by default; the mandatory checkbox is
  ticked deliberately (backend DCA default and frontend editor model).
  Existing questions, templates and duplication are unaffected.
- Yes/no/maybe questions get a per-question display option 'Antworten
  als Daumen darstellen' (showThumbs, e.g. for surveys with children):
  the reader renders thumb-up/down/sideways SVG icons above the answer
  labels. Purely presentational - answers are still stored, evaluated
  and exported as yes/no/maybe, and the 'Keine Antwort' card, jump
  hints and dim logic are unchanged. Icons are inline SVG in CI colors
  (no CDN). Requires contao:migrate for the new column.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
admin.juergenandClaude Fable 5 committed 2026-09-11 11:38:55 +02:00
1 parent ab2dbfbc45
commit fa0bbaa73c
12 files changed
+50 -3

No files matched your search

+9 -2
View File
@@ -73,7 +73,7 @@ $GLOBALS['TL_DCA']['tl_survey_content'] = [
],
'palettes' => [
'default' => '{question_legend},type,question,description;{settings_legend},mandatory,published',
'yes_no_maybe' => '{question_legend},type,question,description;{settings_legend},mandatory,published,allowMaybe,jumpOnYes,jumpOnNo',
'yes_no_maybe' => '{question_legend},type,question,description;{settings_legend},mandatory,published,allowMaybe,showThumbs,jumpOnYes,jumpOnNo',
'text' => '{question_legend},type,question,description;{settings_legend},mandatory,published',
'range' => '{question_legend},type,question,description;{settings_legend},mandatory,published,rangeMin,rangeMax,rangeStep',
'choice' => '{question_legend},type,question,description;{settings_legend},mandatory,published,allowMultiple,answerOption1,answerOption2,answerOption3,answerOption4,answerOption5,answerOption6,answerOption7,answerOption8,answerOption9,answerOption10',
@@ -111,7 +111,8 @@ $GLOBALS['TL_DCA']['tl_survey_content'] = [
'mandatory' => [
'label' => &$GLOBALS['TL_LANG']['tl_survey_content']['mandatory'],
'inputType' => 'checkbox',
'default' => '1',
// Neue Fragen sind bewusst freiwillig; Pflicht wird aktiv angehakt
// (Kundenentscheidung 09/2026, Pretest-Punkt 3).
'eval' => ['tl_class' => 'w50 m12'],
'sql' => "char(1) NOT NULL default ''",
],
@@ -128,6 +129,12 @@ $GLOBALS['TL_DCA']['tl_survey_content'] = [
'eval' => ['tl_class' => 'w50 m12 clr'],
'sql' => "char(1) NOT NULL default ''",
],
'showThumbs' => [
'label' => &$GLOBALS['TL_LANG']['tl_survey_content']['showThumbs'],
'inputType' => 'checkbox',
'eval' => ['tl_class' => 'w50 m12'],
'sql' => "char(1) NOT NULL default ''",
],
'jumpOnYes' => [
'label' => &$GLOBALS['TL_LANG']['tl_survey_content']['jumpOnYes'],
'inputType' => 'select',
@@ -15,6 +15,7 @@ $GLOBALS['TL_LANG']['tl_survey_content']['settings_legend'] = 'Einstellungen';
$GLOBALS['TL_LANG']['tl_survey_content']['mandatory'] = ['Pflichtfrage', 'Diese Frage muss beantwortet werden.'];
$GLOBALS['TL_LANG']['tl_survey_content']['published'] = ['Aktiv', 'Die Frage wird in der Umfrage angezeigt.'];
$GLOBALS['TL_LANG']['tl_survey_content']['allowMaybe'] = ['Vielleicht anbieten', 'Ergänzt die Ja-Nein-Frage um die Antwortmöglichkeit Vielleicht.'];
$GLOBALS['TL_LANG']['tl_survey_content']['showThumbs'] = ['Antworten als Daumen darstellen', 'Zeigt bei Ja/Nein/Vielleicht zusätzlich Daumen-Symbole an (z. B. für Befragungen mit Kindern). Gespeichert und ausgewertet werden weiterhin die Antworten Ja, Nein und Vielleicht.'];
$GLOBALS['TL_LANG']['tl_survey_content']['jumpOnYes'] = ['Zur Frage springen bei Auswahl ja', 'Springt bei der Antwort Ja direkt zu dieser Frage.'];
$GLOBALS['TL_LANG']['tl_survey_content']['jumpOnNo'] = ['Zur Frage springen bei Auswahl nein', 'Springt bei der Antwort Nein direkt zu dieser Frage.'];
$GLOBALS['TL_LANG']['tl_survey_content']['allowMultiple'] = ['Multiple-Choice aktivieren', 'Erlaubt mehrere Antworten statt einer einzelnen Auswahl.'];
@@ -15,6 +15,7 @@ $GLOBALS['TL_LANG']['tl_survey_content']['settings_legend'] = 'Settings';
$GLOBALS['TL_LANG']['tl_survey_content']['mandatory'] = ['Required question', 'This question must be answered.'];
$GLOBALS['TL_LANG']['tl_survey_content']['published'] = ['Active', 'The question is shown in the survey.'];
$GLOBALS['TL_LANG']['tl_survey_content']['allowMaybe'] = ['Offer maybe', 'Adds the Maybe answer option to the yes-no question.'];
$GLOBALS['TL_LANG']['tl_survey_content']['showThumbs'] = ['Show thumb icons', 'Displays thumb icons for yes/no/maybe answers (e.g. for surveys with children). Answers are still stored and evaluated as yes, no and maybe.'];
$GLOBALS['TL_LANG']['tl_survey_content']['jumpOnYes'] = ['Jump to question on yes', 'Jumps directly to this question when the answer is Yes.'];
$GLOBALS['TL_LANG']['tl_survey_content']['jumpOnNo'] = ['Jump to question on no', 'Jumps directly to this question when the answer is No.'];
$GLOBALS['TL_LANG']['tl_survey_content']['allowMultiple'] = ['Enable multiple choice', 'Allows multiple answers instead of a single selection.'];
@@ -31,6 +31,7 @@
<label class="survey-inline-check">{{ form_widget(form.mandatory) }}<span>{{ 'survey.edit.mandatory'|trans }}</span></label>
<label class="survey-inline-check">{{ form_widget(form.published) }}<span>{{ 'survey.edit.active'|trans }}</span></label>
<label class="survey-inline-check yes-no-config-field"{% if questionType != 'yes_no_maybe' %} style="display:none"{% endif %}>{{ form_widget(form.allowMaybe) }}<span>{{ 'survey.edit.allow_maybe'|trans }}</span></label>
<label class="survey-inline-check yes-no-config-field"{% if questionType != 'yes_no_maybe' %} style="display:none"{% endif %}>{{ form_widget(form.showThumbs) }}<span>{{ 'survey.edit.show_thumbs'|trans }}</span></label>
<label class="survey-inline-check choice-config-field"{% if questionType != 'choice' %} style="display:none"{% endif %}>{{ form_widget(form.allowMultiple) }}<span>{{ 'survey.edit.allow_multiple'|trans }}</span></label>
</div>
<div class="yes-no-config-field survey-grid-columns" style="grid-template-columns: repeat(2, minmax(0, 1fr));{% if questionType != 'yes_no_maybe' %}display:none;{% endif %}">
@@ -79,9 +79,16 @@
{% set answerChildren = surveyForm.answer is defined ? surveyForm.answer|filter(child => child.vars.value is not defined or child.vars.value != '__no_answer__') : [] %}
{% set noAnswerChildren = surveyForm.answer is defined ? surveyForm.answer|filter(child => child.vars.value is defined and child.vars.value == '__no_answer__') : [] %}
{% if question.type == 'yes_no_maybe' %}
{# Reine Darstellungsoption: gespeichert wird weiterhin yes/no/maybe. #}
{% set showThumbIcons = '1' == question.showThumbs %}
<div class="survey-grid" style="grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));" data-survey-answer-zone>
{% for child in answerChildren %}
<label class="survey-card" style="cursor: pointer;">
{% if showThumbIcons and child.vars.value in ['yes', 'no', 'maybe'] %}
<span class="survey-thumb survey-thumb--{{ child.vars.value }}" aria-hidden="true">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" focusable="false"><path fill="currentColor" d="M2 20h2c.55 0 1-.45 1-1v-9c0-.55-.45-1-1-1H2v11zm19.83-7.12c.11-.25.17-.52.17-.8V11c0-1.1-.9-2-2-2h-5.5l.92-4.65c.05-.22.02-.46-.08-.66-.23-.45-.52-.86-.88-1.22L14 2 7.59 8.41C7.21 8.79 7 9.3 7 9.83v7.84C7 18.95 8.05 20 9.34 20h8.11c.7 0 1.36-.37 1.72-.97l2.66-6.15z"/></svg>
</span>
{% endif %}
<div class="survey-meta"><strong>{{ child.vars.label|striptags|trim }}</strong>{{ form_widget(child) }}</div>
{% set jumpHint = jumpHints|default({})[child.vars.value]|default(null) %}
{% if jumpHint %}
+15
View File
@@ -735,6 +735,21 @@
flex-wrap: wrap;
}
/* Optionale Daumen-Darstellung bei Ja/Nein/Vielleicht (z. B. Kinderbefragungen).
Reine Anzeige – gespeichert wird weiterhin yes/no/maybe. */
.survey-thumb {
display: flex;
justify-content: center;
margin: 0.2rem auto 0.55rem;
}
.survey-thumb svg { width: 46px; height: 46px; }
.survey-thumb--yes { color: var(--survey-green); }
.survey-thumb--no { color: var(--survey-orange); }
.survey-thumb--maybe { color: var(--survey-gray-dark); }
.survey-thumb--no svg { transform: rotate(180deg); }
.survey-thumb--maybe svg { transform: rotate(90deg); }
.survey-preview-bar .survey-preview-restart {
flex-shrink: 0;
padding: 0.45rem 0.95rem;
+6 -1
View File
@@ -11,9 +11,12 @@ final class SurveyQuestionData
public string $type = 'yes_no_maybe';
public string $question = '';
public ?string $description = '';
public bool $mandatory = true;
// Neue Fragen sind bewusst freiwillig; Pflicht wird aktiv angehakt
// (Kundenentscheidung 09/2026, Pretest-Punkt 3).
public bool $mandatory = false;
public bool $published = true;
public bool $allowMaybe = false;
public bool $showThumbs = false;
public bool $allowMultiple = false;
public ?int $jumpOnYes = 0;
public ?int $jumpOnNo = 0;
@@ -46,6 +49,7 @@ final class SurveyQuestionData
$data->mandatory = '1' === (string) $question->mandatory;
$data->published = '1' === (string) $question->published;
$data->allowMaybe = '1' === (string) $question->allowMaybe;
$data->showThumbs = '1' === (string) $question->showThumbs;
$data->allowMultiple = '1' === (string) $question->allowMultiple;
$data->jumpOnYes = (int) $question->jumpOnYes;
$data->jumpOnNo = (int) $question->jumpOnNo;
@@ -74,6 +78,7 @@ final class SurveyQuestionData
'mandatory' => $this->mandatory,
'published' => $this->published,
'allowMaybe' => $this->allowMaybe,
'showThumbs' => $this->showThumbs,
'allowMultiple' => $this->allowMultiple,
'jumpOnYes' => max(0, (int) ($this->jumpOnYes ?? 0)),
'jumpOnNo' => max(0, (int) ($this->jumpOnNo ?? 0)),
+5
View File
@@ -71,6 +71,11 @@ final class SurveyQuestionEditorType extends AbstractType
'required' => false,
'attr' => ['data-question-editor-target' => 'yesNoMaybeField'],
])
->add('showThumbs', CheckboxType::class, [
'label' => 'Antworten als Daumen darstellen',
'required' => false,
'attr' => ['data-question-editor-target' => 'yesNoMaybeField'],
])
->add('jumpOnYes', ChoiceType::class, [
'label' => 'Zur Frage springen bei Auswahl ja',
'required' => false,
@@ -298,6 +298,7 @@ final class SurveyQuestionRepository
'mandatory' => !$isSection && !empty($data['mandatory']) ? '1' : '',
'published' => $isSection || !empty($data['published']) ? '1' : '',
'allowMaybe' => $isYesNo && !empty($data['allowMaybe']) ? '1' : '',
'showThumbs' => $isYesNo && !empty($data['showThumbs']) ? '1' : '',
'jumpOnYes' => $isYesNo ? max(0, (int) ($data['jumpOnYes'] ?? $existing?->jumpOnYes ?? 0)) : 0,
'jumpOnNo' => $isYesNo ? max(0, (int) ($data['jumpOnNo'] ?? $existing?->jumpOnNo ?? 0)) : 0,
'allowMultiple' => $isChoice && !empty($data['allowMultiple']) ? '1' : '',
+2
View File
@@ -153,6 +153,7 @@ final class SurveyEditorService
'mandatory' => '1' === (string) $sourceQuestion->mandatory,
'published' => '1' === (string) $sourceQuestion->published,
'allowMaybe' => '1' === (string) ($sourceQuestion->allowMaybe ?? ''),
'showThumbs' => '1' === (string) ($sourceQuestion->showThumbs ?? ''),
'allowMultiple' => '1' === (string) ($sourceQuestion->allowMultiple ?? ''),
'answerOption1' => (string) ($sourceQuestion->answerOption1 ?? ''),
'answerOption2' => (string) ($sourceQuestion->answerOption2 ?? ''),
@@ -191,6 +192,7 @@ final class SurveyEditorService
'mandatory' => '1' === (string) $sourceQuestion->mandatory,
'published' => '1' === (string) $sourceQuestion->published,
'allowMaybe' => '1' === (string) ($sourceQuestion->allowMaybe ?? ''),
'showThumbs' => '1' === (string) ($sourceQuestion->showThumbs ?? ''),
'allowMultiple' => '1' === (string) ($sourceQuestion->allowMultiple ?? ''),
'answerOption1' => (string) ($sourceQuestion->answerOption1 ?? ''),
'answerOption2' => (string) ($sourceQuestion->answerOption2 ?? ''),
+1
View File
@@ -88,6 +88,7 @@ survey:
mandatory: "Pflichtfrage"
active: "Aktiv"
allow_maybe: "Vielleicht anbieten"
show_thumbs: "Antworten als Daumen darstellen"
jump_yes: "Zur Frage springen bei Auswahl ja"
jump_no: "Zur Frage springen bei Auswahl nein"
allow_multiple: "Multiple-Choice aktivieren"
+1
View File
@@ -88,6 +88,7 @@ survey:
mandatory: "Required question"
active: "Active"
allow_maybe: "Offer maybe"
show_thumbs: "Show thumb icons"
jump_yes: "Jump to question on yes"
jump_no: "Jump to question on no"
allow_multiple: "Enable multiple choice"