[IMP] survey: improve mobile UI
PURPOSE This commit removes a few unessential content, fixes misalignments and removes the input autofocus on mobile devices SPECS Remove a few unessential content Some contents take a considerable space on mobile without adding essential information, requiring the user to scroll to see useful info. These contents are now removed on mobile devices. Fix misalignments Some parts were misaligned on small screens, taking unnecessary space or cutting parts of sentences. These parts have been adapted to handle small screens. Remove action buttons related to live sessions on small screens Since the presentation mode doesn't look good on mobile devices (questions have a lot of linebreaks, arrows do not always work), and since normally no one presents a live session using a mobile device, the action buttons "Create Live Session", "Open Session Manager" and "Close Live Session" are now hidden on small screens. Remove the autofocus on the first input in survey forms on mobile devices When a new page of a survey is loaded, the first input is autofocused so that the user can directly complete the survey without manually getting the focus. This causes inconsistencies and undesired behaviors on mobile devices. Especially, when the focus is set automatically on Android devices, the soft keyboard is opened, the viewport is reduced and the view is scrolled to the input, which involves that the user misses information about the survey (such as the beginning of the question for long questions), or section descriptions. The autofocus has therefore been removed for mobile devices. Task-2622693 Part-of: odoo/odoo#87990
This commit is contained in:
@@ -908,6 +908,7 @@ class Survey(models.Model):
|
||||
)
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _("Share a Survey"),
|
||||
'view_mode': 'form',
|
||||
'res_model': 'survey.invite',
|
||||
'target': 'new',
|
||||
|
||||
@@ -4,6 +4,7 @@ odoo.define('survey.form', function (require) {
|
||||
var field_utils = require('web.field_utils');
|
||||
var publicWidget = require('web.public.widget');
|
||||
var time = require('web.time');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var Dialog = require('web.Dialog');
|
||||
var dom = require('web.dom');
|
||||
@@ -322,7 +323,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa
|
||||
*/
|
||||
_updateEnterButtonText: function (event) {
|
||||
const $target = event.target;
|
||||
const isTextbox = event.type == "focusin" && $target.tagName.toLowerCase() === 'textarea';
|
||||
const isTextbox = event.type === "focusin" && $target.tagName.toLowerCase() === 'textarea';
|
||||
const text = !isTextbox ? _t('or press Enter') : isMac ? _t("or press ⌘+Enter") : _t("or press CTRL+Enter");
|
||||
$('#enter-tooltip').text(text);
|
||||
},
|
||||
@@ -575,8 +576,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa
|
||||
this.$('.o_survey_form_content').fadeIn(this.fadeInOutDelay);
|
||||
$("html, body").animate({ scrollTop: 0 }, this.fadeInOutDelay);
|
||||
self._focusOnFirstInput();
|
||||
}
|
||||
else if (result && result.fields && result.error === 'validation') {
|
||||
} else if (result && result.fields && result.error === 'validation') {
|
||||
this.$('.o_survey_form_content').fadeIn(0);
|
||||
this._showErrors(result.fields);
|
||||
} else {
|
||||
@@ -936,7 +936,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa
|
||||
|
||||
if (!this._checkIsMasterTab()) {
|
||||
this.shouldReloadMasterTab = true;
|
||||
this.masterTabCheckInterval = setInterval(function() {
|
||||
this.masterTabCheckInterval = setInterval(function () {
|
||||
if (self._checkIsMasterTab()) {
|
||||
clearInterval(self.masterTabCheckInterval);
|
||||
}
|
||||
@@ -1007,7 +1007,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa
|
||||
}
|
||||
|
||||
$dateGroup.datetimepicker({
|
||||
format : datetimepickerFormat,
|
||||
format: datetimepickerFormat,
|
||||
minDate: minDate,
|
||||
maxDate: maxDate,
|
||||
disabledDates: disabledDates,
|
||||
@@ -1022,7 +1022,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa
|
||||
up: 'fa fa-chevron-up',
|
||||
down: 'fa fa-chevron-down',
|
||||
},
|
||||
locale : moment.locale(),
|
||||
locale: moment.locale(),
|
||||
allowInputToggle: true,
|
||||
});
|
||||
$dateGroup.on('error.datetimepicker', function (err) {
|
||||
@@ -1039,7 +1039,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa
|
||||
});
|
||||
},
|
||||
|
||||
_formatDateTime: function (datetimeValue, format){
|
||||
_formatDateTime: function (datetimeValue, format) {
|
||||
return moment(field_utils.format.datetime(moment(datetimeValue), null, {timezone: true}), format);
|
||||
},
|
||||
|
||||
@@ -1057,14 +1057,15 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa
|
||||
|
||||
/**
|
||||
* Will automatically focus on the first input to allow the user to complete directly the survey,
|
||||
* without having to manually get the focus (only if the input has the right type - can write something inside -)
|
||||
* without having to manually get the focus (only if the input has the right type - can write something inside -
|
||||
* and if the device is not a mobile device to avoid missing information when the soft keyboard is opened)
|
||||
*/
|
||||
_focusOnFirstInput: function () {
|
||||
var $firstTextInput = this.$('.js_question-wrapper').first() // Take first question
|
||||
.find("input[type='text'],input[type='number'],textarea") // get 'text' inputs
|
||||
.filter('.form-control') // needed for the auto-resize
|
||||
.not('.o_survey_comment'); // remove inputs for comments that does not count as answers
|
||||
if ($firstTextInput.length > 0) {
|
||||
if ($firstTextInput.length > 0 && !config.device.isMobile) {
|
||||
$firstTextInput.focus();
|
||||
}
|
||||
},
|
||||
@@ -1085,7 +1086,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa
|
||||
window.location.reload();
|
||||
}
|
||||
return true;
|
||||
} else if (!$errorModal.modal._isShown){
|
||||
} else if (!$errorModal.modal._isShown) {
|
||||
$errorModal.find('.text-danger').text(window.location.hostname);
|
||||
$errorModal.modal('show');
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ _::-webkit-full-page-media, _:future, :root .o_survey_wrap {
|
||||
}
|
||||
|
||||
.o_survey_progress_wrapper {
|
||||
min-width: 11rem;
|
||||
max-width: 15rem;
|
||||
min-width: 7rem;
|
||||
max-width: 11rem;
|
||||
|
||||
.o_survey_progress {
|
||||
height:0.5em;
|
||||
@@ -294,11 +294,11 @@ _::-webkit-full-page-media, _:future, :root .o_survey_wrap {
|
||||
cursor: pointer;
|
||||
|
||||
&.o_survey_session_navigation_next {
|
||||
right: 2rem;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
&.o_survey_session_navigation_previous {
|
||||
left: 2rem;
|
||||
left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
.btn.filter-remove-answer {
|
||||
border-color: #DEE2E6;
|
||||
background-color: transparent;
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
i.fa-times {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<field name="question_type" widget="radio" attrs="{'required': [('is_page', '=', False)]}" />
|
||||
</group>
|
||||
<group>
|
||||
<div class="col-lg-6 offset-lg-3 w-100 o_preview_questions">
|
||||
<div class="col-lg-6 offset-lg-3 d-none d-sm-block w-100 o_preview_questions">
|
||||
<!-- Multiple choice: only one answer -->
|
||||
<div attrs="{'invisible': [('question_type', '!=', 'simple_choice')]}" role="img" aria-label="Multiple choice with one answer"
|
||||
title="Multiple choice with one answer">
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
<button name="action_send_survey" string="Share" type="object" class="oe_highlight" attrs="{'invisible': [('active', '=', False)]}"/>
|
||||
<button name="action_result_survey" string="See results" type="object" class="oe_highlight"
|
||||
attrs="{'invisible': [('answer_done_count', '<=', 0)]}"/>
|
||||
<button name="action_start_session" string="Create Live Session" type="object"
|
||||
<button name="action_start_session" string="Create Live Session" type="object" class="d-none d-sm-block"
|
||||
attrs="{'invisible': ['|', ('session_state', '!=', False), '|', ('active', '=', False), ('certification', '=', True)]}" />
|
||||
<button name="action_open_session_manager" string="Open Session Manager" type="object"
|
||||
<button name="action_open_session_manager" string="Open Session Manager" type="object" class="d-none d-sm-block"
|
||||
attrs="{'invisible': [('session_state', '=', False)]}" />
|
||||
<button name="action_end_session" string="Close Live Session" type="object"
|
||||
<button name="action_end_session" string="Close Live Session" type="object" class="d-none d-sm-block"
|
||||
attrs="{'invisible': [('session_state', 'not in', ['ready', 'in_progress'])]}" />
|
||||
<button name="action_test_survey" string="Test" type="object" attrs="{'invisible': ['|', ('active', '=', False), ('question_ids', '=', [])]}"/>
|
||||
<button name="action_unarchive" string="Reopen" class="btn-primary" type="object" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
@@ -228,7 +228,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div t-attf-class="col-lg-1 col-sm-4 py-0 my-2 col-#{selection_mode ? '12' : '6'}">
|
||||
<div t-attf-class="col-lg-1 col-sm-4 d-none d-sm-block py-0 my-2 col-#{selection_mode ? '12' : '6'}">
|
||||
<span class="font-weight-bold"><field name="question_count"/></span><br t-if="!selection_mode"/>
|
||||
<span class="text-muted">Questions</span>
|
||||
</div>
|
||||
@@ -249,7 +249,7 @@
|
||||
<span class="text-muted">Registered</span>
|
||||
</a>
|
||||
</div>
|
||||
<div t-if="!selection_mode" class="col-lg-1 col-sm-4 col-6 py-0 my-2">
|
||||
<div t-if="!selection_mode" class="col-lg-1 col-sm-4 col-6 d-none d-sm-block py-0 my-2">
|
||||
<a type="object"
|
||||
name="action_survey_user_input_completed"
|
||||
class="font-weight-bold">
|
||||
@@ -271,7 +271,7 @@
|
||||
<span class="text-muted" t-else="">Certified</span>
|
||||
</a>
|
||||
</div>
|
||||
<div t-if="!selection_mode" class="col-lg-3 col-sm-12 py-0 my-2 pb-lg-3 ml-auto text-lg-right">
|
||||
<div t-if="!selection_mode" class="col-lg-3 col-sm-12 d-none d-sm-block py-0 my-2 pb-lg-3 ml-auto text-lg-right">
|
||||
<button name="action_send_survey"
|
||||
string="Share" type="object"
|
||||
class="btn btn-secondary border"
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</xpath>
|
||||
<xpath expr="//footer" position="after">
|
||||
<div class="py-3 m-0 p-0 text-right">
|
||||
<div class="o_survey_progress_wrapper d-inline-block pr-5 text-left">
|
||||
<div class="o_survey_progress_wrapper d-inline-block pr-1 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) and not survey_form_readonly">
|
||||
<t t-if="survey.questions_layout == 'page_per_section'">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</template>
|
||||
|
||||
<template id="survey_page_statistics_header" name="Survey: result statistics header">
|
||||
<div class="o_survey_statistics_header py-5 mt32">
|
||||
<div class="o_survey_statistics_header pt-5 mt32">
|
||||
<h1>
|
||||
<span t-field="survey.title"/>
|
||||
<span style="font-size:1.5em;"
|
||||
@@ -64,31 +64,33 @@
|
||||
<t t-set="table_data" t-value="question_data['table_data']"/>
|
||||
|
||||
<div class="o_survey_results_question pb-5 border-bottom">
|
||||
<div class="d-flex align-items-start mb-2">
|
||||
<div class="mr-auto">
|
||||
<h5 t-field="question.title" class="mb-3"/>
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-6">
|
||||
<h5 t-field="question.title" class="mr-3"/>
|
||||
</div>
|
||||
<!-- Question info -->
|
||||
<span class="badge badge-info" t-field='question.question_type'/>
|
||||
<t t-if="question.question_type == 'matrix'">
|
||||
<span class="badge badge-info ml-2" t-field='question.matrix_subtype'/>
|
||||
</t>
|
||||
<!-- Scoring info -->
|
||||
<t t-if="question.is_scored_question">
|
||||
<span class="badge badge-success ml-3"><span t-esc="question_data['right_inputs_count']" class="mr-1"/>Correct</span>
|
||||
<t t-if="question.question_type in ['simple_choice', 'multiple_choice']">
|
||||
<span class="badge badge-warning ml-1" t-if="question.question_type == 'multiple_choice'">
|
||||
<span t-esc="question_data['partial_inputs_count']" class="mr-1"/>Partial
|
||||
</span>
|
||||
<div class="col-sm-6">
|
||||
<span class="badge badge-info mr-1" t-field='question.question_type'/>
|
||||
<t t-if="question.question_type == 'matrix'">
|
||||
<span class="badge badge-info ml-1" t-field='question.matrix_subtype'/>
|
||||
</t>
|
||||
</t>
|
||||
<!-- Inputs info -->
|
||||
<span class="badge badge-info ml-3">
|
||||
<span t-esc="len(question_data['answer_input_done_ids'])" class="mr-1"/>Answered
|
||||
</span>
|
||||
<span class="badge badge-info ml-1">
|
||||
<span t-esc="len(question_data['answer_input_skipped_ids'])" class="mr-1"/>Skipped
|
||||
</span>
|
||||
<!-- Scoring info -->
|
||||
<t t-if="question.is_scored_question">
|
||||
<span class="badge badge-success mr-1"><span t-esc="question_data['right_inputs_count']" class="mr-1"/>Correct</span>
|
||||
<t t-if="question.question_type in ['simple_choice', 'multiple_choice']">
|
||||
<span class="badge badge-warning mr-1" t-if="question.question_type == 'multiple_choice'">
|
||||
<span t-esc="question_data['partial_inputs_count']" class="mr-1"/>Partial
|
||||
</span>
|
||||
</t>
|
||||
</t>
|
||||
<!-- Inputs info -->
|
||||
<span class="badge badge-info mr-1">
|
||||
<span t-esc="len(question_data['answer_input_done_ids'])" class="mr-1"/>Answered
|
||||
</span>
|
||||
<span class="badge badge-info mr-1">
|
||||
<span t-esc="len(question_data['answer_input_skipped_ids'])" class="mr-1"/>Skipped
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Question Description -->
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="text-center">
|
||||
<h1 class="mb-4" t-field="survey.title" />
|
||||
<h2 class="mb-5 font-weight-normal">
|
||||
<span>Go to <a t-att-href="survey.session_link" t-esc="survey.session_link" target="_blank" /></span>
|
||||
<span class="text-break">Go to <a t-att-href="survey.session_link" t-esc="survey.session_link" target="_blank" /></span>
|
||||
<i class="fa fa-link font-weight-normal ml-3 o_survey_session_copy" />
|
||||
<input class="o_survey_session_copy_url d-none" type="text" t-att-value="survey.session_link" />
|
||||
</h2>
|
||||
@@ -114,7 +114,7 @@
|
||||
class="font-weight-bold fa fa-chevron-right o_survey_session_navigation o_survey_session_navigation_next p-3" />
|
||||
<a role="button"
|
||||
class="font-weight-bold fa fa-chevron-left o_survey_session_navigation o_survey_session_navigation_previous p-3" />
|
||||
<div class="o_survey_session_results flex-column flex-grow-1">
|
||||
<div class="o_survey_session_results flex-column flex-grow-1 mx-5">
|
||||
<div class="row">
|
||||
<div class="col-lg-12"><h1 t-esc="question.title"></h1></div>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
attrs="{
|
||||
'invisible': [('survey_users_login_required', '=', True)],
|
||||
}"
|
||||
placeholder="Add a list of email of recipients (will not be converted into contacts). Separated by commas, semicolons or newline..."/>
|
||||
placeholder="Add a list of email recipients (separated by commas, semicolons or line breaks). They will not be converted into contacts."/>
|
||||
</group>
|
||||
<div col="2" class="alert alert-warning" role="alert"
|
||||
attrs="{'invisible': ['|', ('survey_access_mode', '=', 'public'), ('existing_text', '=', False)]}">
|
||||
|
||||
Reference in New Issue
Block a user