[IMP] survey: apply small survey form design changes

This commit slightly improves the survey form by adding a few cosmetic changes,
notably:

- Add/reduce some margins/paddings
- Adjust colors/font-weight
- Adjust heights
- Change some icons by more fitting replacements
- Center matrix choices
- Move survey progression to bottom-right
- Remove ugly jumbotrons
- Hide survey title in page_per_question layout
- When there are 5 or less option for simple_choice or multiple_choice
  questions, display the options vertically.
- Only show the textarea for the "comment answer" if the user clicks on the
  related option.

One of the biggest design change is the introduction of a "minimized layout"
that reduces the width of some question types on bigger screen.
This gives a cleaner look to the survey form.
Impacted question types:
- Numeric
- Date
- Datetime
- Simple / Multiple choices

Technical note: As the survey progression is now outside of the survey content,
that was re-rendered on every submit, we have to manually refresh the
progression with a second template render on the controller side.

Small fix addition:
We also fix the matrix key selection feature in case the matrix had a total of
more than 25 options, which would raise an error.
Indeed, they "use key selection" condition only checked the number of rows,
and not the rows * columns (total number of options count).

LINKS

PR #46768
Task 2208574
This commit is contained in:
Aurélien Warnon
2020-03-31 15:43:03 +00:00
parent 6405ffd483
commit 695294b953
9 changed files with 241 additions and 127 deletions
+28 -3
View File
@@ -311,10 +311,35 @@ class Survey(http.Controller):
def _prepare_question_html(self, survey_sudo, answer_sudo, **post):
""" Survey page navigation is done in AJAX. This function prepare the 'next page' to display in html
and send back this html to the survey_form widget that will inject it into the page."""
data = self._prepare_survey_data(survey_sudo, answer_sudo, **post)
survey_data = self._prepare_survey_data(survey_sudo, answer_sudo, **post)
survey_content = False
if answer_sudo.state == 'done':
return request.env.ref('survey.survey_fill_form_done').render(data)
return request.env.ref('survey.survey_fill_form_in_progress').render(data)
survey_content = request.env.ref('survey.survey_fill_form_done').render(survey_data)
else:
survey_content = request.env.ref('survey.survey_fill_form_in_progress').render(survey_data)
survey_progress = False
if answer_sudo.state == 'in_progress' and not survey_data.get('question', request.env['survey.question']).is_page:
if survey_sudo.questions_layout == 'page_per_section':
page_ids = survey_sudo.page_ids.ids
survey_progress = request.env.ref('survey.survey_progression').render({
'survey': survey_sudo,
'page_ids': page_ids,
'page_number': page_ids.index(survey_data['page'].id) + (1 if survey_sudo.progression_mode == 'number' else 0)
})
elif survey_sudo.questions_layout == 'page_per_question':
page_ids = survey_sudo.question_ids.ids
survey_progress = request.env.ref('survey.survey_progression').render({
'survey': survey_sudo,
'page_ids': page_ids,
'page_number': page_ids.index(survey_data['question'].id)
})
return {
'survey_content': survey_content,
'survey_progress': survey_progress
}
@http.route('/survey/<string:survey_token>/<string:answer_token>', type='http', auth='public', website=True)
def survey_display_page(self, survey_token, answer_token, **post):
@@ -14,6 +14,7 @@
<field name="access_token">4ead4bc8-b8f2-4760-a682-1fde8ddb95ac</field>
<field name="state">open</field>
<field name="access_mode">public</field>
<field name="questions_layout">page_per_question</field>
<field name="users_can_go_back" eval="True" />
<field name="users_login_required" eval="True" />
<field name="scoring_type" >scoring_with_answers</field>
+21 -3
View File
@@ -55,6 +55,10 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
self.preventEnterSubmit = true;
}
self._initSessionManagement();
// Needs global selector as the progress is not within the survey form, but needs to be
// updated at the same time
self.$surveyProgress = $('.o_survey_progress_wrapper');
});
},
@@ -99,8 +103,8 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
/**
* Checks, if the 'other' choice is checked. Applies only if the comment count as answer.
* If not checked : Clear the comment textarea and disable it
* If checked : enable the comment textarea and focus on it
* If not checked : Clear the comment textarea, hide and disable it
* If checked : enable the comment textarea, show and focus on it
*
* @private
* @param {Event} event
@@ -114,11 +118,13 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
if ($otherItem.prop('checked') || $commentInput.hasClass('o_survey_comment')) {
$commentInput.enable();
$commentInput.closest('.o_survey_comment_container').removeClass('d-none');
if ($otherItem.prop('checked')) {
$commentInput.focus();
}
} else {
$commentInput.val('');
$commentInput.closest('.o_survey_comment_container').addClass('d-none');
$commentInput.enable(false);
}
@@ -283,6 +289,8 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
this.fadeInOutDelay = 400;
}
this.$('.o_survey_main_title:visible').fadeOut(400);
this.preventEnterSubmit = false;
this.readonly = false;
this._nextScreen(
@@ -324,6 +332,10 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
if (this.options.isStartScreen) {
route = "/survey/begin";
// Hide survey title in 'page_per_question' layout: it takes too much space
if (this.options.questionsLayout === 'page_per_question') {
this.$('.o_survey_main_title').fadeOut(400);
}
} else {
var $form = this.$('form');
var formData = new FormData($form[0]);
@@ -396,7 +408,13 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
if (result && !result.error) {
this.$(".o_survey_form_content").empty();
this.$(".o_survey_form_content").html(result);
this.$(".o_survey_form_content").html(result.survey_content);
if (result.survey_progress && this.$surveyProgress.length !== 0) {
this.$surveyProgress.html(result.survey_progress);
} else if (options.isFinish && this.$surveyProgress.length !== 0) {
this.$surveyProgress.remove();
}
// Hide timer if end screen (if page_per_question in case of conditional questions)
if (self.options.questionsLayout === 'page_per_question' && this.$('.o_survey_finished').length > 0) {
+74 -21
View File
@@ -18,6 +18,19 @@ nav#oe_main_menu_navbar {
min-height: 100%;
}
.o_survey_progress_wrapper {
min-width: 11rem;
max-width: 15rem;
.o_survey_progress {
height:0.5em;
}
}
.o_survey_brand_message {
background-color: rgba(255,255,255,0.7);
}
.o_survey_form, .o_survey_print, .o_survey_session_manage, .o_survey_quick_access {
.o_survey_question_error {
height: 0px;
@@ -42,27 +55,27 @@ nav#oe_main_menu_navbar {
.o_survey_question_datetime,
.o_survey_question_numerical_box {
border: 0px;
border-bottom: 2px solid $primary;
border-bottom: 1px solid $primary;
&:disabled {
color: black !important;
border-color: $gray-600;
border-bottom: 2px solid $gray-600;
border-bottom: 1px solid $gray-600;
}
&:focus {
border-color: $primary;
box-shadow: none;
}
}
.o_survey_form_date .input-group-append {
right: 0;
.input-group-text i {
font-size: large;
}
bottom: 5px;
top: auto;
}
.o_survey_choice_btn {
transition: background-color 0.3s ease;
flex: 1 0 300px;
color: $primary;
span {
line-height: 25px;
@@ -83,6 +96,24 @@ nav#oe_main_menu_navbar {
}
}
input::placeholder, textarea::placeholder {
font-weight: 300;
}
.o_survey_page_per_question.o_survey_simple_choice.o_survey_minimized_display,
.o_survey_page_per_question.o_survey_multiple_choice.o_survey_minimized_display,
.o_survey_page_per_question.o_survey_numerical_box,
.o_survey_page_per_question.o_survey_date,
.o_survey_page_per_question.o_survey_datetime {
// 'pixel perfect' layouting for choice questions having less than 5 choices in page_per_question mode
// we use media queries instead of bootstrap classes because they don't provide everything needed here
@media (min-width: 768px) {
width: 50%;
position: relative;
left: 25%;
}
}
.o_survey_question_matrix {
td {
min-width: 100px;
@@ -93,8 +124,13 @@ nav#oe_main_menu_navbar {
display: inline;
}
}
.o_survey_choice_key > span > span {
top: 0px;
.o_survey_choice_key {
left: 10px;
right: auto;
top: 12px;
> span > span {
top: 0px;
}
}
&.o_survey_selected {
@@ -106,6 +142,22 @@ nav#oe_main_menu_navbar {
}
}
}
thead {
th:first-child {
border-top-left-radius: .25rem;
}
th:last-child {
border-top-right-radius: .25rem;
}
}
tbody tr:last-child {
th {
border-bottom-left-radius: .25rem;
}
td:last-child {
border-bottom-right-radius: .25rem;
}
}
}
}
@@ -125,23 +177,19 @@ nav#oe_main_menu_navbar {
**********************************************************/
.o_survey_form {
min-height: 100%;
.o_survey_progress {
height:0.5em;
}
min-height: 25rem;
.o_survey_choice_btn {
cursor: pointer;
background-color: rgba($primary, 0.5);
border: 2px solid $primary;
background-color: rgba($primary, 0.1);
box-shadow: $primary 0px 0px 0px 1px;
&.o_survey_selected {
background-color: $primary;
box-shadow: $primary 0px 0px 0px 2px;
}
&:hover {
background-color: rgba($primary, 0.8);
background-color: rgba($primary, 0.3);
.o_survey_choice_key span.o_survey_key {
opacity: 1;
}
@@ -151,19 +199,22 @@ nav#oe_main_menu_navbar {
.o_survey_choice_key {
width: 25px;
height: 25px;
border: 2px solid $primary;
border: 1px solid $primary;
span {
font-size: smaller;
top: -2px;
top: -1px;
&.o_survey_key {
width: inherit;
right: 10px;
border: 2px solid $primary;
border: 1px solid $primary;
border-right: 0px;
height: 25px;
transition: opacity 0.4s ease;
white-space: nowrap;
opacity: 0;
span {
top: -2px;
}
}
}
}
@@ -318,6 +369,8 @@ nav#oe_main_menu_navbar {
background-color: $gray-500;
border-color: transparent;
cursor: default;
color: white; // not bootstrap to customize for survey_print only
font-weight: bold; // not bootstrap to customize for survey_print only
&.bg-success, &.bg-danger {
opacity: 0.6;
@@ -326,7 +379,7 @@ nav#oe_main_menu_navbar {
background-color: $gray-600;
opacity: 1;
}
i.fa-square, i.fa-circle {
i.fa-square-o, i.fa-circle-thin {
display: none;
}
}
+2 -2
View File
@@ -14,11 +14,11 @@
<button name="action_result_survey" string="See results" type="object" class="oe_highlight"
attrs="{'invisible': ['|', ('state', '=', 'draft'), ('answer_done_count', '&lt;=', 0)]}"/>
<button name="action_start_session" string="Create Live Session" type="object"
attrs="{'invisible': ['|', ('session_state', '!=', False), ('state', '=', 'draft')]}" />
attrs="{'invisible': ['|', ('session_state', '!=', False), '|', ('state', '!=', 'open'), ('certification', '=', True)]}" />
<button name="action_open_session_manager" string="Open Session Manager" type="object"
attrs="{'invisible': ['|', ('session_state', '=', False), ('state', '=', 'draft')]}" />
<button name="action_end_session" string="Close Live Session" type="object"
attrs="{'invisible': ['|', ('session_state', '!=', 'in_progress'), ('state', '=', 'draft')]}" />
attrs="{'invisible': ['|', ('session_state', 'not in', ['ready', 'in_progress']), ('state', '=', 'draft')]}" />
<button name="action_draft" string="Set to draft" type="object" states="closed"/>
<button name="action_test_survey" string="Test" type="object" attrs="{'invisible': ['|', ('state', '=', 'closed'), ('id', '=', False)]}"/>
<button name="action_print_survey" string="Print" type="object" attrs="{'invisible': [('id', '=', False)]}"/>
+109 -92
View File
@@ -21,12 +21,25 @@
<div id="wrap" class="oe_structure oe_empty"/>
</xpath>
<xpath expr="//footer" position="after">
<div class="py-3">
<div class="float-right px-5 py-2 bg-white rounded-left">
<t t-call="web.brand_promotion_message">
<div class="py-3 m-0 p-0 text-right">
<div class="o_survey_progress_wrapper d-inline-block pr-5 text-left">
<t t-call="survey.survey_progression"
t-if="survey and survey.questions_layout != 'one_page' and answer and answer.state == 'in_progress' and (not question or not question.is_page)">
<t t-if="survey.questions_layout == 'page_per_section'">
<t t-set="page_ids" t-value="survey.page_ids.ids"/>
<t t-set="page_number" t-value="page_ids.index(page.id) + (1 if survey.progression_mode == 'number' else 0)"/>
</t>
<t t-else="">
<t t-set="page_ids" t-value="survey.question_ids.ids"/>
<t t-set="page_number" t-value="page_ids.index(question.id)"/>
</t>
</t>
</div>
<div class="o_survey_brand_message float-right rounded mr-3">
<div class="px-2 py-2 d-inline-block" t-call="web.brand_promotion_message">
<t t-set="_message"></t>
<t t-set="_utm_medium" t-valuef="survey"/>
</t>
</div>
</div>
</div>
</xpath>
@@ -35,9 +48,9 @@
<!-- Main survey template -->
<template id="survey_page_fill" name="Survey: main page (take survey)">
<t t-call="survey.layout">
<div class="wrap o_survey_wrap mt16 pb16 d-flex">
<div class="container o_survey_form d-flex flex-column">
<t t-if="answer.test_entry" t-call="survey.survey_button_form_view" />
<t t-if="answer.test_entry" t-call="survey.survey_button_form_view" />
<div class="wrap o_survey_wrap d-flex align-items-center">
<div class="container o_survey_form d-flex flex-column mb-5">
<t t-call="survey.survey_fill_header" />
<t t-call="survey.survey_fill_form" />
@@ -47,10 +60,13 @@
</template>
<template id="survey_fill_header" name="Survey: main page header">
<div class="o_survey_nav pt16 pb24">
<div class="o_survey_nav pt16 mb-2">
<div class="container m-0 p-0">
<div class="row">
<div class="col-lg-8"><h1 t-esc="survey.title"></h1></div>
<div class="col-lg-8">
<h1 t-if="answer.state == 'new' or survey.questions_layout != 'page_per_question'"
t-esc="survey.title" class="o_survey_main_title"></h1>
</div>
<div class="o_survey_timer col-lg-4">
<h1 class="o_survey_timer_container timer text-right">
</h1>
@@ -67,7 +83,7 @@
<template id="survey_fill_form" name="Survey: main page content">
<t t-set="survey_form_readonly" t-value="answer.state == 'done'"/>
<form role="form" method="post" t-att-name="survey.id"
class="d-flex flex-grow-1 align-items-center"
class="d-flex flex-grow-1"
t-att-data-answer-token="answer.access_token"
t-att-data-survey-token="survey.access_token"
t-att-data-session-in-progress="answer.is_session_answer"
@@ -107,9 +123,9 @@
</template>
<template id="survey_fill_form_start" name="Survey: start form content">
<div class="wrap o_survey_start pb256">
<div class='mt32 mb32'>
<div t-field='survey.description' class="oe_no_empty"/>
<div class="wrap o_survey_start">
<div class='mb32'>
<div t-field='survey.description' class="oe_no_empty pb-5"/>
<t t-if="answer.is_session_answer">
<div class="font-weight-bold">
The session will begin automatically when the host starts.
@@ -130,7 +146,7 @@
Start Survey
</t>
</button>
<span class="o_survey_enter font-weight-bold ml-2">or press Enter</span>
<span class="o_survey_enter font-weight-bold text-muted ml-2">or press Enter</span>
</t>
</div>
</div>
@@ -152,7 +168,7 @@
<div class="text-center mt16 mb256">
<button type="submit" value="finish" class="btn btn-primary">
Submit</button><span class="font-weight-bold ml-2 d-none d-md-inline">or press Enter</span>
Submit</button><span class="font-weight-bold text-muted ml-2 d-none d-md-inline">or press Enter</span>
</div>
</t>
@@ -166,42 +182,44 @@
</t>
<div class="row">
<div class="offset-3 col-6 text-center mt16 mb128">
<div class="offset-3 col-6 text-center mt16">
<button t-if="survey.users_can_go_back and page != survey.page_ids[0]" type="submit" class="btn btn-secondary"
name="button_submit" value="previous" t-att-data-previous-page-id="previous_page_id">Back</button>
<button type="submit" t-att-value="'next' if not survey_last else 'finish'" class="btn btn-primary">
Continue</button><span class="font-weight-bold ml-2 d-none d-md-inline"> or press Enter</span>
</div>
<div class="col-3">
<div class="float-right">
<t t-call="survey.survey_progression">
<t t-set="page_ids" t-value="survey.page_ids.ids"/>
<t t-set="page_number" t-value="page_ids.index(page.id) + (1 if survey.progression_mode == 'number' else 0)"/>
</t>
</div>
Continue</button><span class="font-weight-bold text-muted ml-2 d-none d-md-inline"> or press Enter</span>
</div>
</div>
</t>
<t t-if="survey.questions_layout == 'page_per_question'">
<!-- If we have a choice question and less than 6 options, we want minimized display.
Minimized display means we display the choices vertically (instead of optimized based on screen space).
An exception is made for options with images, where we always want to optimize screen space.
Numeric, date and datetime questions are also displayed "minimized", with a smaller screen width.-->
<t t-set="minimized_display" t-value="survey.questions_layout == 'page_per_question' and len(question.suggested_answer_ids) &lt;= 5 and not any(suggestion.value_image for suggestion in question.suggested_answer_ids)" />
<div t-if="survey.questions_layout == 'page_per_question'"
t-attf-class="o_survey_page_per_question o_survey_#{question.question_type} #{'o_survey_minimized_display' if minimized_display else ''}">
<input type="hidden" name="question_id" t-att-value="question.id" />
<!-- User has already answered for this session -->
<t t-if="answer.is_session_answer and (has_answered or answer.question_time_limit_reached)">
<fieldset disabled="disabled">
<t t-set="survey_form_readonly" t-value="True" />
<t t-call="survey.question_container" />
<div class="mt-5">
<t t-call="survey.question_container" />
</div>
</fieldset>
<div t-if="answer.question_time_limit_reached and not has_answered" class="font-weight-bold mb128">Sorry, you have not been fast enough.</div>
<div t-else="" class="font-weight-bold mb128">We have registered your answer! Please wait for the host to go to the next question.</div>
<div t-if="answer.question_time_limit_reached and not has_answered" class="font-weight-bold">Sorry, you have not been fast enough.</div>
<div t-else="" class="font-weight-bold">We have registered your answer! Please wait for the host to go to the next question.</div>
</t>
<t t-elif="answer.is_session_answer and question.is_page and not is_html_empty(question.description)">
<div class="font-weight-bold mt-5">Pay attention to the host screen until the next question.</div>
</t>
<t t-else="">
<t t-call="survey.question_container"/>
<div class="mt-5">
<t t-call="survey.question_container"/>
</div>
<div class="row">
<div class="offset-3 col-6 text-center mt16 mb128">
<div class="offset-3 col-6 text-center mt16">
<!-- Can go back if:
- Back button is allowed in configuration
- This is not a session answer (doesn't make sense to go back in session context)
@@ -219,25 +237,17 @@
Continue
</t>
</button>
<span class="font-weight-bold ml-2 d-none d-md-inline">or press Enter</span>
</div>
<div class="col-3">
<div class="float-right">
<t t-if="not question.is_page" t-call="survey.survey_progression">
<t t-set="page_ids" t-value="survey.question_ids.ids"/>
<t t-set="page_number" t-value="page_ids.index(question.id)"/>
</t>
</div>
<span class="font-weight-bold text-muted ml-2 d-none d-md-inline">or press Enter</span>
</div>
</div>
</t>
</t>
</div>
</template>
<!-- Finished (taken and finished) survey page -->
<template id="survey_fill_form_done" name="Survey: finished">
<div class="wrap">
<div class="o_survey_finished mt32 mb32 pb256">
<div class="o_survey_finished mt32 mb32">
<h1>Thank you!</h1>
<div t-field="survey.description_done" class="oe_no_empty" />
<div class="row">
@@ -298,15 +308,19 @@
<!--Use Key selection if number of choices is < 26 to keep Z for other choice if any-->
<t t-set="letters" t-value="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<t t-set="useKeySelection" t-value="len(question.suggested_answer_ids) &lt; len(letters) and survey.questions_layout == 'page_per_question'"/>
<div t-att-class="'js_question-wrapper pb-4 %s' % ('d-none' if not display_question else '')"
<!-- Extra 'right' margin is added on layouts that are not "page_per_question" to align with choices questions, since all choices have a mr-2 class (pixel perfect yay...) -->
<t t-set="extra_right_margin" t-value="survey.questions_layout != 'page_per_question' and question.question_type not in ['simple_choice', 'multiple_choice']"/>
<div t-att-class="'js_question-wrapper pb-4 %s %s' % ('d-none' if not display_question else '', 'mr-2' if extra_right_margin else '')"
t-att-id="question.id" t-att-data-required="question.constr_mandatory"
t-att-data-constr-error-msg="question.constr_error_msg"
t-att-data-validation-error-msg="question.validation_error_msg">
<h4 t-if="not hide_question_title">
<span t-field='question.title' />
<span t-if="question.constr_mandatory" class="text-danger">*</span>
</h4>
<div t-field='question.description' class="text-muted oe_no_empty"/>
<div class="mb-4">
<h3 t-if="not hide_question_title">
<span t-field='question.title' />
<span t-if="question.constr_mandatory" class="text-danger">*</span>
</h3>
<div t-if="not is_html_empty(question.description)" t-field='question.description' class="text-muted oe_no_empty mt-1"/>
</div>
<t t-if="question.question_type == 'text_box'"><t t-call="survey.question_text_box"/></t>
<t t-if="question.question_type == 'char_box'"><t t-call="survey.question_char_box"/></t>
<t t-if="question.question_type == 'numerical_box'"><t t-call="survey.question_numerical_box"/></t>
@@ -320,16 +334,16 @@
</template>
<template id="question_text_box" name="Question: free text box">
<div class="o_survey_comment_container py-0 px-1">
<textarea class="form-control o_survey_question_text_box font-weight-bold bg-transparent text-primary rounded-0" rows="3" t-att-name="question.id"
<div class="o_survey_comment_container p-0">
<textarea class="form-control o_survey_question_text_box bg-transparent text-dark rounded-0 p-0" rows="3" t-att-name="question.id"
t-att-data-question-type="question.question_type"><t t-if="answer_lines" t-esc="answer_lines[0].value_text_box or None"/></textarea>
</div>
</template>
<template id="question_char_box" name="Question: text box">
<div class="o_survey_comment_container py-0 px-1">
<div class="o_survey_comment_container p-0">
<input t-att-type="'email' if question.validation_email else 'text'"
class="form-control o_survey_question_text_box font-weight-bold bg-transparent text-primary rounded-0" t-att-name="question.id"
class="form-control o_survey_question_text_box bg-transparent text-dark rounded-0 p-0" t-att-name="question.id"
t-att-value="answer_lines[0].value_char_box if answer_lines else None"
t-att-data-question-type="question.question_type"
t-att-data-validation-length-min="question.validation_length_min if question.validation_required else False"
@@ -338,7 +352,8 @@
</template>
<template id="question_numerical_box" name="Question: numerical box">
<input type="number" step="any" class="form-control o_survey_question_numerical_box font-weight-bold bg-transparent text-primary rounded-0" t-att-name="question.id" t-att-value="answer_lines[0].value_numerical_box if answer_lines else None"
<input type="number" step="any" class="form-control o_survey_question_numerical_box bg-transparent text-dark rounded-0 p-0"
t-att-name="question.id" t-att-value="answer_lines[0].value_numerical_box if answer_lines else None"
t-att-data-question-type="question.question_type"
t-att-data-validation-float-min="question.validation_min_float_value if question.validation_required else False"
t-att-data-validation-float-max="question.validation_max_float_value if question.validation_required else False"/>
@@ -348,12 +363,12 @@
<div class="input-group o_survey_form_date" t-attf-id="datetimepicker_#{question.id}" data-target-input="nearest"
t-att-data-mindate="question.validation_min_date"
t-att-data-maxdate="question.validation_max_date">
<input type="text" class="form-control datetimepicker-input o_survey_question_date font-weight-bold bg-transparent text-primary rounded-0"
<input type="text" class="form-control datetimepicker-input o_survey_question_date bg-transparent text-dark rounded-0 p-0"
t-attf-data-target="#datetimepicker_#{question.id}" t-att-name="question.id"
t-att-value="format_date(answer_lines[0].value_date) if answer_lines else None"
t-att-data-question-type="question.question_type"/>
<div t-if="not survey_form_readonly" class="input-group-append position-absolute" t-attf-data-target="#datetimepicker_#{question.id}" data-toggle="datetimepicker">
<div class="input-group-text text-primary border-0 bg-transparent"><i class="fa fa-calendar"></i></div>
<div class="input-group-text text-primary border-0 bg-transparent p-0"><i class="fa fa-calendar"></i></div>
</div>
</div>
</template>
@@ -362,12 +377,12 @@
<div class="input-group o_survey_form_date" t-attf-id="datetimepicker_#{question.id}" data-target-input="nearest"
t-att-data-mindate="question.validation_min_datetime"
t-att-data-maxdate="question.validation_max_datetime">
<input type="text" class="form-control datetimepicker-input o_survey_question_datetime font-weight-bold bg-transparent text-primary rounded-0"
<input type="text" class="form-control datetimepicker-input o_survey_question_datetime bg-transparent text-dark rounded-0 p-0"
t-attf-data-target="#datetimepicker_#{question.id}" t-att-name="question.id"
t-att-value="format_datetime(answer_lines[0].value_datetime) if answer_lines else None"
t-att-data-question-type="question.question_type"/>
<div t-if="not survey_form_readonly" class="input-group-append position-absolute" t-attf-data-target="#datetimepicker_#{question.id}" data-toggle="datetimepicker">
<div class="input-group-text text-primary border-0 bg-transparent"><i class="fa fa-calendar"></i></div>
<div class="input-group-text text-primary border-0 bg-transparent p-0"><i class="fa fa-calendar"></i></div>
</div>
</div>
</template>
@@ -392,7 +407,7 @@
t-att-data-name="question.id"
data-question-type="simple_choice_radio">
<t t-set="item_idx" t-value="0"/>
<div class="d-flex flex-wrap font-weight-bold col-lg-12">
<div t-attf-class="col-lg-12 d-flex flex-wrap">
<t t-set="has_correct_answer" t-value="scoring_display_correction and any(label.is_correct for label in question.suggested_answer_ids)"/>
<t t-foreach='question.suggested_answer_ids' t-as='label'>
<t t-set="item_idx" t-value="label_index"/>
@@ -405,9 +420,9 @@
<t t-set="answer_class" t-elif="not is_correct" t-value="'bg-danger'" />
<label t-att-for="str(question.id) + '_' + str(label.id)"
t-att-class="'o_survey_choice_btn m-1 py-1 px-3 text-white rounded %s %s' % (answer_class, 'o_survey_selected' if answer_selected else '')">
t-att-class="'o_survey_choice_btn mr-2 mb-2 py-1 px-3 rounded %s %s' % (answer_class, 'o_survey_selected' if answer_selected else '')">
<t t-call="survey.survey_selection_key">
<t t-set="selection_key_class" t-value="'o_survey_radio_btn float-left d-flex'"/>
<t t-set="selection_key_class" t-value="'position-relative o_survey_radio_btn float-left d-flex'"/>
</t>
<span class="ml-2" t-field='label.value'/>
<input t-att-id="str(question.id) + '_' + str(label.id)" type="radio" t-att-value='label.id' class="o_survey_form_choice_item invisible position-absolute"
@@ -415,17 +430,17 @@
t-att-checked="answer_line and answer_line.suggested_answer_id.id == label.id and 'checked' or None"
t-att-data-selection-key="letters[item_idx] if useKeySelection else ''"/>
<i class="fa fa-check-circle float-right mt-1 position-relative"></i>
<i class="fa fa-circle float-right mt-1 position-relative"></i>
<i class="fa fa-circle-thin float-right mt-1 position-relative"></i>
<t t-call="survey.question_suggested_value_image"/>
</label>
</t>
</div>
<div t-if='question.comments_allowed and question.comment_count_as_answer' class="js_comments col-lg-12" >
<div class="d-flex flex-wrap font-weight-bold">
<label t-att-class="'o_survey_choice_btn m-1 py-1 px-3 text-white rounded %s' % ('o_survey_selected' if comment_line else '')">
<div class="d-flex flex-wrap">
<label t-att-class="'o_survey_choice_btn mr-2 py-1 px-3 rounded %s' % ('o_survey_selected' if comment_line else '')">
<t t-set="item_idx" t-value="item_idx + 1"/>
<t t-call="survey.survey_selection_key">
<t t-set="selection_key_class" t-value="'o_survey_radio_btn float-left d-flex'"/>
<t t-set="selection_key_class" t-value="'position-relative o_survey_radio_btn float-left d-flex'"/>
</t>
<input type="radio" class="o_survey_form_choice_item o_survey_js_form_other_comment invisible position-absolute" value="-1"
t-att-name='question.id'
@@ -433,17 +448,17 @@
t-att-data-selection-key="letters[item_idx] if useKeySelection else ''"/>
<span class="ml-2" t-field="question.comments_message" />
<i class="fa fa-check-circle float-right mt-1 position-relative"></i>
<i class="fa fa-circle float-right mt-1 position-relative"></i>
<i class="fa fa-circle-thin float-right mt-1 position-relative"></i>
</label>
</div>
<div class="o_survey_comment_container py-0 px-1">
<textarea type="text" class="form-control o_survey_question_text_box font-weight-bold bg-transparent text-primary rounded-0"
<div t-attf-class="o_survey_comment_container mt-3 py-0 px-1 #{'d-none' if not comment_line else ''}">
<textarea type="text" class="form-control o_survey_question_text_box bg-transparent text-dark rounded-0 p-0"
t-att-disabled="None if comment_line else 'disabled'"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea>
</div>
</div>
<div t-if='question.comments_allowed and not question.comment_count_as_answer' class="col-lg-12 o_survey_comment_container mx-1 py-0 pl-3 pr-4">
<textarea type="text" class="form-control o_survey_comment o_survey_question_text_box font-weight-bold bg-transparent text-primary rounded-0"
t-att-placeholder="question.comments_message"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea>
<div t-if='question.comments_allowed and not question.comment_count_as_answer' class="col-lg-12 o_survey_comment_container mx-1 mt-3 pl-3 pr-4">
<textarea type="text" class="form-control o_survey_comment o_survey_question_text_box bg-transparent text-dark rounded-0 p-0"
t-att-placeholder="question.comments_message if not survey_form_readonly else ''"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea>
</div>
</div>
</template>
@@ -454,7 +469,7 @@
t-att-data-name="question.id"
t-att-data-question-type="question.question_type">
<t t-set="item_idx" t-value="0"/>
<div class="d-flex flex-wrap font-weight-bold col-lg-12">
<div class="d-flex flex-wrap col-lg-12">
<t t-set="has_correct_answer" t-value="scoring_display_correction and any(label.is_correct for label in question.suggested_answer_ids)"/>
<t t-foreach='question.suggested_answer_ids' t-as='label'>
<t t-set="item_idx" t-value="label_index"/>
@@ -467,9 +482,9 @@
<t t-set="answer_class" t-elif="is_correct" t-value="'bg-success'" />
<t t-set="answer_class" t-elif="not is_correct" t-value="'bg-danger'" />
<label t-att-class="'o_survey_choice_btn m-1 py-1 px-3 text-white rounded %s %s' % (answer_class, 'o_survey_selected' if answer_line else '')">
<label t-att-class="'o_survey_choice_btn mr-2 py-1 px-3 rounded %s %s' % (answer_class, 'o_survey_selected' if answer_line else '')">
<t t-call="survey.survey_selection_key">
<t t-set="selection_key_class" t-value="'float-left d-flex'"/>
<t t-set="selection_key_class" t-value="'position-relative float-left d-flex'"/>
</t>
<input type="checkbox" t-att-value='label.id' class="o_survey_form_choice_item invisible position-absolute"
t-att-name="question.id"
@@ -477,17 +492,17 @@
t-att-data-selection-key="letters[item_idx] if useKeySelection else ''"/>
<span class="ml-2" t-field='label.value'/>
<i class="fa fa-check-square float-right mt-1 position-relative"></i>
<i class="fa fa-square float-right mt-1 position-relative"></i>
<i class="fa fa-square-o float-right mt-1 position-relative"></i>
<t t-call="survey.question_suggested_value_image"/>
</label>
</t>
</div>
<div t-if='question.comments_allowed and question.comment_count_as_answer' class="js_ck_comments col-lg-12" >
<div class="d-flex flex-wrap font-weight-bold">
<label t-att-class="'o_survey_choice_btn m-1 py-1 px-3 text-white rounded %s' % ('o_survey_selected' if comment_line else '')">
<div class="d-flex flex-wrap">
<label t-att-class="'o_survey_choice_btn mr-2 py-1 px-3 rounded %s' % ('o_survey_selected' if comment_line else '')">
<t t-set="item_idx" t-value="item_idx + 1"/>
<t t-call="survey.survey_selection_key">
<t t-set="selection_key_class" t-value="'float-left d-flex'"/>
<t t-set="selection_key_class" t-value="'position-relative float-left d-flex'"/>
</t>
<input type="checkbox" class="o_survey_form_choice_item o_survey_js_form_other_comment invisible position-absolute" value="-1"
t-att-name="question.id"
@@ -495,17 +510,17 @@
t-att-data-selection-key="letters[item_idx] if useKeySelection else ''"/>
<span class="ml-2" t-field="question.comments_message" />
<i class="fa fa-check-square float-right mt-1 position-relative"></i>
<i class="fa fa-square float-right mt-1 position-relative"></i>
<i class="fa fa-square-o float-right mt-1 position-relative"></i>
</label>
</div>
<div class="o_survey_comment_container py-0 px-1">
<textarea type="text" class="form-control o_survey_question_text_box font-weight-bold bg-transparent text-primary rounded-0"
<div t-attf-class="o_survey_comment_container mt-3 py-0 px-1 #{'d-none' if not comment_line else ''}">
<textarea type="text" class="form-control o_survey_question_text_box bg-transparent text-dark rounded-0 p-0"
t-att-disabled="None if comment_line else 'disabled'"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea>
</div>
</div>
<div t-if='question.comments_allowed and not question.comment_count_as_answer' class="col-lg-12 o_survey_comment_container mx-1 py-0 pl-3 pr-4">
<textarea type="text" class="form-control o_survey_comment o_survey_question_text_box font-weight-bold bg-transparent text-primary rounded-0"
t-att-placeholder="question.comments_message"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea>
<div t-if='question.comments_allowed and not question.comment_count_as_answer' class="col-lg-12 o_survey_comment_container mx-1 mt-3 py-0 pl-3 pr-4">
<textarea type="text" class="form-control o_survey_comment o_survey_question_text_box bg-transparent text-dark rounded-0 p-0"
t-att-placeholder="question.comments_message if not survey_form_readonly else ''"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea>
</div>
</div>
</template>
@@ -519,16 +534,18 @@
<thead>
<tr>
<th class="border-0"> </th>
<th class="border-0" t-foreach="question.suggested_answer_ids" t-as="col_label"><span t-field="col_label.value" /></th>
<th class="border-0 font-weight-normal" t-foreach="question.suggested_answer_ids" t-as="col_label"><span t-field="col_label.value" /></th>
</tr>
</thead>
<tbody>
<t t-set="item_idx" t-value="0"/>
<!-- For matrix, we have an extra check because we have rows * columns total options -->
<t t-set="useKeySelection" t-value="useKeySelection and (len(question.suggested_answer_ids) * len(question.matrix_row_ids)) &lt; len(letters)" />
<tr class="bg-white text-white" t-foreach="question.matrix_row_ids" t-as="row_label">
<th class="border-0"><span t-field="row_label.value" /></th>
<th class="border-0 font-weight-normal text-left"><span t-field="row_label.value" /></th>
<t t-foreach="question.suggested_answer_ids" t-as="col_label">
<t t-set="answer" t-value="answer_lines.filtered(lambda line: line.suggested_answer_id == col_label and line.matrix_row_id == row_label)"/>
<td t-att-class="'o_survey_matrix_btn text-primary border-0 %s'
<td t-att-class="'o_survey_matrix_btn text-primary border-0 position-relative %s'
% ('o_survey_selected' if answer else '')">
<input t-att-type="'checkbox' if question.matrix_subtype == 'multiple' else 'radio'"
t-att-name="'%s_%s' % (question.id, row_label.id)" t-att-value='col_label.id'
@@ -538,11 +555,11 @@
class="o_survey_form_choice_item d-none"/>
<i t-att-class="'o_survey_matrix_empty_checkbox fa fa-%s position-relative'
% ('square-o' if question.matrix_subtype == 'multiple' else 'circle-thin')"></i>
<i t-att-class="'fa fa-check-%s position-relative'
% ('square' if question.matrix_subtype == 'multiple' else 'circle')"></i>
<i t-att-class="'fa fa-%s position-relative'
% ('check-square' if question.matrix_subtype == 'multiple' else 'check-circle')"></i>
<t t-call="survey.survey_selection_key">
<t t-set="selection_key_class"
t-value="'float-right font-weight-bold %s' % ('o_survey_radio_btn' if question.matrix_subtype != 'multiple' else '')"/>
t-value="'position-absolute float-right font-weight-bold %s' % ('o_survey_radio_btn' if question.matrix_subtype != 'multiple' else '')"/>
</t>
<t t-set="item_idx" t-value="item_idx + 1"/>
</td>
@@ -551,14 +568,14 @@
</tbody>
</table>
<div t-if='question.comments_allowed'>
<textarea type="text" class="form-control o_survey_question_text_box o_survey_comment font-weight-bold bg-transparent text-primary rounded-0"
t-att-placeholder="question.comments_message"
<textarea type="text" class="form-control o_survey_question_text_box o_survey_comment bg-transparent text-dark rounded-0 p-0 mt-3"
t-att-placeholder="question.comments_message if not survey_form_readonly else ''"
t-att-name="'%s_%s' % (question.id, 'comment')"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea>
</div>
</template>
<template id="survey_selection_key">
<div t-if="useKeySelection" t-att-class="'o_survey_choice_key bg-white position-relative rounded %s' % selection_key_class">
<div t-if="useKeySelection" t-att-class="'o_survey_choice_key bg-white rounded %s' % selection_key_class">
<span class="o_survey_key text-center position-absolute bg-white rounded-left py-0 pr-4 pl-2"><span class="text-primary text-center text-center w-100 position-relative">Key</span></span>
<span class="text-primary text-center w-100 position-relative" t-esc="letters[item_idx]"/>
</div>
@@ -23,9 +23,9 @@
<!-- Error: void survey -->
<template id="survey_void_content" name="Survey: void content">
<t t-call="survey.layout">
<t t-if="answer.test_entry" t-call="survey.survey_button_form_view" />
<div class="wrap">
<div class="container">
<t t-if="answer.test_entry" t-call="survey.survey_button_form_view" />
<div class="jumbotron mt32">
<h1><span t-field="survey.title"/> survey is void</h1>
<p>Please make sure you have at least one question in your survey. You also need at least one section if you chose the "Page per section" layout.<br />
@@ -74,7 +74,7 @@
<!-- ============================================================ -->
<template id="survey_button_form_view" name="Survey: back to form view">
<div groups="survey.group_survey_manager" t-ignore="true" class="alert alert-info alert-dismissible rounded-0 fade show d-print-none css_editable_mode_hidden">
<div groups="survey.group_survey_manager" t-ignore="true" class="alert alert-info alert-dismissible rounded-0 fade show d-print-none css_editable_mode_hidden mb-0">
<div t-ignore="true" class="text-center">
<a t-attf-href="/web#view_type=form&amp;model=survey.survey&amp;id=#{survey.id}&amp;action=survey.action_survey_form"><i class="fa fa-fw fa-arrow-right"/><span t-if="answer and answer.test_entry">This is a test survey. </span>Edit Survey.</a>
</div>
@@ -5,10 +5,10 @@
<template id="survey_page_print" name="Survey: print page">
<t t-call="survey.layout">
<t t-set="survey_form_readonly" t-value="true"/>
<t t-if="answer.test_entry" t-call="survey.survey_button_form_view" />
<div class="wrap">
<div class="o_survey_print container">
<t t-if="answer.test_entry" t-call="survey.survey_button_form_view" />
<div class='jumbotron mt32'>
<div class='py-5 mt32'>
<h1><span t-field='survey.title'/></h1>
<t t-if="survey.description"><div t-field='survey.description' class="oe_no_empty"/></t>
<t t-if="review" t-call="survey.survey_button_retake"/>
@@ -3,9 +3,9 @@
<data>
<template id="survey_page_statistics" name="Survey: result statistics page">
<t t-call="survey.layout">
<t t-call="survey.survey_button_form_view" />
<t t-set="page_record_limit" t-value="10"/><!-- Change this record_limit to change number of record per page-->
<div class="container o_survey_result">
<t t-call="survey.survey_button_form_view" />
<t t-call="survey.survey_page_statistics_header" />
<t t-call="survey.survey_page_statistics_inner" />
</div>
@@ -13,7 +13,7 @@
</template>
<template id="survey_page_statistics_header" name="Survey: result statistics header">
<div class="jumbotron mt32">
<div class="py-5 mt32">
<h1>
<span t-field="survey.title"/>
<span style="font-size:1.5em;"