Refine survey editor workflow and publication handling

This commit is contained in:
Jürgen Mummert
2026-05-29 10:19:19 +02:00
parent 6bbded4181
commit 1174410c1b
40 changed files with 1121 additions and 335 deletions
@@ -189,12 +189,50 @@
padding-right: 0.25rem;
}
.survey-text-summary-grid {
display: grid;
gap: 0.9rem;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.survey-text-summary-card {
display: grid;
gap: 0.6rem;
padding: 1rem;
border-radius: 1.2rem;
background: rgba(255, 255, 255, 0.78);
border: 1px solid rgba(162, 168, 180, 0.24);
}
.survey-text-response-grid {
display: grid;
gap: 0.9rem;
}
.survey-response-card {
display: grid;
gap: 0.7rem;
margin: 0;
font-style: normal;
background: rgba(255, 255, 255, 0.72);
}
.survey-response-card blockquote {
margin: 0;
}
.survey-response-card__index {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
border-radius: 999px;
background: rgba(0, 100, 173, 0.1);
color: var(--survey-blue);
font-weight: 700;
}
.survey-metric-grid {
display: grid;
gap: 0.85rem;
@@ -338,6 +376,18 @@
align-items: center;
}
.survey-action-stack {
display: grid;
gap: 0.35rem;
}
.survey-action-help {
color: var(--survey-gray-dark);
font-size: 0.92rem;
line-height: 1.35;
max-width: 28rem;
}
.survey-publish-toggle-form {
margin: 0;
}
@@ -427,6 +477,12 @@
.survey-button.primary { background: var(--survey-blue); color: #fff; }
.survey-button.secondary { background: rgba(255,255,255,0.86); color: var(--survey-blue); border-color: rgba(0,100,173,0.18); }
.survey-button.danger { background: rgba(157, 82, 118, 0.12); color: var(--survey-purple); border-color: rgba(157, 82, 118, 0.22); }
.survey-button.is-disabled,
.survey-button[disabled] {
opacity: 0.45;
cursor: not-allowed;
pointer-events: none;
}
.survey-button-compact {
padding: 0.65rem 0.9rem;
@@ -46,41 +46,32 @@
{{ form_end(surveyForm, {render_rest: false}) }}
</div>
{% else %}
{% set locked = survey.isLocked == '1' %}
<div class="survey-button-row survey-button-row--editor-actions">
{% if backUrl %}<a class="survey-button secondary" href="{{ backUrl }}">{{ 'survey.edit.back_to_list'|trans }}</a>{% endif %}
{% if publicUrl %}<a class="survey-button primary" href="{{ publicUrl }}" target="_blank" rel="noreferrer">{{ 'survey.edit.public_view_open'|trans }}</a>{% endif %}
{% if publicUrl %}
<button
type="button"
class="survey-button secondary"
data-copy-text="{{ publicUrl|e('html_attr') }}"
data-copy-label="{{ 'survey.edit.copy_public_link'|trans|e('html_attr') }}"
data-copy-success="{{ 'survey.edit.copy_public_link_success'|trans|e('html_attr') }}"
>{{ 'survey.edit.copy_public_link'|trans }}</button>
{% endif %}
<form method="post" class="survey-publish-toggle-form">
<input type="hidden" name="REQUEST_TOKEN" value="{{ contao.request_token }}">
<input type="hidden" name="_survey_action" value="toggle-published">
<input type="hidden" name="item_id" value="{{ survey.id }}">
<input type="hidden" name="_token" value="{{ csrf_token('toggle-published-' ~ survey.id) }}">
<button
type="submit"
class="survey-publish-toggle{{ survey.published ? ' is-on' : '' }}"
aria-pressed="{{ survey.published ? 'true' : 'false' }}"
>
<span class="survey-publish-toggle__track" aria-hidden="true">
<span class="survey-publish-toggle__thumb"></span>
</span>
<span class="survey-publish-toggle__copy">
<span class="survey-publish-toggle__label">{{ 'survey.edit.status'|trans }}</span>
<span class="survey-publish-toggle__value">{{ survey.published ? 'survey.edit.published'|trans : 'survey.edit.draft'|trans }}</span>
</span>
</button>
</form>
<div class="survey-action-stack">
{% if not survey.published %}
<form method="post" class="survey-action-form">
<input type="hidden" name="REQUEST_TOKEN" value="{{ contao.request_token }}">
<input type="hidden" name="_survey_action" value="publish-survey">
<input type="hidden" name="item_id" value="{{ survey.id }}">
<input type="hidden" name="_token" value="{{ csrf_token('publish-survey-' ~ survey.id) }}">
<button class="survey-button primary" onclick="return confirm('{{ 'survey.edit.publish_confirm'|trans|e('js') }}');">{{ 'survey.edit.publish_survey'|trans }}</button>
</form>
<div class="survey-action-help">{{ 'survey.edit.publish_help'|trans }}</div>
{% else %}
<form method="post" class="survey-action-form">
<input type="hidden" name="REQUEST_TOKEN" value="{{ contao.request_token }}">
<input type="hidden" name="_survey_action" value="toggle-active">
<input type="hidden" name="item_id" value="{{ survey.id }}">
<input type="hidden" name="_token" value="{{ csrf_token('toggle-active-' ~ survey.id) }}">
<button class="survey-button secondary">{{ survey.isActive ? 'survey.edit.deactivate_survey'|trans : 'survey.edit.activate_survey'|trans }}</button>
</form>
<div class="survey-action-help">{{ survey.isActive ? 'survey.edit.deactivate_help'|trans : 'survey.edit.activate_help'|trans }}</div>
{% endif %}
</div>
</div>
{% if locked %}
{% if structureLocked|default(false) %}
<div class="survey-alert warning">{{ 'survey.edit.structure_locked'|trans }}</div>
{% endif %}
@@ -147,7 +138,15 @@
</h4>
<div class="survey-accordion-panel handorgel__content">
<div class="handorgel__content__inner" data-survey-question-editor>
{% if questionItemForm %}
{% if structureLocked|default(false) %}
<div class="survey-grid">
<div class="survey-meta">
<span class="survey-badge neutral">{{ ('survey.survey.question_type_' ~ question.type)|trans }}</span>
{% if question.mandatory %}<span class="survey-badge warning">{{ 'survey.edit.mandatory_badge'|trans }}</span>{% endif %}
</div>
{% if question.description|striptags|trim %}<p>{{ question.description|striptags|trim }}</p>{% endif %}
</div>
{% elseif questionItemForm %}
{% set questionType = questionItemForm.type.vars.value ?: question.type ?: 'yes_no_maybe' %}
{{ form_start(questionItemForm, {attr: {class: 'survey-form-grid', id: questionFormId, 'data-survey-dirty-form': '1'}}) }}
<input type="hidden" name="REQUEST_TOKEN" value="{{ contao.request_token }}">
@@ -186,15 +185,13 @@
<div class="survey-button-row survey-button-row--form-actions survey-button-row--question-actions">
<button class="survey-button primary" type="submit" form="{{ questionFormId }}">{{ 'survey.edit.save_this_question'|trans }}</button>
{% if not locked %}
<form method="post" class="survey-question-action-form">
<input type="hidden" name="REQUEST_TOKEN" value="{{ contao.request_token }}">
<input type="hidden" name="_survey_action" value="delete-question">
<input type="hidden" name="item_id" value="{{ question.id }}">
<input type="hidden" name="_token" value="{{ csrf_token('delete-question-' ~ question.id) }}">
<button class="survey-button danger" onclick="return confirm('{{ 'survey.edit.delete_question_confirm'|trans|e('js') }}');">{{ 'survey.edit.delete'|trans }}</button>
</form>
{% endif %}
<form method="post" class="survey-question-action-form">
<input type="hidden" name="REQUEST_TOKEN" value="{{ contao.request_token }}">
<input type="hidden" name="_survey_action" value="delete-question">
<input type="hidden" name="item_id" value="{{ question.id }}">
<input type="hidden" name="_token" value="{{ csrf_token('delete-question-' ~ question.id) }}">
<button class="survey-button danger" onclick="return confirm('{{ 'survey.edit.delete_question_confirm'|trans|e('js') }}');">{{ 'survey.edit.delete'|trans }}</button>
</form>
<div class="survey-dirty-hint" data-survey-dirty-hint-for="{{ questionFormId }}" hidden>{{ 'survey.edit.dirty_question'|trans }}</div>
</div>
{% endif %}
@@ -205,7 +202,7 @@
<div>{{ 'survey.edit.no_questions'|trans }}</div>
{% endfor %}
{% if not locked %}
{% if not (structureLocked|default(false)) %}
<article id="survey-question-new" class="survey-accordion-item">
<h4 class="survey-accordion-header handorgel__header">
<button
@@ -266,7 +263,7 @@
</div>
</div>
</article>
{% endif %}
{% endif %}
</div>
</div>
@@ -35,31 +35,15 @@
<div class="survey-list-card__head">
<div class="survey-list-card__title-wrap">
<h3>{{ survey.title|striptags|trim }}</h3>
{% if survey.isLocked %}
<div class="survey-meta">
<div class="survey-meta">
<span class="survey-badge {{ survey.published ? 'success' : 'warning' }}">{{ survey.published ? 'survey.list.published'|trans : 'survey.list.draft'|trans }}</span>
{% if survey.published %}
<span class="survey-badge {{ survey.isActive ? 'success' : 'warning' }}">{{ survey.isActive ? 'survey.list.active'|trans : 'survey.list.inactive'|trans }}</span>
{% elseif survey.isLocked %}
<span class="survey-badge warning">{{ 'survey.list.locked'|trans }}</span>
</div>
{% endif %}
{% endif %}
</div>
</div>
<form method="post" class="survey-publish-toggle-form survey-list-card__toggle-form">
<input type="hidden" name="REQUEST_TOKEN" value="{{ contao.request_token }}">
<input type="hidden" name="_survey_action" value="toggle-published">
<input type="hidden" name="item_id" value="{{ survey.id }}">
<input type="hidden" name="_token" value="{{ csrf_token('toggle-published-' ~ survey.id) }}">
<button
type="submit"
class="survey-publish-toggle survey-publish-toggle--compact{{ survey.published ? ' is-on' : '' }}"
aria-pressed="{{ survey.published ? 'true' : 'false' }}"
>
<span class="survey-publish-toggle__track" aria-hidden="true">
<span class="survey-publish-toggle__thumb"></span>
</span>
<span class="survey-publish-toggle__copy">
<span class="survey-publish-toggle__label">{{ 'survey.edit.status'|trans }}</span>
<span class="survey-publish-toggle__value">{{ survey.published ? 'survey.list.published'|trans : 'survey.list.draft'|trans }}</span>
</span>
</button>
</form>
</div>
<div class="survey-meta survey-list-card__stats">
<span class="survey-badge neutral">{{ 'survey.list.questions'|trans({'%count%': survey.questionCount}) }}</span>
@@ -69,8 +53,13 @@
{% if survey.editUrl %}
<a class="survey-button primary" href="{{ survey.editUrl }}">{{ 'survey.list.edit'|trans }}</a>
{% endif %}
{% if survey.publicUrl %}
{% if survey.publicUrl and survey.published and survey.isActive %}
<a class="survey-button secondary" href="{{ survey.publicUrl }}" target="_blank" rel="noreferrer">{{ 'survey.list.public_view'|trans }}</a>
{% elseif survey.publicUrl %}
<span class="survey-button secondary is-disabled" aria-disabled="true">{{ 'survey.list.public_view'|trans }}</span>
{% endif %}
{% if survey.draftUrl and not survey.published %}
<a class="survey-button secondary" href="{{ survey.draftUrl }}" target="_blank" rel="noreferrer">{{ 'survey.list.draft_view'|trans }}</a>
{% endif %}
{% if survey.resultsUrl %}
<a class="survey-button secondary" href="{{ survey.resultsUrl }}" target="_blank" rel="noreferrer">{{ 'survey.list.show_results'|trans }}</a>
@@ -16,10 +16,15 @@
<article class="survey-card">
<div class="survey-meta">
<h3>{{ survey.title|striptags|trim }}</h3>
{% if isPreview %}<span class="survey-badge warning">{{ 'survey.survey.preview_badge'|trans }}</span>{% endif %}
{% if progress.total > 0 %}<span class="survey-badge neutral">{{ progress.current }}/{{ progress.total }}</span>{% endif %}
</div>
<p>{{ (survey.description|striptags|trim) ?: 'survey.survey.default_description'|trans }}</p>
{% if isPreview %}
<div class="survey-alert warning">{{ 'survey.survey.preview_notice'|trans }}</div>
{% endif %}
{% if progress.total > 0 %}
<div class="survey-progress"><span style="width: {{ progress.percentage }}%"></span></div>
{% endif %}
@@ -28,7 +33,7 @@
{% if isComplete %}
<article class="survey-card">
<h3>{{ 'survey.survey.thanks_title'|trans }}</h3>
<p>{{ 'survey.survey.thanks_text'|trans }}</p>
<p>{{ isPreview ? 'survey.survey.preview_thanks_text'|trans : 'survey.survey.thanks_text'|trans }}</p>
{% if answers %}
<div class="survey-grid">
{% for answer in answers %}
@@ -47,8 +52,25 @@
<h3>{{ question.question|striptags|trim }}</h3>
{% if question.description|striptags|trim %}<p>{{ question.description|striptags|trim }}</p>{% endif %}
{% if canGoBack %}
<form method="post" class="survey-button-row survey-button-row--navigation">
<input type="hidden" name="REQUEST_TOKEN" value="{{ contao.request_token }}">
<input type="hidden" name="_survey_navigation" value="back">
<input type="hidden" name="_navigation_token" value="{{ csrf_token((isPreview ? 'survey-preview-back-' : 'survey-back-') ~ survey.id) }}">
{% if isPreview %}
<input type="hidden" name="preview_question" value="{{ question.id }}">
<input type="hidden" name="preview_history" value="{{ historyState }}">
{% endif %}
<button type="submit" class="survey-button secondary">{{ 'survey.survey.back'|trans }}</button>
</form>
{% endif %}
{{ form_start(surveyForm, {attr: {class: 'survey-form-grid'}}) }}
<input type="hidden" name="REQUEST_TOKEN" value="{{ contao.request_token }}">
{% if isPreview %}
<input type="hidden" name="preview_question" value="{{ question.id }}">
<input type="hidden" name="preview_history" value="{{ historyState }}">
{% endif %}
{% if question.type == 'yes_no_maybe' %}
<div class="survey-grid" style="grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));">
{% for child in surveyForm.answer %}
@@ -108,25 +108,25 @@
</div>
{% else %}
{% if question.responseSummary is defined and question.responseSummary %}
<div class="survey-results-list">
<div class="survey-text-summary-grid">
{% for option in question.responseSummary %}
<div class="survey-results-row">
<article class="survey-text-summary-card">
<strong>{{ option.label|striptags|trim }}</strong>
<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>
</article>
{% endfor %}
</div>
{% endif %}
<div class="survey-results-list survey-results-responses">
<div class="survey-text-response-grid survey-results-responses">
{% for response in question.responses %}
<blockquote class="survey-card survey-response-card">{{ response|striptags|trim|nl2br }}</blockquote>
<article class="survey-card survey-response-card">
<span class="survey-response-card__index">{{ loop.index }}</span>
<blockquote>{{ response|striptags|trim|nl2br }}</blockquote>
</article>
{% else %}
<p>{{ 'survey.results.no_text_responses'|trans }}</p>
{% endfor %}