[MERGE][IMP] survey: redesign frontend views
PURPOSE
=========
In 3 words, make survey beautiful.
SPECIFICATIONS
==============
- Center the question in Question per page mode
- When last question or page of the survey, Continue becomes Finish
- Add Press key capabilities to speed up the survey completion
- ENTER : go to next question (or next page)
- A to Z : to select answers in choice and matrix question type
(only in question per page mode to avoid mismatch and complexity with multiple questions
- New layout for single / multiple choice
- Custom radio buttons and checkboxes. Unified style for both except we keep circle icon for
simple choice and square icon for multiple choice.
- Add a progress bar (2 modes : % and number)
- Add Background image
- Re-do quiz correction mode that were removed from controller during survey refactoring.
- Review breadcrumb style (make it more integrated to the new design)
See sub commits for more details.
LINKS
=====
Follow the survey refactoring done in #42908.
Task ID: '2150291'
closes odoo/odoo#43237
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -24,21 +24,6 @@ $o-portal-bg-color: desaturate($gray-200, 100%);
|
||||
$o-portal-use-default-colors: $body-bg == $o-portal-default-body-bg;
|
||||
|
||||
// Frontend general
|
||||
#wrapwrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
> main {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
.navbar-brand {
|
||||
flex: 0 0 auto;
|
||||
|
||||
@@ -27,7 +27,7 @@ class CustomerPortal(CustomerPortal):
|
||||
#
|
||||
def resize_to_48(b64source):
|
||||
if not b64source:
|
||||
b64source = base64.b64encode(Binary().placeholder())
|
||||
b64source = base64.b64encode(Binary.placeholder())
|
||||
return image_process(b64source, size=(48, 48))
|
||||
|
||||
values = {
|
||||
|
||||
@@ -15,6 +15,8 @@ from odoo.http import request, content_disposition
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import format_datetime, format_date
|
||||
|
||||
from odoo.addons.web.controllers.main import Binary
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -283,6 +285,24 @@ class Survey(http.Controller):
|
||||
return request.render('survey.survey_page_fill',
|
||||
self._prepare_survey_data(access_data['survey_sudo'], access_data['answer_sudo'], **post))
|
||||
|
||||
@http.route('/survey/get_background_image/<string:survey_token>/<string:answer_token>', type='http', auth="public", website=True, sitemap=False)
|
||||
def survey_get_background(self, survey_token, answer_token):
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return werkzeug.exceptions.Forbidden()
|
||||
|
||||
survey_sudo, answer_sudo = access_data['survey_sudo'], access_data['answer_sudo']
|
||||
|
||||
status, headers, image_base64 = request.env['ir.http'].sudo().binary_content(
|
||||
model='survey.survey', id=survey_sudo.id, field='background_image',
|
||||
default_mimetype='image/png')
|
||||
|
||||
return Binary._content_image_get_response(status, headers, image_base64)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# JSON ROUTES to begin / continue survey (ajax navigation) + Tools
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@http.route('/survey/begin/<string:survey_token>/<string:answer_token>', type='json', auth='public', website=True)
|
||||
def survey_begin(self, survey_token, answer_token, **post):
|
||||
""" Route used to start the survey user input and display the first survey page. """
|
||||
@@ -427,6 +447,7 @@ class Survey(http.Controller):
|
||||
'review': review,
|
||||
'survey': survey_sudo,
|
||||
'answer': answer_sudo,
|
||||
'scoring_display_correction': survey_sudo.scoring_type == 'scoring_with_answers' and answer_sudo,
|
||||
'format_datetime': lambda dt: format_datetime(request.env, dt, dt_format=False),
|
||||
'format_date': lambda date: format_date(request.env, date)
|
||||
})
|
||||
@@ -464,23 +485,8 @@ class Survey(http.Controller):
|
||||
|
||||
return self._generate_report(succeeded_attempt, download=True)
|
||||
|
||||
def _generate_report(self, user_input, download=True):
|
||||
report = request.env.ref('survey.certification_report').sudo().render_qweb_pdf([user_input.id], data={'report_type': 'pdf'})[0]
|
||||
|
||||
report_content_disposition = content_disposition('Certification.pdf')
|
||||
if not download:
|
||||
content_split = report_content_disposition.split(';')
|
||||
content_split[0] = 'inline'
|
||||
report_content_disposition = ';'.join(content_split)
|
||||
|
||||
return request.make_response(report, headers=[
|
||||
('Content-Type', 'application/pdf'),
|
||||
('Content-Length', len(report)),
|
||||
('Content-Disposition', report_content_disposition),
|
||||
])
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# REPORTING SURVEY ROUTES
|
||||
# REPORTING SURVEY ROUTES AND TOOLS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@http.route('/survey/results/<model("survey.survey"):survey>', type='http', auth='user', website=True)
|
||||
@@ -509,6 +515,21 @@ class Survey(http.Controller):
|
||||
'search_finished': post.get('finished') == 'true',
|
||||
})
|
||||
|
||||
def _generate_report(self, user_input, download=True):
|
||||
report = request.env.ref('survey.certification_report').sudo().render_qweb_pdf([user_input.id], data={'report_type': 'pdf'})[0]
|
||||
|
||||
report_content_disposition = content_disposition('Certification.pdf')
|
||||
if not download:
|
||||
content_split = report_content_disposition.split(';')
|
||||
content_split[0] = 'inline'
|
||||
report_content_disposition = ';'.join(content_split)
|
||||
|
||||
return request.make_response(report, headers=[
|
||||
('Content-Type', 'application/pdf'),
|
||||
('Content-Length', len(report)),
|
||||
('Content-Disposition', report_content_disposition),
|
||||
])
|
||||
|
||||
def _extract_filters_data(self, survey, post):
|
||||
search_filters = []
|
||||
line_filter_domain, line_choices = [], []
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<field name="description" type="html"><p>Test your vendor skills!</p></field>
|
||||
<field name="certification_give_badge">True</field>
|
||||
<field name="certification_badge_id" ref="vendor_certification_badge"/>
|
||||
<field name="background_image" type="base64" file="survey/static/src/img/survey_background_2.jpg"/>
|
||||
</record>
|
||||
<!-- Page 1 -->
|
||||
<record model="survey.question" id="vendor_certification_page_1">
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<field name="questions_layout">page_per_section</field>
|
||||
<field name="description" type="html">
|
||||
<p>This small quiz will test your knowledge about our Company. Be prepared !</p></field>
|
||||
<field name="background_image" type="base64" file="survey/static/src/img/survey_background.jpg"/>
|
||||
</record>
|
||||
|
||||
<!-- Page 1: general informations -->
|
||||
@@ -279,4 +280,45 @@
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">Winter</field>
|
||||
</record>
|
||||
|
||||
<!-- Page 4: Feedback - non scored question -->
|
||||
<record id="survey_demo_quiz_p4" model="survey.question">
|
||||
<field name="title">Your feeling</field>
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">26</field>
|
||||
<field name="question_type" eval="False"/>
|
||||
<field name="is_page" eval="True"/>
|
||||
<field name="description" type="html">
|
||||
<p>We may be interested by your input.</p></field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q1" model="survey.question">
|
||||
<field name="survey_id" ref="survey_demo_quiz"/>
|
||||
<field name="sequence">27</field>
|
||||
<field name="title">What do you think about this survey ?</field>
|
||||
<field name="description" type="html"><span>If you don't like us, please try to be as objective as possible.</span></field>
|
||||
<field name="question_type">simple_choice</field>
|
||||
<field name="comments_allowed" eval="True"/>
|
||||
<field name="comment_count_as_answer" eval="True"/>
|
||||
<field name="constr_mandatory" eval="False"/>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q1_sug1" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q1"/>
|
||||
<field name="sequence">1</field>
|
||||
<field name="value">Good</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q1_sug2" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q1"/>
|
||||
<field name="sequence">2</field>
|
||||
<field name="value">Not Good, Not Bad</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q1_sug3" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q1"/>
|
||||
<field name="sequence">3</field>
|
||||
<field name="value">Iznogoud</field>
|
||||
</record>
|
||||
<record id="survey_demo_quiz_p4_q1_sug4" model="survey.question.answer">
|
||||
<field name="question_id" ref="survey_demo_quiz_p4_q1"/>
|
||||
<field name="sequence">4</field>
|
||||
<field name="value">I have no idea, I'm a dog!</field>
|
||||
</record>
|
||||
</data></odoo>
|
||||
|
||||
@@ -30,6 +30,7 @@ class Survey(models.Model):
|
||||
description_done = fields.Html(
|
||||
"End Message", translate=True,
|
||||
help="This message will be displayed when survey is completed")
|
||||
background_image = fields.Binary("Background Image")
|
||||
active = fields.Boolean("Active", default=True)
|
||||
state = fields.Selection(selection=[
|
||||
('draft', 'Draft'), ('open', 'In Progress'), ('closed', 'Closed')
|
||||
@@ -49,6 +50,10 @@ class Survey(models.Model):
|
||||
('random', 'Randomized per section')],
|
||||
string="Selection", required=True, default='all',
|
||||
help="If randomized is selected, add the number of random questions next to the section.")
|
||||
progression_mode = fields.Selection([
|
||||
('percent', 'Percentage'),
|
||||
('number', 'Number')], string='Progression Mode', default='percent',
|
||||
help="If Number is selected, it will display the number of questions answered on the total number of question to answer.")
|
||||
# attendees
|
||||
user_input_ids = fields.One2many('survey.user_input', 'survey_id', string='User responses', readonly=True, groups='survey.group_survey_user')
|
||||
# security / access
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 163 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
@@ -6,11 +6,13 @@ var publicWidget = require('web.public.widget');
|
||||
var time = require('web.time');
|
||||
var core = require('web.core');
|
||||
var _t = core._t;
|
||||
var dom = require('web.dom');
|
||||
|
||||
publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
selector: '.o_survey_form',
|
||||
events: {
|
||||
'change .o_survey_form_choice_item': '_onChangeChoiceItem',
|
||||
'click .o_survey_matrix_btn': '_onMatrixBtnClick',
|
||||
'click button[type="submit"]': '_onSubmit',
|
||||
},
|
||||
custom_events: {
|
||||
@@ -29,6 +31,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
this.fadeInOutDelay = 400;
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
self.options = self.$target.find('form').data();
|
||||
// Init fields
|
||||
if (!self.options.isStartScreen) {
|
||||
self._initTimer();
|
||||
self._initBreadcrumb();
|
||||
@@ -36,6 +39,13 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
self.$('div.o_survey_form_date').each(function () {
|
||||
self._initDateTimePicker($(this));
|
||||
});
|
||||
self._initChoiceItems();
|
||||
self._initTextArea();
|
||||
self._focusOnFirstInput();
|
||||
// Init event listener
|
||||
if (!self.options.readonly) {
|
||||
$(document).on('keypress', self._onKeyPress.bind(self));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -46,6 +56,35 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
// Handlers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
_onKeyPress: function (event) {
|
||||
// If user is answering a textarea, do not handle keyPress
|
||||
if (this.$("textarea").is(":focus")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var keyCode = event.keyCode;
|
||||
var letter = String.fromCharCode(keyCode).toUpperCase();
|
||||
|
||||
// Handle Start / Next / Submit
|
||||
if (keyCode === 13) { // Enter : go Next
|
||||
event.preventDefault();
|
||||
this._submitForm({});
|
||||
} else if (self.options.questionsLayout === 'page_per_question'
|
||||
&& letter.match(/[a-z]/i)) {
|
||||
var $choiceInput = this.$(`input[data-selection-key=${letter}]`);
|
||||
if ($choiceInput.length === 1) {
|
||||
if ($choiceInput.attr('type') === 'radio') {
|
||||
$choiceInput.prop("checked", true).trigger('change');
|
||||
} else {
|
||||
$choiceInput.prop("checked", !$choiceInput.prop("checked")).trigger('change');
|
||||
}
|
||||
// Avoid selection key to be typed into the textbox if 'other' is selected by key
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -55,7 +94,8 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
* @param {Event} event
|
||||
*/
|
||||
_onChangeChoiceItem: function (event) {
|
||||
var $choiceItemGroup = $(event.currentTarget).parents('.o_survey_form_choice');
|
||||
var $target = $(event.currentTarget);
|
||||
var $choiceItemGroup = $target.closest('.o_survey_form_choice');
|
||||
var $otherItem = $choiceItemGroup.find('.o_survey_js_form_other_comment');
|
||||
var $commentInput = $choiceItemGroup.find('textarea[type="text"]');
|
||||
|
||||
@@ -68,6 +108,36 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
$commentInput.val('');
|
||||
$commentInput.enable(false);
|
||||
}
|
||||
|
||||
var $matrixBtn = $target.closest('.o_survey_matrix_btn');
|
||||
if ($target.attr('type') === 'radio') {
|
||||
if ($matrixBtn.length > 0) {
|
||||
$matrixBtn.closest('tr').find('td').removeClass('o_survey_selected');
|
||||
$matrixBtn.addClass('o_survey_selected');
|
||||
} else {
|
||||
$choiceItemGroup.find('label').removeClass('o_survey_selected');
|
||||
$target.closest('label').addClass('o_survey_selected');
|
||||
}
|
||||
} else { // $target.attr('type') === 'checkbox'
|
||||
if ($matrixBtn.length > 0) {
|
||||
$matrixBtn.toggleClass('o_survey_selected', !$matrixBtn.hasClass('o_survey_selected'));
|
||||
} else {
|
||||
var $label = $target.closest('label');
|
||||
$label.toggleClass('o_survey_selected', !$label.hasClass('o_survey_selected'));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onMatrixBtnClick: function (event) {
|
||||
if (!this.options.readonly) {
|
||||
var $target = $(event.currentTarget);
|
||||
var $input = $target.find('input');
|
||||
if ($input.attr('type') === 'radio') {
|
||||
$input.prop("checked", true).trigger('change');
|
||||
} else {
|
||||
$input.prop("checked", !$input.prop("checked")).trigger('change');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onSubmit: function (event) {
|
||||
@@ -177,8 +247,11 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
} else {
|
||||
this._updateBreadcrumb();
|
||||
}
|
||||
self._initChoiceItems();
|
||||
self._initTextArea();
|
||||
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') {
|
||||
this.$('.o_survey_form_content').fadeIn(0);
|
||||
@@ -321,7 +394,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
*/
|
||||
_prepareSubmitValues: function (formData, params) {
|
||||
var self = this;
|
||||
formData.forEach(function (value, key){
|
||||
formData.forEach(function (value, key) {
|
||||
switch (key) {
|
||||
case 'csrf_token':
|
||||
case 'token':
|
||||
@@ -463,6 +536,25 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
// INIT FIELDS TOOLS
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will allow the textarea to resize on carriage return instead of showing scrollbar.
|
||||
*/
|
||||
_initTextArea: function () {
|
||||
this.$('textarea').each(function () {
|
||||
dom.autoresize($(this));
|
||||
});
|
||||
},
|
||||
|
||||
_initChoiceItems: function () {
|
||||
this.$("input[type='radio'],input[type='checkbox']").each(function () {
|
||||
var matrixBtn = $(this).parents('.o_survey_matrix_btn');
|
||||
if ($(this).prop("checked")) {
|
||||
var $target = matrixBtn.length > 0 ? matrixBtn : $(this).closest('label');
|
||||
$target.addClass('o_survey_selected');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Will initialize the breadcrumb widget that handles navigation to a previously filled in page.
|
||||
*
|
||||
@@ -573,6 +665,17 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 -)
|
||||
*/
|
||||
_focusOnFirstInput: function () {
|
||||
var $inputs = this.$("input[type='text'],input[type='number'],textarea").not('.o_survey_comment');
|
||||
if ($inputs.length > 0) {
|
||||
$inputs.first().focus();
|
||||
}
|
||||
},
|
||||
|
||||
// ERRORS TOOLS
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -591,10 +694,12 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
var scrollLocation = $target.offset().top;
|
||||
var navbarHeight = $('.o_main_navbar').height();
|
||||
if (navbarHeight) {
|
||||
scrollLocation -= navbarHeight;
|
||||
// In overflow auto, scrollLocation of target can be negative if target is out of screen (up side)
|
||||
scrollLocation = scrollLocation >= 0 ? scrollLocation - navbarHeight : scrollLocation + navbarHeight;
|
||||
}
|
||||
$('html, body').animate({
|
||||
scrollTop: scrollLocation
|
||||
var scrollinside = $("#wrapwrap").scrollTop();
|
||||
$('#wrapwrap').animate({
|
||||
scrollTop: scrollinside + scrollLocation
|
||||
}, 500);
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
odoo.define('survey.print', function (require) {
|
||||
'use strict';
|
||||
|
||||
var publicWidget = require('web.public.widget');
|
||||
var dom = require('web.dom');
|
||||
|
||||
publicWidget.registry.SurveyPrintWidget = publicWidget.Widget.extend({
|
||||
selector: '.o_survey_print',
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Widget
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
// Will allow the textarea to resize if any carriage return instead of showing scrollbar.
|
||||
self.$('textarea').each(function () {
|
||||
dom.autoresize($(this));
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
return publicWidget.registry.SurveyPrintWidget;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,193 @@
|
||||
.o_survey_wrap {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
Common Style
|
||||
**********************************************************/
|
||||
|
||||
.o_survey_form, .o_survey_print {
|
||||
.o_survey_question_error {
|
||||
height: 0px;
|
||||
transition: height .5s ease;
|
||||
line-height: 4rem;
|
||||
&.slide_in {
|
||||
height: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.o_survey_question_text_box,
|
||||
.o_survey_question_date,
|
||||
.o_survey_question_datetime,
|
||||
.o_survey_question_numerical_box {
|
||||
border: 0px;
|
||||
border-bottom: 2px solid $o-brand-primary;
|
||||
&:disabled {
|
||||
color: black !important;
|
||||
border-color: $gray-600;
|
||||
border-bottom: 2px solid $gray-600;
|
||||
}
|
||||
&:focus {
|
||||
border-color: $o-brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.o_survey_form_date .input-group-append {
|
||||
right: 0;
|
||||
.input-group-text i {
|
||||
font-size: large;
|
||||
}
|
||||
}
|
||||
|
||||
.o_survey_choice_btn {
|
||||
transition: background-color 0.3s ease;
|
||||
flex: 1 0 300px;
|
||||
|
||||
span {
|
||||
line-height: 25px;
|
||||
}
|
||||
i {
|
||||
top: 0px;
|
||||
font-size: large;
|
||||
&.fa-check-circle,&.fa-check-square {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.o_survey_selected i {
|
||||
display: none;
|
||||
&.fa-check-circle,&.fa-check-square {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_survey_question_matrix {
|
||||
td {
|
||||
min-width: 100px;
|
||||
i {
|
||||
font-size: 22px;
|
||||
display: none;
|
||||
&.o_survey_matrix_empty_checkbox {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
.o_survey_choice_key > span > span {
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
&.o_survey_selected {
|
||||
i {
|
||||
display: inline;
|
||||
&.o_survey_matrix_empty_checkbox {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
Form Specific Style
|
||||
**********************************************************/
|
||||
|
||||
.o_survey_form {
|
||||
min-height: 100%;
|
||||
|
||||
.o_survey_choice_btn {
|
||||
background-color: rgba($o-brand-primary, 0.5);
|
||||
border: 2px solid $o-brand-primary;
|
||||
cursor: pointer;
|
||||
|
||||
&.o_survey_selected {
|
||||
background-color: $o-brand-primary;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($o-brand-primary, 0.8);
|
||||
.o_survey_choice_key span.o_survey_key {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_survey_choice_key {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border: 2px solid $o-brand-primary;
|
||||
span {
|
||||
font-size: smaller;
|
||||
top: -2px;
|
||||
&.o_survey_key {
|
||||
width: inherit;
|
||||
right: 10px;
|
||||
border: 2px solid $o-brand-primary;
|
||||
border-right: 0px;
|
||||
height: 25px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_survey_question_matrix {
|
||||
th {
|
||||
background-color: $o-brand-primary;
|
||||
}
|
||||
td {
|
||||
background-color: rgba($o-brand-primary, 0.2);
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($o-brand-primary, 0.5);
|
||||
cursor: pointer;
|
||||
.o_survey_choice_key span.o_survey_key {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_survey_progress {
|
||||
height:0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
Print Specific Style
|
||||
**********************************************************/
|
||||
|
||||
.o_survey_print {
|
||||
.o_survey_choice_btn {
|
||||
background-color: $gray-500;
|
||||
border-color: transparent;
|
||||
cursor: default;
|
||||
|
||||
&.bg-success, &.bg-danger {
|
||||
opacity: 0.6;
|
||||
}
|
||||
&.o_survey_selected {
|
||||
background-color: $gray-600;
|
||||
opacity: 1;
|
||||
}
|
||||
i.fa-square, i.fa-circle {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.o_survey_question_matrix {
|
||||
th {
|
||||
/* important needed to force override bg-primary set on th in the template */
|
||||
background-color: $gray-600 !important;
|
||||
}
|
||||
td {
|
||||
background-color: $gray-200;
|
||||
&:hover {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
i.fa-check-square, i.fa-check-circle, i.o_survey_matrix_empty_checkbox {
|
||||
color: $gray-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
.o_survey_header {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 25px;
|
||||
}
|
||||
|
||||
.o_survey_title {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.js_question-wrapper {
|
||||
padding-bottom: 25px;
|
||||
}
|
||||
|
||||
.o_survey_breadcrumb_container {
|
||||
min-height: 3rem;
|
||||
}
|
||||
|
||||
.o_survey_question_error {
|
||||
overflow: hidden;
|
||||
height: 0px;
|
||||
border: 0px;
|
||||
padding: 0rem 0.75rem;
|
||||
transition: height .5s ease;
|
||||
line-height: 4rem;
|
||||
&.slide_in {
|
||||
height: 4rem;
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,11 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="survey.survey_breadcrumb_template">
|
||||
<ol class="breadcrumb justify-content-end">
|
||||
<ol class="breadcrumb justify-content-end bg-transparent">
|
||||
<t t-set="canGoBack" t-value="widget.canGoBack"/>
|
||||
<t t-foreach="widget.pages" t-as="page">
|
||||
<li t-att-class="'breadcrumb-item' + (page.id === widget.currentPageId ? ' active' : '')"
|
||||
<t t-set="isActivePage" t-value="page.id === widget.currentPageId"/>
|
||||
<li t-att-class="'breadcrumb-item' + (isActivePage ? ' active font-weight-bold' : '')"
|
||||
t-att-data-page-id="page.id"
|
||||
t-att-data-page-title="page.title">
|
||||
<t t-if="widget.currentPageId === page.id">
|
||||
@@ -14,12 +15,13 @@
|
||||
<t t-set="canGoBack" t-value="false" />
|
||||
</t>
|
||||
<t t-if="canGoBack">
|
||||
<a href="#">
|
||||
<a class="text-primary" href="#">
|
||||
<span t-esc="page.title" />
|
||||
</a>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span t-esc="page.title" />
|
||||
<span t-att-class="(isActivePage ? 'text-black' : 'text-muted')"
|
||||
t-esc="page.title" />
|
||||
</t>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
@@ -24,43 +24,43 @@ var failSteps = [{ // Page-1
|
||||
trigger: 'button.btn.btn-primary.btn-lg:contains("Start Certification")',
|
||||
}, { // Question: Do we sell Acoustic Bloc Screens?
|
||||
content: "Selecting answer 'No'",
|
||||
trigger: 'div.js_question-wrapper:contains("Do we sell Acoustic Bloc Screens") label:contains("No") input',
|
||||
trigger: 'div.js_question-wrapper:contains("Do we sell Acoustic Bloc Screens") label:contains("No")',
|
||||
}, { // Question: Select all the existing products
|
||||
content: "Ticking answer 'Fanta'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Fanta") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Fanta")'
|
||||
}, {
|
||||
content: "Ticking answer 'Drawer'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Drawer") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Drawer")'
|
||||
}, {
|
||||
content: "Ticking answer 'Conference chair'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Conference chair") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Conference chair")'
|
||||
}, { // Question: Select all the available customizations for our Customizable Desk
|
||||
content: "Ticking answer 'Color'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the available customizations for our Customizable Desk") label:contains("Color") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the available customizations for our Customizable Desk") label:contains("Color")'
|
||||
}, {
|
||||
content: "Ticking answer 'Height'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the available customizations for our Customizable Desk") label:contains("Height") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the available customizations for our Customizable Desk") label:contains("Height")'
|
||||
}, { // Question: How many versions of the Corner Desk do we have?
|
||||
content: "Selecting answer '2'",
|
||||
trigger: 'div.js_question-wrapper:contains("How many versions of the Corner Desk do we have") label:contains("2") input',
|
||||
trigger: 'div.js_question-wrapper:contains("How many versions of the Corner Desk do we have") label:contains("2")',
|
||||
}, { // Question: Do you think we have missing products in our catalog? (not rated)
|
||||
content: "Missing products",
|
||||
trigger: 'div.js_question-wrapper:contains("Do you think we have missing products in our catalog") textarea',
|
||||
run: "text I don't know products enough to be able to answer that",
|
||||
}, { // Page-2 Question: How much do we sell our Cable Management Box?
|
||||
content: "Selecting answer '80$'",
|
||||
trigger: 'div.js_question-wrapper:contains("How much do we sell our Cable Management Box") label:contains("80$") input',
|
||||
trigger: 'div.js_question-wrapper:contains("How much do we sell our Cable Management Box") label:contains("80$")',
|
||||
}, { // Question: Select all the the products that sell for 100$ or more
|
||||
content: "Ticking answer 'Corner Desk Right Sit'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Corner Desk Right Sit") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Corner Desk Right Sit")'
|
||||
}, {
|
||||
content: "Ticking answer 'Desk Combination'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Desk Combination") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Desk Combination")'
|
||||
}, {
|
||||
content: "Ticking answer 'Office Chair Black'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Office Chair Black") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Office Chair Black")'
|
||||
}, { // Question: What do you think about our prices (not rated)?
|
||||
trigger: 'div.js_question-wrapper:contains("What do you think about our prices") label:contains("Correctly priced") input',
|
||||
trigger: 'div.js_question-wrapper:contains("What do you think about our prices") label:contains("Correctly priced")',
|
||||
}, {
|
||||
content: "Finish Survey",
|
||||
trigger: 'button[type="submit"]',
|
||||
|
||||
@@ -23,44 +23,44 @@ tour.register('test_certification_success', {
|
||||
trigger: 'button.btn.btn-primary.btn-lg:contains("Start Certification")',
|
||||
}, { // Question: Do we sell Acoustic Bloc Screens?
|
||||
content: "Selecting answer 'Yes'",
|
||||
trigger: 'div.js_question-wrapper:contains("Do we sell Acoustic Bloc Screens") label:contains("Yes") input',
|
||||
trigger: 'div.js_question-wrapper:contains("Do we sell Acoustic Bloc Screens") label:contains("Yes")',
|
||||
}, { // Question: Select all the existing products
|
||||
content: "Ticking answer 'Chair floor protection'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Chair floor protection") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Chair floor protection")'
|
||||
}, {
|
||||
content: "Ticking answer 'Drawer'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Drawer") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Drawer")'
|
||||
}, {
|
||||
content: "Ticking answer 'Conference chair'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Conference chair") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the existing products") label:contains("Conference chair")'
|
||||
}, { // Question: Select all the available customizations for our Customizable Desk
|
||||
content: "Ticking answer 'Color'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the available customizations for our Customizable Desk") label:contains("Color") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the available customizations for our Customizable Desk") label:contains("Color")'
|
||||
}, {
|
||||
content: "Ticking answer 'Legs'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the available customizations for our Customizable Desk") label:contains("Legs") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the available customizations for our Customizable Desk") label:contains("Legs")'
|
||||
}, { // Question: How many versions of the Corner Desk do we have?
|
||||
content: "Selecting answer '2'",
|
||||
trigger: 'div.js_question-wrapper:contains("How many versions of the Corner Desk do we have") label:contains("2") input',
|
||||
trigger: 'div.js_question-wrapper:contains("How many versions of the Corner Desk do we have") label:contains("2")',
|
||||
}, { // Question: Do you think we have missing products in our catalog? (not rated)
|
||||
content: "Missing products",
|
||||
trigger: 'div.js_question-wrapper:contains("Do you think we have missing products in our catalog") textarea',
|
||||
run: "text I think we should make more versions of the customizable desk, it's such an amazing product!",
|
||||
}, { // Page-2 Question: How much do we sell our Cable Management Box?
|
||||
content: "Selecting answer '80$' (wrong one)",
|
||||
trigger: 'div.js_question-wrapper:contains("How much do we sell our Cable Management Box") label:contains("80$") input',
|
||||
trigger: 'div.js_question-wrapper:contains("How much do we sell our Cable Management Box") label:contains("80$")',
|
||||
}, { // Question: Select all the the products that sell for 100$ or more
|
||||
content: "Ticking answer 'Corner Desk Right Sit'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Corner Desk Right Sit") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Corner Desk Right Sit")'
|
||||
}, {
|
||||
content: "Ticking answer 'Desk Combination'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Desk Combination") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Desk Combination")'
|
||||
}, {
|
||||
content: "Ticking answer 'Large Desk'",
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Large Desk") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the the products that sell for 100$ or more") label:contains("Large Desk")'
|
||||
}, { // Question: What do you think about our prices (not rated)?
|
||||
content: "Selecting answer 'Underpriced'",
|
||||
trigger: 'div.js_question-wrapper:contains("What do you think about our prices") label:contains("Underpriced") input',
|
||||
trigger: 'div.js_question-wrapper:contains("What do you think about our prices") label:contains("Underpriced")',
|
||||
}, {
|
||||
content: "Finish Survey",
|
||||
trigger: 'button[type="submit"]',
|
||||
|
||||
@@ -21,7 +21,7 @@ tour.register('test_survey', {
|
||||
run: 'text 05/05/1980',
|
||||
}, {
|
||||
content: 'Answer How frequently do you buy products online',
|
||||
trigger: 'div.js_question-wrapper:contains("How frequently do you buy products online") label:contains("Once a month") input',
|
||||
trigger: 'div.js_question-wrapper:contains("How frequently do you buy products online") label:contains("Once a month")',
|
||||
}, {
|
||||
content: 'Answer How many times did you order products on our website',
|
||||
trigger: 'div.js_question-wrapper:contains("How many times did you order products on our website") input',
|
||||
@@ -33,25 +33,25 @@ tour.register('test_survey', {
|
||||
// Page-2
|
||||
{
|
||||
content: 'Answer Which of the following words would you use to describe our products (High Quality)',
|
||||
trigger: 'div.js_question-wrapper:contains("Which of the following words would you use to describe our products") label:contains("High quality") input',
|
||||
trigger: 'div.js_question-wrapper:contains("Which of the following words would you use to describe our products") label:contains("High quality")',
|
||||
}, {
|
||||
content: 'Answer Which of the following words would you use to describe our products (Good value for money)',
|
||||
trigger: 'div.js_question-wrapper:contains("Which of the following words would you use to describe our products") label:contains("Good value for money") input',
|
||||
trigger: 'div.js_question-wrapper:contains("Which of the following words would you use to describe our products") label:contains("Good value for money")',
|
||||
}, {
|
||||
content: 'Answer What do your think about our new eCommerce (The new layout and design is fresh and up-to-date)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The new layout and design is fresh and up-to-date") input:first',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The new layout and design is fresh and up-to-date") td:first',
|
||||
}, {
|
||||
content: 'Answer What do your think about our new eCommerce (It is easy to find the product that I want)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("It is easy to find the product that I want") input:eq(2)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("It is easy to find the product that I want") td:eq(2)',
|
||||
}, {
|
||||
content: 'Answer What do your think about our new eCommerce (The tool to compare the products is useful to make a choice)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The tool to compare the products is useful to make a choice") input:eq(3)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The tool to compare the products is useful to make a choice") td:eq(3)',
|
||||
}, {
|
||||
content: 'Answer What do your think about our new eCommerce (The checkout process is clear and secure)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The checkout process is clear and secure") input:eq(2)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The checkout process is clear and secure") td:eq(2)',
|
||||
}, {
|
||||
content: 'Answer What do your think about our new eCommerce (I have added products to my wishlist)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("I have added products to my wishlist") input:last',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("I have added products to my wishlist") td:last',
|
||||
}, {
|
||||
content: 'Answer Do you have any other comments, questions, or concerns',
|
||||
trigger: 'div.js_question-wrapper:contains("Do you have any other comments, questions, or concerns") textarea',
|
||||
|
||||
@@ -16,7 +16,7 @@ tour.register('test_survey_prefill', {
|
||||
trigger: 'div.js_question-wrapper:contains("When is your date of birth ?") input',
|
||||
run: 'text 05/05/1980',
|
||||
}, { // Question: How frequently do you buy products online ?
|
||||
trigger: 'div.js_question-wrapper:contains("How frequently do you buy products online ?") label:contains("Once a week") input',
|
||||
trigger: 'div.js_question-wrapper:contains("How frequently do you buy products online ?") label:contains("Once a week")',
|
||||
}, { // Question: How many times did you order products on our website ?
|
||||
trigger: 'div.js_question-wrapper:contains("How many times did you order products on our website ?") input',
|
||||
run: 'text 42',
|
||||
@@ -27,25 +27,25 @@ tour.register('test_survey_prefill', {
|
||||
// Page-2
|
||||
{ // Question: Which of the following words would you use to describe our products ?
|
||||
content: 'Answer Which of the following words would you use to describe our products (High Quality)',
|
||||
trigger: 'div.js_question-wrapper:contains("Which of the following words would you use to describe our products") label:contains("High quality") input',
|
||||
trigger: 'div.js_question-wrapper:contains("Which of the following words would you use to describe our products") label:contains("High quality")',
|
||||
}, {
|
||||
content: 'Answer Which of the following words would you use to describe our products (Good value for money)',
|
||||
trigger: 'div.js_question-wrapper:contains("Which of the following words would you use to describe our products") label:contains("Good value for money") input',
|
||||
trigger: 'div.js_question-wrapper:contains("Which of the following words would you use to describe our products") label:contains("Good value for money")',
|
||||
}, {
|
||||
content: 'Answer What do your think about our new eCommerce (The new layout and design is fresh and up-to-date)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The new layout and design is fresh and up-to-date") input:first',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The new layout and design is fresh and up-to-date") td:first',
|
||||
}, {
|
||||
content: 'Answer What do your think about our new eCommerce (It is easy to find the product that I want)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("It is easy to find the product that I want") input:eq(2)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("It is easy to find the product that I want") td:eq(2)',
|
||||
}, {
|
||||
content: 'Answer What do your think about our new eCommerce (The tool to compare the products is useful to make a choice)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The tool to compare the products is useful to make a choice") input:eq(3)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The tool to compare the products is useful to make a choice") td:eq(3)',
|
||||
}, {
|
||||
content: 'Answer What do your think about our new eCommerce (The checkout process is clear and secure)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The checkout process is clear and secure") input:eq(2)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("The checkout process is clear and secure") td:eq(2)',
|
||||
}, {
|
||||
content: 'Answer What do your think about our new eCommerce (I have added products to my wishlist)',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("I have added products to my wishlist") input:last',
|
||||
trigger: 'div.js_question-wrapper:contains("What do your think about our new eCommerce") tr:contains("I have added products to my wishlist") td:last',
|
||||
}, {
|
||||
content: 'Answer Do you have any other comments, questions, or concerns',
|
||||
trigger: 'div.js_question-wrapper:contains("Do you have any other comments, questions, or concerns") textarea',
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
<script type="text/javascript" src="/survey/static/src/js/survey_timer.js" />
|
||||
<script type="text/javascript" src="/survey/static/src/js/survey_breadcrumb.js" />
|
||||
<script type="text/javascript" src="/survey/static/src/js/survey_form.js" />
|
||||
<script type="text/javascript" src="/survey/static/src/js/survey_print.js" />
|
||||
<script type="text/javascript" src="/survey/static/src/js/survey_result.js" />
|
||||
|
||||
<t t-call="web._assets_helpers"/>
|
||||
<link href="/survey/static/src/css/survey_print.css" rel="stylesheet" type="text/css"/>
|
||||
<link href="/survey/static/src/css/survey_result.css" rel="stylesheet" type="text/css"></link>
|
||||
<link rel="stylesheet" type="text/scss" href="/survey/static/src/scss/survey_templates.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/survey/static/src/scss/survey_form.scss"/>
|
||||
</template>
|
||||
|
||||
<template id="survey_report_assets_pdf" inherit_id="web.report_assets_pdf">
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<field name="background_image" widget="image" class="oe_avatar"/>
|
||||
<div class="oe_title" style="width: 100%;">
|
||||
<label for="title" class="oe_edit_only"/>
|
||||
<h1><field name="title" placeholder="e.g. Satisfaction Survey"/></h1>
|
||||
@@ -75,6 +76,7 @@
|
||||
<group name="options">
|
||||
<group string="Questions" name="questions">
|
||||
<field name="questions_layout" widget="radio" />
|
||||
<field name="progression_mode" widget="radio" />
|
||||
<label for="is_time_limited" string="Time Limit"/>
|
||||
<div>
|
||||
<field name="is_time_limited" nolabel="1"/>
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
<data>
|
||||
<!-- Main survey layout -->
|
||||
<template id="survey.layout" name="Survey Layout" inherit_id="web.frontend_layout" primary="True">
|
||||
<xpath expr="//div[@id='wrapwrap']" position="attributes">
|
||||
<attribute name="t-att-style" add="('height: 100%; overflow: auto; background: url(' + '/survey/get_background_image/%s/%s' % (survey.access_token, answer.access_token) + ') no-repeat fixed center; box-shadow: inset 0 0 0 10000px rgba(255,255,255,.7); background-size: cover;') if survey and survey.background_image and answer else 'height: 100%; overflow: auto;'"/>
|
||||
</xpath>
|
||||
<xpath expr="//head/t[@t-call-assets][last()]" position="after">
|
||||
<t t-call-assets="survey.survey_assets" lazy_load="True"/>
|
||||
</xpath>
|
||||
@@ -17,8 +20,8 @@
|
||||
|
||||
<template id="survey_page_fill" name="Survey: main page (take survey)">
|
||||
<t t-call="survey.layout">
|
||||
<div class="wrap">
|
||||
<div class="container o_survey_form">
|
||||
<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-call="survey.survey_fill_header" />
|
||||
@@ -29,7 +32,7 @@
|
||||
</template>
|
||||
|
||||
<template id="survey_fill_header" name="Survey: main page header">
|
||||
<div class="o_page_header o_survey_header">
|
||||
<div class="o_survey_nav pt16 pb24">
|
||||
<div class="container m-0 p-0">
|
||||
<div class="row">
|
||||
<div class="col-lg-8"><h1 t-esc="survey.title"></h1></div>
|
||||
@@ -38,7 +41,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div t-att-class="'o_survey_breadcrumb_container mt8 bg-200' + (' d-none ' if answer.state != 'in_progress' else '')"
|
||||
<div t-att-class="'o_survey_breadcrumb_container mt8' + (' d-none ' if answer.state != 'in_progress' else '')"
|
||||
t-if="survey.questions_layout == 'page_per_section' and answer.state != 'done'"
|
||||
t-att-data-can-go-back="survey.users_can_go_back"
|
||||
t-att-data-pages="json.dumps(breadcrumb_pages)" />
|
||||
@@ -48,19 +51,21 @@
|
||||
<template id="survey_fill_form" name="Survey: main page content">
|
||||
<t t-set="survey_form_readonly" t-value="false"/>
|
||||
<form role="form" method="post" t-att-name="survey.id"
|
||||
class="d-flex flex-grow-1 align-items-center"
|
||||
t-att-data-answer-token="answer.access_token"
|
||||
t-att-data-survey-token="survey.access_token"
|
||||
t-att-data-timer="answer.start_datetime.isoformat() if survey.is_time_limited
|
||||
and answer.start_datetime and answer.state != 'done' else False"
|
||||
t-att-data-time-limit-minutes="survey.time_limit"
|
||||
t-att-data-is-start-screen="answer.state == 'new'">
|
||||
t-att-data-is-start-screen="answer.state == 'new'"
|
||||
t-att-data-questions-layout="survey.questions_layout">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<input type="hidden" name="token" t-att-value="answer.access_token" />
|
||||
<div class="o_survey_error alert alert-danger d-none" role="alert">
|
||||
<p>There was an error during the validation of the survey.</p>
|
||||
</div>
|
||||
|
||||
<div class="o_survey_form_content">
|
||||
<div class="o_survey_form_content w-100">
|
||||
<t t-if="answer.state == 'new'" t-call="survey.survey_fill_form_start"/>
|
||||
<t t-elif="answer.state == 'in_progress'" t-call="survey.survey_fill_form_in_progress" />
|
||||
<t t-else="" t-call="survey.survey_fill_form_done"/>
|
||||
@@ -83,7 +88,7 @@
|
||||
</template>
|
||||
|
||||
<template id="survey_fill_form_start" name="Survey: start form content">
|
||||
<div class="wrap o_survey_start">
|
||||
<div class="wrap o_survey_start pb256">
|
||||
<div class='mt32 mb32'>
|
||||
<div t-field='survey.description' class="oe_no_empty"/>
|
||||
<div t-if="survey.is_time_limited">
|
||||
@@ -99,7 +104,7 @@
|
||||
<t t-else="">
|
||||
Start Survey
|
||||
</t>
|
||||
</button>
|
||||
</button><span class="font-weight-bold ml-2 text-primary d-none d-md-inline">or press Enter</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -107,17 +112,18 @@
|
||||
<template id="survey_fill_form_in_progress" name="Survey: form with questions">
|
||||
<t t-if="survey.questions_layout == 'one_page'">
|
||||
<t t-foreach='survey.question_and_page_ids' t-as='question'>
|
||||
<h2 t-if="question.is_page" t-field='question.title' class="o_survey_title" />
|
||||
<h2 t-if="question.is_page" t-field='question.title' class="o_survey_title pb16" />
|
||||
<t t-if="not question.is_page and question in answer.predefined_question_ids" t-call="survey.question_container"/>
|
||||
</t>
|
||||
|
||||
<div class="text-center mt16 mb256">
|
||||
<button type="submit" value="finish" class="btn btn-primary">Submit Survey</button>
|
||||
<button type="submit" value="finish" class="btn btn-primary">
|
||||
Submit</button><span class="font-weight-bold ml-2 text-primary d-none d-md-inline">or press Enter</span>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-if="survey.questions_layout == 'page_per_section'">
|
||||
<h2 t-field='page.title' class="o_survey_title" />
|
||||
<h2 t-field='page.title' class="o_survey_title pb16" />
|
||||
<div t-field='page.description' class="oe_no_empty"/>
|
||||
|
||||
<input type="hidden" name="page_id" t-att-value="page.id" />
|
||||
@@ -125,12 +131,19 @@
|
||||
<t t-if="question in answer.predefined_question_ids" t-call="survey.question_container"/>
|
||||
</t>
|
||||
|
||||
<div class="text-center mt16 mb256">
|
||||
<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">Previous page</button>
|
||||
<button type="submit" t-att-value="'next' if not last else 'finish'" class="btn btn-primary">
|
||||
<t t-esc="'Next Page' if not last else 'Submit Survey'"/>
|
||||
</button>
|
||||
<div class="row">
|
||||
<div class="offset-3 col-6 text-center mt16 mb128">
|
||||
<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 last else 'finish'" class="btn btn-primary">
|
||||
Continue</button><span class="font-weight-bold ml-2 text-primary d-none d-md-inline"> or press Enter</span>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<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>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -138,12 +151,19 @@
|
||||
<input type="hidden" name="question_id" t-att-value="question.id" />
|
||||
<t t-call="survey.question_container"/>
|
||||
|
||||
<div class="text-center mt16 mb256">
|
||||
<button t-if="survey.users_can_go_back and question != answer.predefined_question_ids[0]" type="submit" class="btn btn-secondary"
|
||||
name="button_submit" value="previous" t-att-data-previous-page-id="previous_page_id">Previous page</button>
|
||||
<button type="submit" t-att-value="'next' if not last else 'finish'" class="btn btn-primary">
|
||||
<t t-esc="'Next Page' if not last else 'Submit Survey'"/>
|
||||
</button>
|
||||
<div class="row">
|
||||
<div class="offset-3 col-6 text-center mt16 mb128">
|
||||
<button t-if="survey.users_can_go_back and question != answer.predefined_question_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 last else 'finish'" class="btn btn-primary">
|
||||
Continue</button><span class="font-weight-bold ml-2 text-primary d-none d-md-inline">or press Enter</span>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<t 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>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
@@ -151,7 +171,7 @@
|
||||
<!-- 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">
|
||||
<div class="o_survey_finished mt32 mb32 pb256">
|
||||
<h1>Thank you!</h1>
|
||||
<div t-field="survey.description_done" class="oe_no_empty" />
|
||||
<div class="row">
|
||||
@@ -204,7 +224,10 @@
|
||||
<!-- Question widgets -->
|
||||
<template id="question_container" name="Survey: question container">
|
||||
<t t-set="answer_lines" t-value="answer.user_input_line_ids.filtered(lambda line: line.question_id == question)"/>
|
||||
<div class="js_question-wrapper" t-att-id="question.id" t-att-data-required="question.constr_mandatory"
|
||||
<!--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) < len(letters) and survey.questions_layout == 'page_per_question'"/>
|
||||
<div class="js_question-wrapper pb-4" 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>
|
||||
@@ -220,26 +243,30 @@
|
||||
<t t-if="question.question_type == 'simple_choice'"><t t-call="survey.question_simple_choice"/></t>
|
||||
<t t-if="question.question_type == 'multiple_choice'"><t t-call="survey.question_multiple_choice"/></t>
|
||||
<t t-if="question.question_type == 'matrix'"><t t-call="survey.question_matrix"/></t>
|
||||
<div class="o_survey_question_error alert alert-danger" role="alert"></div>
|
||||
<div class="o_survey_question_error overflow-hidden border-0 py-0 px-3 alert alert-danger" role="alert"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="question_text_box" name="Question: free text box">
|
||||
<textarea class="form-control o_survey_question_text_box" 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 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"
|
||||
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">
|
||||
<input t-att-type="'email' if question.validation_email else 'text'"
|
||||
class="form-control o_survey_question_text_box" t-att-name="question.id"
|
||||
<div class="o_survey_comment_container py-0 px-1">
|
||||
<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"
|
||||
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"
|
||||
t-att-data-validation-length-max="question.validation_length_max if question.validation_required else False"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="question_numerical_box" name="Question: numerical box">
|
||||
<input type="number" step="any" class="form-control o_survey_question_numerical_box" 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 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"
|
||||
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"/>
|
||||
@@ -249,12 +276,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"
|
||||
<input type="text" class="form-control datetimepicker-input o_survey_question_date font-weight-bold bg-transparent text-primary rounded-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" t-attf-data-target="#datetimepicker_#{question.id}" data-toggle="datetimepicker">
|
||||
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
@@ -263,12 +290,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"
|
||||
<input type="text" class="form-control datetimepicker-input o_survey_question_datetime font-weight-bold bg-transparent text-primary rounded-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" t-attf-data-target="#datetimepicker_#{question.id}" data-toggle="datetimepicker">
|
||||
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
@@ -279,27 +306,58 @@
|
||||
<div class="row o_survey_form_choice"
|
||||
t-att-data-name="question.id"
|
||||
data-question-type="simple_choice_radio">
|
||||
<div t-foreach='question.suggested_answer_ids' t-as='label' t-attf-class="col-lg-#{question.column_nb}">
|
||||
<label t-att-class="' bg-success ' if quizz_correction and label.answer_score > 0.0 else None">
|
||||
<input type="radio" t-att-value='label.id' class="o_survey_form_choice_item"
|
||||
t-att-name='question.id'
|
||||
t-att-checked="answer_line and answer_line.suggested_answer_id.id == label.id and 'checked' or None"/>
|
||||
<span t-field='label.value'/>
|
||||
</label>
|
||||
<t t-set="item_idx" t-value="0"/>
|
||||
<div class="d-flex flex-wrap font-weight-bold 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"/>
|
||||
<t t-set="answer_selected" t-value="answer_line and answer_line.suggested_answer_id.id == label.id"/>
|
||||
<t t-set="is_correct" t-value="label.answer_score > 0.0"/>
|
||||
|
||||
<!--Used for print mode with corrections -->
|
||||
<t t-set="answer_class" t-if="not has_correct_answer" t-value="''" />
|
||||
<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-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 t-call="survey.survey_selection_key">
|
||||
<t t-set="selection_key_class" t-value="'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"
|
||||
t-att-name='question.id'
|
||||
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>
|
||||
</label>
|
||||
</t>
|
||||
</div>
|
||||
<div t-if='question.comments_allowed and question.comment_count_as_answer' class="js_comments col-lg-12" >
|
||||
<label>
|
||||
<input type="radio" class="o_survey_form_choice_item o_survey_js_form_other_comment" value="-1"
|
||||
t-att-name='question.id'
|
||||
t-att-checked="comment_line and 'checked' or None"/>
|
||||
<span t-field="question.comments_message" />
|
||||
</label>
|
||||
<textarea type="text" class="form-control"
|
||||
t-attf-disabled="#{'' if comment_line else 'disabled'}"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea>
|
||||
<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 '')">
|
||||
<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>
|
||||
<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'
|
||||
t-att-checked="comment_line and 'checked' or None"
|
||||
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>
|
||||
</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"
|
||||
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">
|
||||
<span t-field="question.comments_message"/>
|
||||
<textarea type="text" class="form-control o_survey_comment"><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 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>
|
||||
</div>
|
||||
</template>
|
||||
@@ -309,61 +367,132 @@
|
||||
<div class="row o_survey_form_choice o_survey_question_multiple_choice"
|
||||
t-att-data-name="question.id"
|
||||
t-att-data-question-type="question.question_type">
|
||||
<div t-foreach='question.suggested_answer_ids' t-as='label' t-attf-class="col-lg-#{question.column_nb}">
|
||||
<t t-set="answer_line" t-value="answer_lines.filtered(lambda line: line.suggested_answer_id == label)"/>
|
||||
<label t-att-class="' bg-success ' if quizz_correction and label.answer_score > 0.0 else None">
|
||||
<input type="checkbox" t-att-value='label.id' class="o_survey_form_choice_item"
|
||||
t-att-name="question.id"
|
||||
t-att-checked="'checked' if answer_line else None"/>
|
||||
<span t-field='label.value'/>
|
||||
</label>
|
||||
</div>
|
||||
<t t-set="item_idx" t-value="0"/>
|
||||
<div class="d-flex flex-wrap font-weight-bold 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"/>
|
||||
<t t-set="answer_line" t-value="answer_lines.filtered(lambda line: line.suggested_answer_id == label)"/>
|
||||
<t t-set="answer_selected" t-value="answer_line and answer_line.suggested_answer_id.id == label.id"/>
|
||||
<t t-set="is_correct" t-value="label.answer_score > 0.0"/>
|
||||
|
||||
<div t-if='question.comments_allowed and question.comment_count_as_answer' class="js_ck_comments col-lg-12" >
|
||||
<label>
|
||||
<input type="checkbox" class="o_survey_form_choice_item o_survey_js_form_other_comment" value="-1"
|
||||
t-att-name="question.id"
|
||||
t-att-checked="comment_line and 'checked' or None"/>
|
||||
<span t-field="question.comments_message" />
|
||||
</label>
|
||||
<textarea type="text" class="form-control"
|
||||
t-attf-disabled="#{'' if comment_line else 'disabled'}"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea>
|
||||
<!--Used for print mode with corrections -->
|
||||
<t t-set="answer_class" t-if="not has_correct_answer" t-value="''" />
|
||||
<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 '')">
|
||||
<t t-call="survey.survey_selection_key">
|
||||
<t t-set="selection_key_class" t-value="'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"
|
||||
t-att-checked="'checked' if answer_line else None"
|
||||
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>
|
||||
</label>
|
||||
</t>
|
||||
</div>
|
||||
<div t-if='question.comments_allowed and not question.comment_count_as_answer' class="col-lg-12">
|
||||
<span t-field="question.comments_message"/>
|
||||
<textarea type="text" class="form-control o_survey_comment"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea>
|
||||
<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 '')">
|
||||
<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>
|
||||
<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"
|
||||
t-att-checked="comment_line and 'checked' or None"
|
||||
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>
|
||||
</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"
|
||||
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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="question_matrix" name="Question: matrix">
|
||||
<t t-set="comment_line" t-value="answer_lines.filtered(lambda line: line.value_char_box)"/>
|
||||
<table class="table table-hover o_survey_question_matrix"
|
||||
<table class="table table-hover o_survey_question_matrix text-white text-center mb-0"
|
||||
t-att-data-name="question.id"
|
||||
t-att-data-question-type="question.question_type"
|
||||
t-att-data-sub-questions="question.matrix_row_ids.ids">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th t-foreach="question.suggested_answer_ids" t-as="col_label"><span t-field="col_label.value" /></th>
|
||||
<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>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="question.matrix_row_ids" t-as="row_label">
|
||||
<th><span t-field="row_label.value" /></th>
|
||||
<td t-foreach="question.suggested_answer_ids" t-as="col_label">
|
||||
<t t-set="item_idx" t-value="0"/>
|
||||
<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>
|
||||
<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)"/>
|
||||
<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'
|
||||
t-att-checked="'checked' if answer else None"
|
||||
t-att-data-row-id="row_label.id"/>
|
||||
</td>
|
||||
<td t-att-class="'o_survey_matrix_btn text-primary border-0 %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'
|
||||
t-att-checked="'checked' if answer else None"
|
||||
t-att-data-row-id="row_label.id"
|
||||
t-att-data-selection-key="letters[item_idx] if useKeySelection else ''"
|
||||
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>
|
||||
<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>
|
||||
<t t-set="item_idx" t-value="item_idx + 1"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div t-if='question.comments_allowed'>
|
||||
<span t-field="question.comments_message"/>
|
||||
<textarea type="text" class="form-control" t-att-name="'%s_%s' % (question.id, 'comment')"><t t-esc="comment_line.value_char_box if comment_line else ''"/></textarea>
|
||||
<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"
|
||||
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">
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<template id="survey_progression" name="Survey: Progression">
|
||||
<div class="float-right">
|
||||
<t t-if="len(page_ids) > 1">
|
||||
<t t-set="percentage" t-value="round(100*(page_number/len(page_ids)))"/>
|
||||
<t t-if="survey.progression_mode == 'percent'">
|
||||
<span class="o_survey_progress_percent" t-esc="percentage"/> % completed
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span class="o_survey_progress_number" t-esc="page_number"/> of <span t-esc="len(page_ids)"/>
|
||||
<span t-if="survey.questions_layout == 'page_per_question'">answered</span>
|
||||
<span t-else="">pages</span>
|
||||
</t>
|
||||
<div class="o_survey_progress progress flex-grow-1">
|
||||
<div class="progress-bar bg-primary" t-att-style="'width: ' + str(percentage) + '%'"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</template>
|
||||
</data>
|
||||
|
||||
@@ -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 mt16 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">
|
||||
<div t-ignore="true" class="text-center">
|
||||
<a t-attf-href="/web#view_type=form&model=survey.survey&id=#{survey.id}&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>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<t t-call="survey.layout">
|
||||
<t t-set="survey_form_readonly" t-value="true"/>
|
||||
<div class="wrap">
|
||||
<div class="container">
|
||||
<div class="o_survey_print container">
|
||||
<t t-if="answer.test_entry" t-call="survey.survey_button_form_view" />
|
||||
<div class='jumbotron mt32'>
|
||||
<h1><span t-field='survey.title'/></h1>
|
||||
@@ -27,7 +27,7 @@
|
||||
<h2>
|
||||
<span t-field='question.title'/>
|
||||
<span t-if="question.constr_mandatory" class="text-danger">*</span>
|
||||
<span t-if="quizz_correction" class="badge badge-pill" t-att-data-score-question="question.id"></span>
|
||||
<span t-if="scoring_display_correction" class="badge badge-pill" t-att-data-score-question="question.id"></span>
|
||||
</h2>
|
||||
<t t-if="question.description"><div class="text-muted oe_no_empty" t-field='question.description'/></t>
|
||||
<t t-if="question.question_type == 'text_box'"><t t-call="survey.question_text_box"/></t>
|
||||
@@ -38,7 +38,7 @@
|
||||
<t t-if="question.question_type == 'simple_choice'"><t t-call="survey.question_simple_choice"/></t>
|
||||
<t t-if="question.question_type == 'multiple_choice'"><t t-call="survey.question_multiple_choice"/></t>
|
||||
<t t-if="question.question_type == 'matrix'"><t t-call="survey.question_matrix"/></t>
|
||||
<div class="o_survey_question_error alert alert-danger" role="alert"></div>
|
||||
<div class="o_survey_question_error overflow-hidden border-0 py-0 px-3 alert alert-danger" role="alert"></div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
@@ -89,16 +89,16 @@ var failCertificationSteps = [{
|
||||
trigger: 'button:contains("Start Certification")'
|
||||
}, { // Question: What type of wood is the best for furniture?
|
||||
content: 'Survey: selecting answer "Fir"',
|
||||
trigger: 'div.js_question-wrapper:contains("What type of wood is the best for furniture") label:contains("Fir") input',
|
||||
trigger: 'div.js_question-wrapper:contains("What type of wood is the best for furniture") label:contains("Fir")'
|
||||
}, { // Question: Select all the furniture shown in the video
|
||||
content: 'Survey: ticking answer "Table"',
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the furniture shown in the video") label:contains("Table") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the furniture shown in the video") label:contains("Table")'
|
||||
}, {
|
||||
content: 'Survey: ticking answer "Bed"',
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the furniture shown in the video") label:contains("Bed") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the furniture shown in the video") label:contains("Bed")'
|
||||
}, {
|
||||
content: 'Survey: submitting the certification with wrong answers',
|
||||
trigger: 'button:contains("Submit Survey")'
|
||||
trigger: 'button:contains("Submit")'
|
||||
}];
|
||||
|
||||
var retrySteps = [{
|
||||
@@ -111,19 +111,19 @@ var succeedCertificationSteps = [{
|
||||
trigger: 'button:contains("Start Certification")'
|
||||
}, { // Question: What type of wood is the best for furniture?
|
||||
content: 'Survey: selecting answer "Oak"',
|
||||
trigger: 'div.js_question-wrapper:contains("What type of wood is the best for furniture") label:contains("Oak") input',
|
||||
trigger: 'div.js_question-wrapper:contains("What type of wood is the best for furniture") label:contains("Oak")',
|
||||
}, { // Question: Select all the furniture shown in the video
|
||||
content: 'Survey: ticking answer "Chair"',
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the furniture shown in the video") label:contains("Chair") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the furniture shown in the video") label:contains("Chair")'
|
||||
}, {
|
||||
content: 'Survey: ticking answer "Shelve"',
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the furniture shown in the video") label:contains("Shelve") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the furniture shown in the video") label:contains("Shelve")'
|
||||
}, {
|
||||
content: 'Survey: ticking answer "Desk"',
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the furniture shown in the video") label:contains("Desk") input'
|
||||
trigger: 'div.js_question-wrapper:contains("Select all the furniture shown in the video") label:contains("Desk")'
|
||||
}, {
|
||||
content: 'Survey: submitting the certification with correct answers',
|
||||
trigger: 'button:contains("Submit Survey")'
|
||||
trigger: 'button:contains("Submit")'
|
||||
}];
|
||||
|
||||
var certificationCompletionSteps = [{
|
||||
|
||||
@@ -1365,7 +1365,8 @@ class View(http.Controller):
|
||||
|
||||
class Binary(http.Controller):
|
||||
|
||||
def placeholder(self, image='placeholder.png'):
|
||||
@staticmethod
|
||||
def placeholder(image='placeholder.png'):
|
||||
with tools.file_open(get_resource_path('web', 'static/src/img', image), 'rb') as fd:
|
||||
return fd.read()
|
||||
|
||||
@@ -1442,10 +1443,17 @@ class Binary(http.Controller):
|
||||
filename_field=filename_field, download=download, mimetype=mimetype,
|
||||
default_mimetype='image/png', access_token=access_token)
|
||||
|
||||
return Binary._content_image_get_response(status, headers, image_base64, field=field, download=download,
|
||||
width=width, height=height, crop=crop, quality=quality,
|
||||
placeholder=placeholder)
|
||||
|
||||
@staticmethod
|
||||
def _content_image_get_response(status, headers, image_base64, field='datas', download=None,
|
||||
width=0, height=0, crop=False, quality=0, placeholder='placeholder.png'):
|
||||
if status in [301, 304] or (status != 200 and download):
|
||||
return request.env['ir.http']._response_by_status(status, headers, image_base64)
|
||||
if not image_base64:
|
||||
image_base64 = base64.b64encode(self.placeholder(image=placeholder))
|
||||
image_base64 = base64.b64encode(Binary.placeholder(image=placeholder))
|
||||
if not (width or height):
|
||||
width, height = odoo.tools.image_guess_size_from_field_name(field)
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Frontend general
|
||||
#wrapwrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
> main {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
}
|
||||
@@ -399,6 +399,7 @@
|
||||
</t>
|
||||
|
||||
<t t-call="web._assets_bootstrap"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/web/static/src/scss/base_frontend.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/web/static/src/scss/lazyloader.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/web/static/src/scss/navbar_mobile.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/web/static/src/scss/notification.scss"/>
|
||||
|
||||
Reference in New Issue
Block a user