[IMP] survey: usability onboarding

Various views and usability tweaks:
- datetime picker in surveys
- remove technical field res_model for survey
- various relabelling
- better validation of date constraints
- onchange when email validation
- button to the backend in survey frontends
- icons in questions form views
This commit is contained in:
Ajay Patel
2015-03-09 19:31:14 +01:00
committed by Richard Mathot
parent 02cc08586b
commit de4135305c
10 changed files with 175 additions and 37 deletions
@@ -5,7 +5,6 @@
<record model="survey.survey" id="appraisal_form">
<field name="title">Employee Appraisal Form</field>
<field name="stage_id" ref="survey.stage_permanent" />
<field name="res_model">hr_evaluation</field>
<field name="auth_required" eval="True" />
<field name="users_can_go_back" eval="True" />
<field name="description">&lt;p&gt; &lt;/p&gt;</field>
@@ -320,7 +319,6 @@ over the coming year.&lt;/p&gt;</field>
<record model="survey.survey" id="opinion_form">
<field name="title">Employee Opinion Form</field>
<field name="stage_id" ref="survey.stage_permanent" />
<field name="res_model">hr_evaluation</field>
<field name="auth_required" eval="True" />
<field name="users_can_go_back" eval="True" />
<field name="description">&lt;p&gt; &lt;/p&gt;</field>
@@ -5,7 +5,6 @@
<record model="survey.survey" id="recruitment_form">
<field name="title">Recruitment Form</field>
<field name="stage_id" ref="survey.stage_permanent" />
<field name="res_model">hr_recruitment</field>
<field name="auth_required" eval="True" />
<field name="users_can_go_back" eval="True" />
<field name="description">&lt;p&gt;This form is intended to help the responsible of a recruitment interview.&lt;/p&gt;</field>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -51,3 +51,24 @@
width: 1150px;
}
}
.o_preview_questions {
border: 3px solid #C9C6C6;
width: auto;
padding: 10px 30px 5px;
margin-top: 15px;
color: #C9C6C6;
}
.o_preview_questions .o_datetime {
border: 1px solid #D8D7D7;
margin-bottom: 5px;
}
.o_preview_questions .o_matrix_head {
border-bottom: 1px solid #D8D7D7;
}
.o_preview_questions .o_matrix_row {
border-top: 1px solid #D8D7D7;
}
+13
View File
@@ -35,6 +35,7 @@ openerp.website.if_dom_contains('.js_surveyform', function (the_form) {
// Printing mode: will disable all the controls in the form
if (_.isUndefined(submit_controller)) {
$(".js_surveyform .input-group-addon span.fa-calendar").css("pointer-events", "none");
$('.js_surveyform :input').prop('disabled', true);
print_mode = true;
}
@@ -44,6 +45,18 @@ openerp.website.if_dom_contains('.js_surveyform', function (the_form) {
quiz_correction_mode = true;
}
$("div.input-group span.fa-calendar").on('click', function(e) {
$(e.currentTarget).closest("div.date").datetimepicker({
useSeconds: true,
icons : {
time: 'fa fa-clock-o',
date: 'fa fa-calendar',
up: 'fa fa-chevron-up',
down: 'fa fa-chevron-down'
},
});
});
// Custom code for right behavior of radio buttons with comments box
$('.js_comments>input[type="text"]').focusin(function(){
$(this).prev().find('>input').attr("checked","checked");
+19 -7
View File
@@ -168,7 +168,6 @@ class survey_survey(osv.Model):
_columns = {
'title': fields.char('Title', required=1, translate=True),
'res_model': fields.char('Category'),
'page_ids': fields.one2many('survey.page', 'survey_id', 'Pages', copy=True),
'stage_id': fields.many2one('survey.stage', string="Stage", ondelete="set null", copy=False),
'auth_required': fields.boolean('Login required',
@@ -573,8 +572,8 @@ class survey_question(osv.Model):
oldname='descriptive_text'),
# Answer
'type': fields.selection([('free_text', 'Long Text Zone'),
('textbox', 'Text Input'),
'type': fields.selection([('free_text', 'Multiple Lines Text Box'),
('textbox', 'Single Line Text Box'),
('numerical_box', 'Numerical Value'),
('datetime', 'Date and Time'),
('simple_choice', 'Multiple choice: only one answer'),
@@ -601,7 +600,7 @@ class survey_question(osv.Model):
('2', '6')],
'Number of columns'),
# These options refer to col-xx-[12|6|4|3|2] classes in Bootstrap
'display_mode': fields.selection([('columns', 'Radio Buttons/Checkboxes'),
'display_mode': fields.selection([('columns', 'Radio Buttons'),
('dropdown', 'Selection Box')],
'Display mode'),
@@ -657,6 +656,9 @@ class survey_question(osv.Model):
('validation_date', 'CHECK (validation_min_date <= validation_max_date)', 'Max date cannot be smaller than min date!')
]
def onchange_validation_email(self, cr, uid, ids, validation_email, context=None):
return {'value': {'validation_required': False}} if validation_email else {}
def copy_data(self, cr, uid, ids, default=None, context=None):
current_rec = self.read(cr, uid, ids, context=context)
question = _("%s (copy)") % (current_rec.get('question'))
@@ -746,7 +748,17 @@ class survey_question(osv.Model):
# Answer is not in the right range
try:
dateanswer = datetime.datetime.strptime(answer, DF)
if not (datetime.datetime.strptime(question.validation_min_date, DF) <= dateanswer <= datetime.datetime.strptime(question.validation_max_date, DF)):
min_date = question.validation_min_date and datetime.datetime.strptime(question.validation_min_date, DF) or False
max_date = question.validation_max_date and datetime.datetime.strptime(question.validation_max_date, DF) or False
if (min_date and max_date and not(min_date <= dateanswer <= max_date)):
# If Minimum and Maximum Date are entered
errors.update({answer_tag: question.validation_error_msg})
elif (min_date and not(min_date <= dateanswer)):
# If only Minimum Date is entered and not Define Maximum Date
errors.update({answer_tag: question.validation_error_msg})
elif (max_date and not(dateanswer <= max_date)):
# If only Maximum Date is entered and not Define Minimum Date
errors.update({answer_tag: question.validation_error_msg})
except ValueError: # check that it is a datetime has been done hereunder
pass
@@ -821,7 +833,7 @@ class survey_label(osv.Model):
'sequence': fields.integer('Label Sequence order'),
'value': fields.char("Suggested value", translate=True,
required=True),
'quizz_mark': fields.float('Score for this answer', help="A positive score indicates a correct answer; a negative or null score indicates a wrong answer"),
'quizz_mark': fields.float('Score for this choice', help="A positive score indicates a correct choice; a negative or null score indicates a wrong answer"),
}
_defaults = {
'sequence': 10,
@@ -990,7 +1002,7 @@ class survey_user_input_line(osv.Model):
'value_free_text': fields.text("Free Text answer"),
'value_suggested': fields.many2one('survey.label', "Suggested answer"),
'value_suggested_row': fields.many2one('survey.label', "Row answer"),
'quizz_mark': fields.float("Score given for this answer")
'quizz_mark': fields.float("Score given for this choice")
}
_defaults = {
+5 -3
View File
@@ -14,6 +14,7 @@
<t t-call="website.layout">
<div class="oe_structure" />
<div class="container js_surveyresult">
<t t-call="survey.back" />
<div class="jumbotron mt32">
<h1><span t-field="survey.title" /> <span style="font-size:1.5em;" class="fa fa-bar-chart-o pull-right "/></h1>
<div t-field="survey.description" class="oe_no_empty" />
@@ -98,17 +99,17 @@
<td><a t-att-href="'%s/%s' % (user_input.user_input_id.print_url, user_input.user_input_id.token)"><t t-esc="user_input_index + 1"></t></a></td>
<t t-if="question.type == 'free_text'">
<td>
<span t-field="user_input.value_free_text"></span><br/>
<a t-att-href="'%s/%s' % (user_input.user_input_id.print_url, user_input.user_input_id.token)" t-field="user_input.value_free_text"></a><br/>
</td>
</t>
<t t-if="question.type == 'textbox'">
<td>
<span t-field="user_input.value_text"></span><br/>
<a t-att-href="'%s/%s' % (user_input.user_input_id.print_url, user_input.user_input_id.token)" t-field="user_input.value_text"></a><br/>
</td>
</t>
<t t-if="question.type == 'datetime'">
<td>
<span class="oe_date" t-field="user_input.value_date"></span><br/>
<a t-att-href="'%s/%s' % (user_input.user_input_id.print_url, user_input.user_input_id.token)" class="oe_date" t-field="user_input.value_date"></a><br/>
</td>
</t>
</tr>
@@ -326,6 +327,7 @@
<template id="no_result" name="Survey Result">
<t t-call="website.layout">
<div class="container">
<t t-call="survey.back" />
<h2><span t-field='survey.title'></span></h2>
<div class="jumbotron mt32">
<h2 class="text-muted">
+16 -7
View File
@@ -6,6 +6,7 @@
<t t-call="website.layout">
<div class="wrap">
<div class="container">
<t t-call="survey.back" />
<div class="jumbotron mt32">
<h1>Thank you!</h1>
<div t-field="survey.thank_you_message" class="oe_no_empty" />
@@ -50,6 +51,7 @@
<t t-call="website.layout">
<div class="wrap">
<div class="container">
<t t-call="survey.back" />
<div class="jumbotron mt32">
<h1>Not ready</h1>
<p>This survey has no pages by now!</p>
@@ -59,15 +61,20 @@
</t>
</template>
<!-- Back Button to redirect in form view of survey -->
<template id="back" name="Back">
<div groups="base.group_website_publisher" t-ignore="true" class="pull-right">
<a t-attf-href="/web#view_type=form&amp;model=survey.survey&amp;id=#{survey.id}&amp;action=survey.action_survey_form" class="btn btn-default">Back to Survey</a>
</div>
</template>
<!-- First page of a survey -->
<template id="survey_init" name="Survey">
<t t-call="website.layout">
<div class="wrap">
<div class="oe_structure" />
<div class="container">
<div groups="base.group_website_publisher" t-ignore="true" class="text-right">
<a href="/web#action=survey.action_survey_form" class="btn btn-default">Go back to surveys</a>
</div>
<t t-call="survey.back" />
<div class='jumbotron mt32'>
<h1 t-field='survey.title' />
<div t-field='survey.description' class="oe_no_empty"/>
@@ -92,9 +99,7 @@
<div class="wrap">
<div class="oe_structure"/>
<div class="container">
<div groups="base.group_website_publisher" t-ignore="true" class="text-right">
<a href="/web#action=survey.action_survey_form" class="btn btn-default">Go back to surveys</a>
</div>
<t t-call="survey.back" />
<t t-call="survey.page" />
</div>
<div class="oe_structure"/>
@@ -168,7 +173,10 @@
</template>
<template id="datetime" name="Datetime box">
<input type="text" class="form-control" t-att-name="prefix" placeholder="YYYY-MM-DD HH:mm:ss" />
<div class='input-group date'>
<input type="text" class="form-control" data-date-format="YYYY-MM-DD hh:mm:ss" t-att-name="prefix" placeholder="YYYY-MM-DD hh:mm:ss" />
<span class="input-group-addon"><span class="fa fa-calendar"></span></span>
</div>
</template>
<template id="simple_choice" name="Simple choice">
@@ -264,6 +272,7 @@
<t t-call="website.layout">
<div class="wrap">
<div class="container">
<t t-call="survey.back" />
<div class="row">
<div class='jumbotron mt32'>
<h1><span t-field='survey.title'/></h1>
+97 -13
View File
@@ -113,11 +113,15 @@
<field name="model">survey.survey</field>
<field name="arch" type="xml">
<form string="Survey">
<field name="id" invisible="1"/>
<field name="tot_start_survey" invisible="1"/>
<header groups="base.group_survey_manager">
<button name="action_test_survey" string="Test Survey" type="object"/>
<button name="action_print_survey" string="Print Survey" type="object"/>
<button name="action_send_survey" string="Share and invite by email" type="object" class="oe_highlight" />
<button name="action_result_survey" string="View results" type="object" class="oe_highlight" />
<button name="action_test_survey" string="Design Survey" type="object" class="oe_highlight"/>
<button name="action_test_survey" string="Test Survey" type="object" attrs="{'invisible': [('id', '=', False)]}"/>
<button name="action_print_survey" string="Print Survey" type="object" attrs="{'invisible': [('id', '=', False)]}"/>
<button name="action_send_survey" string="Share and invite by email" type="object" class="oe_highlight" attrs="{'invisible': [('id', '=', False)]}"/>
<button name="action_result_survey" string="View results" type="object" attrs="{'invisible': ['|',('id', '=', False), ('tot_start_survey', '!=', 0)]}"/>
<button name="action_result_survey" string="View results" type="object" class="oe_highlight" attrs="{'invisible': [('tot_start_survey', '=', 0)]}"/>
<field name="stage_id" widget="statusbar" clickable="True" />
</header>
<sheet>
@@ -135,11 +139,10 @@
</tree>
</field>
</page>
<page string="Select Options">
<page string="Options">
<group colspan="4">
<field name="users_can_go_back" string="User can come back in the previous page" />
<field name="auth_required" />
<field name="res_model" groups="base.group_no_one"/>
<field name="quizz_mode" groups="base.group_no_one"/>
</group>
</page>
@@ -180,7 +183,7 @@
<field name="auth_required" />
<field name="public_url" />
<templates>
<div t-name="kanban-box" t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card">
<div t-name="kanban-box" t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click">
<div class="oe_dropdown_toggle oe_dropdown_kanban" t-if="widget.view.is_action_enabled('edit')">
<i class="fa fa-bars fa-lg"/>
<ul class="oe_dropdown_menu">
@@ -288,6 +291,12 @@
</record>
<act_window context="{'search_default_page_id': active_id, 'default_page_id': active_id}" id="act_survey_page_question" name="Questions" res_model="survey.question" src_model="survey.page"/>
<template id="assets_backend" name="survey assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/survey/static/src/css/survey_result.css"/>
</xpath>
</template>
<!-- QUESTIONS -->
<record model="ir.ui.view" id="survey_question_form">
<field name="name">Form view for survey question</field>
@@ -301,18 +310,80 @@
<separator />
</div>
<group>
<field name="type" widget="radio" />
<group>
<field name="type" widget="radio" />
</group>
<group>
<div class="col-md-6 col-md-offset-3 o_preview_questions">
<!-- Multiple Lines Text Zone -->
<div attrs="{'invisible': [('type', '!=', 'free_text')]}">
<i class="fa fa-align-justify fa-4x"/>
</div>
<!-- Single Line Text Zone -->
<div attrs="{'invisible': [('type', '!=', 'textbox')]}">
<i class="fa fa-minus fa-4x"/>
</div>
<!-- Numerical Value -->
<div attrs="{'invisible': [('type', '!=', 'numerical_box')]}">
<i class="fa fa-2x">123..</i>
</div>
<!-- Date and Time -->
<div attrs="{'invisible': [('type', '!=', 'datetime')]}">
<p class="o_datetime">YYYY-MM-DD hh:mm:ss
<i class="fa fa-calendar fa-2x"/>
</p>
</div>
<!-- Multiple choice: only one answer -->
<div attrs="{'invisible': [('type', '!=', 'simple_choice')]}">
<div class="row"><i class="fa fa-circle-o fa-lg"/> answer</div>
<div class="row"><i class="fa fa-dot-circle-o fa-lg"/> answer</div>
<div class="row"><i class="fa fa-circle-o fa-lg"/> answer</div>
</div>
<!-- Multiple choice: multiple answers allowed -->
<div attrs="{'invisible': [('type', '!=', 'multiple_choice')]}">
<div class="row"><i class="fa fa-square-o fa-lg"/> answer</div>
<div class="row"><i class="fa fa-check-square-o fa-lg"/> answer</div>
<div class="row"><i class="fa fa-square-o fa-lg"/> answer</div>
</div>
<!-- Matrix -->
<div attrs="{'invisible': [('type', '!=', 'matrix')]}">
<div class="row o_matrix_head">
<div class="col-md-3"></div>
<div class="col-md-3">ans</div>
<div class="col-md-3">ans</div>
<div class="col-md-3">ans</div>
</div>
<div class="row o_matrix_row">
<div class="col-md-3">Row1</div>
<div class="col-md-3"><i class="fa fa-circle-o fa-lg"/></div>
<div class="col-md-3"><i class="fa fa-dot-circle-o fa-lg"/></div>
<div class="col-md-3"><i class="fa fa-circle-o fa-lg"/></div>
</div>
<div class="row o_matrix_row">
<div class="col-md-3">Row2</div>
<div class="col-md-3"><i class="fa fa-circle-o fa-lg"/></div>
<div class="col-md-3"><i class="fa fa-circle-o fa-lg"/></div>
<div class="col-md-3"><i class="fa fa-dot-circle-o fa-lg"/></div>
</div>
<div class="row o_matrix_row">
<div class="col-md-3">Row3</div>
<div class="col-md-3"><i class="fa fa-dot-circle-o fa-lg"/></div>
<div class="col-md-3"><i class="fa fa-circle-o fa-lg"/></div>
<div class="col-md-3"><i class="fa fa-circle-o fa-lg"/></div>
</div>
</div>
</div>
</group>
</group>
<notebook>
<page string="Answers">
<field name="validation_email" attrs="{'invisible': [('type', '!=', 'textbox')]}"/>
<field name="validation_email" attrs="{'invisible': [('type', '!=', 'textbox')]}" on_change="onchange_validation_email(validation_email)"/>
<label for="validation_email" attrs="{'invisible': [('type', '!=', 'textbox')]}"/>
<separator />
<label for="labels_ids" attrs="{'invisible': [('type', 'not in', ['simple_choice', 'multiple_choice', 'matrix'])]}" />
<field name="labels_ids" string="Type of answers" context="{'default_question_id': active_id}" attrs="{'invisible': [('type', 'not in', ['simple_choice', 'multiple_choice', 'matrix'])]}">
<tree editable="bottom">
<field name="sequence" widget="handle"/>
<field name="value" string="Answers"/>
<field name="value" string="Choices"/>
<field name="quizz_mark" groups="base.group_no_one"/>
</tree>
</field>
@@ -321,7 +392,7 @@
<field name="labels_ids_2" context="{'default_question_id_2': active_id}" attrs="{'invisible': [('type', '!=', 'matrix')]}">
<tree editable="bottom">
<field name="sequence" widget="handle"/>
<field name="value" string="Answers"/>
<field name="value" string="Rows"/>
</tree>
</field>
</page>
@@ -330,7 +401,7 @@
<field name="constr_mandatory" string="Mandatory Answer"/>
<field name="constr_error_msg" attrs="{'invisible': [('constr_mandatory', '=', False)]}"/>
<group colspan="2" nolabel="1" attrs="{'invisible': [('type', 'not in', ['textbox', 'numerical_box', 'datetime'])]}">
<field name="validation_required"/>
<field name="validation_required" attrs="{'invisible': [('validation_email', '=', True), ('type', '=', 'textbox')]}"/>
<group colspan="2" nolabel="1" attrs="{'invisible': [('validation_required', '=', False)]}">
<field name="validation_length_min" attrs="{'invisible': [('type', '!=', 'textbox')]}"/>
<field name="validation_length_max" attrs="{'invisible': [('type', '!=', 'textbox')]}"/>
@@ -529,4 +600,17 @@
<menuitem name="Labels" id="menu_survey_label_form1" action="action_survey_label_form" parent="menu_surveys_configuration" sequence="3"/>
<menuitem name="User Input Lines" id="menu_survey_response_line_form" action="action_survey_user_input_line" parent="menu_surveys_configuration" sequence="4"/>
</data>
<data noupdate="1">
<!-- After installation of the module, open the related menu -->
<record id="action_client_survey_menu" model="ir.actions.client">
<field name="name">Open Survey Menu</field>
<field name="tag">reload</field>
<field name="params" eval="{'menu_id': ref('menu_survey_form')}"/>
</record>
<record id="base.open_menu" model="ir.actions.todo">
<field name="action_id" ref="action_client_survey_menu"/>
<field name="state">open</field>
</record>
</data>
</openerp>
+4 -4
View File
@@ -26,6 +26,10 @@
<link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css"/>
<link rel="stylesheet" href="/web/static/src/font/lato/stylesheet.css" />
<!-- Datetime Picker -->
<link rel="stylesheet" href="/web/static/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css"/>
<script type="text/javascript" src="/web/static/lib/bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js"></script>
</template>
<template id="jqueryui_conflict" name="jquery.ui.conflict">
@@ -59,10 +63,6 @@
<link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css"/>
<script type="text/javascript" src="/web/static/lib/bootstrap/js/bootstrap.js"></script>
<!-- Datetime Picker -->
<link rel="stylesheet" href="/web/static/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css"/>
<script type="text/javascript" src="/web/static/lib/bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js"></script>
<!-- jQuery ui -->
<link rel="stylesheet" href="/web/static/lib/jquery.ui/jquery-ui.css"/>