[IMP] web,project: Kanban onboarding
The goal of this rev. is to improve the user onboarding on the Project app: when the user creates a new project, he is now guided to properly configure the stages of its project. To do so, we added a new mechanism in the Kanban view, allowing to define examples of processes that can be used in the corresponding view. The style of the ColumnQuickCreate widget has also been changed. Part of task #38740
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
odoo.define('project.task_kanban_examples', function (require) {
|
||||
'use strict';
|
||||
|
||||
var core = require('web.core');
|
||||
var kanbanExamplesRegistry = require('web.kanban_examples_registry');
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
var greenBullet = '<span class="o_status o_status_green"></span>';
|
||||
var redBullet = '<span class="o_status o_status_red"></span>';
|
||||
var star = '<a style="color: gold;" class="fa fa-star"/>';
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to escape a text before formatting it.
|
||||
*
|
||||
* First argument is the string to format and the other arguments are the values
|
||||
* to inject into the string.
|
||||
*
|
||||
* @returns {string} the formatted and escaped string
|
||||
*/
|
||||
function escFormat() {
|
||||
arguments[0] = _.escape(arguments[0]);
|
||||
return _.str.sprintf.apply(_.str, arguments);
|
||||
}
|
||||
|
||||
kanbanExamplesRegistry.add('project', [{
|
||||
name: _t('Software Development'),
|
||||
columns: [_t('Backlog'), _t('Specifications'), _t('Development'), _t('Tests'), _t('Delivered')],
|
||||
description: escFormat(_t('Once a task is specified, set it %s in the Specifications ' +
|
||||
'column, so that developers know they can pull it. If you work in sprints, use %s to ' +
|
||||
'mark tasks of the current sprint.'), greenBullet, star),
|
||||
}, {
|
||||
name: _t('Agile'),
|
||||
columns: [_t('Backlog'), _t('Analysis'), _t('Development'), _t('Testing'), _t('Done')],
|
||||
description: escFormat(_t('Waiting for the next stage: use %s and %s bullets.'), greenBullet, redBullet),
|
||||
}, {
|
||||
name: _t('Digital Marketing'),
|
||||
columns: [_t('Ideas'), _t('Researching'), _t('Writing'), _t('Editing'), _t('Done')],
|
||||
description: escFormat(_t('Everyone can propose ideas, and the Editor marks the best ones ' +
|
||||
'as %s. Attach all documents or links to the task directly, to have all information about ' +
|
||||
'a research centralized.'), greenBullet),
|
||||
}, {
|
||||
name: _t('Customer Feedback'),
|
||||
columns: [_t('New'), _t('In development'), _t('Done'), _t('Refused')],
|
||||
description: escFormat(_t('Customers propose feedbacks by email; Odoo creates tasks ' +
|
||||
'automatically, and you can communicate on the task directly. Your managers decide which ' +
|
||||
'feedback is accepted %s and which feedback is moved to the "Refused" column.'), greenBullet),
|
||||
}, {
|
||||
name: _t('Getting Things Done (GTD)'),
|
||||
columns: [_t('Inbox'), _t('Today'), _t('This Week'), _t('This Month'), _t('Long Term')],
|
||||
description: _t('Fill your Inbox easily with the email gateway. Periodically review your ' +
|
||||
'Inbox and schedule tasks by moving them to others columns. Every day, you review the ' +
|
||||
'"This Week" column to move important tasks "Today". Every Monday, you review the "This ' +
|
||||
'Month" column.'),
|
||||
}, {
|
||||
name: _t('Consulting'),
|
||||
columns: [_t('New Projects'), _t('Resources Allocation'), _t('In Progress'), _t('Done')],
|
||||
}, {
|
||||
name: _t('Research Project'),
|
||||
columns: [_t('Brainstorm'), _t('Research'), _t('Draft'), _t('Final Document')],
|
||||
}, {
|
||||
name: _t('Website Redesign'),
|
||||
columns: [_t('Page Ideas'), _t('Copywriting'), _t('Design'), _t('Live')],
|
||||
}, {
|
||||
name: _t('T-shirt Printing'),
|
||||
columns: [_t('New Orders'), _t('Logo Design'), _t('To Print'), _t('Done')],
|
||||
description: escFormat(_t('Communicate with customers on the task using the email gateway. ' +
|
||||
'Attach logo designs to the task, so that information flow from designers to the workers ' +
|
||||
'who print the t-shirt. Organize priorities amongst orders %s using the icon.'), star),
|
||||
}]);
|
||||
|
||||
});
|
||||
@@ -4,6 +4,7 @@
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/project/static/src/css/project.css"/>
|
||||
<script type="text/javascript" src="/project/static/src/js/project.js"></script>
|
||||
<script type="text/javascript" src="/project/static/src/js/project_task_kanban_examples.js"></script>
|
||||
<script type="text/javascript" src="/project/static/src/js/tour.js"></script>
|
||||
<link rel="stylesheet" href="/project/static/src/less/project_dashboard.less"/>
|
||||
</xpath>
|
||||
|
||||
@@ -521,7 +521,7 @@
|
||||
<field name="name">project.task.kanban</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban default_group_by="stage_id" class="o_kanban_small_column o_kanban_project_tasks" on_create="quick_create" quick_create_view="project.quick_create_task_form">
|
||||
<kanban default_group_by="stage_id" class="o_kanban_small_column o_kanban_project_tasks" on_create="quick_create" quick_create_view="project.quick_create_task_form" examples="project">
|
||||
<field name="color"/>
|
||||
<field name="priority"/>
|
||||
<field name="stage_id" options='{"group_by_tooltip": {"description": "Stage Description", "legend_priority": "Use of stars"}}'/>
|
||||
|
||||
@@ -6,34 +6,46 @@ odoo.define('web.kanban_column_quick_create', function (require) {
|
||||
* create kanban columns directly from the Kanban view.
|
||||
*/
|
||||
|
||||
var core = require('web.core');
|
||||
var Dialog = require('web.Dialog');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var _t = core._t;
|
||||
var QWeb = core.qweb;
|
||||
|
||||
var ColumnQuickCreate = Widget.extend({
|
||||
template: 'KanbanView.ColumnQuickCreate',
|
||||
events: {
|
||||
'click': 'toggleFold',
|
||||
'click input': '_onInputClicked',
|
||||
'click .o_quick_create_folded': '_onUnfold',
|
||||
'click .o_kanban_add': '_onAddClicked',
|
||||
'focusout': '_onFocusout',
|
||||
'click .o_kanban_examples': '_onShowExamples',
|
||||
'keydown': '_onKeydown',
|
||||
'keypress input': '_onKeypress',
|
||||
'mousedown .o_kanban_add': '_onMousedown',
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {Object} [options]
|
||||
* @param {Object} [options.examples]
|
||||
*/
|
||||
init: function () {
|
||||
init: function (parent, options) {
|
||||
this._super.apply(this, arguments);
|
||||
this.examples = options.examples;
|
||||
this.folded = true;
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
this.$header = this.$('.o_column_header');
|
||||
this.$quick_create = this.$('.o_kanban_quick_create');
|
||||
this.$quickCreateFolded = this.$('.o_quick_create_folded');
|
||||
this.$quickCreateUnfolded = this.$('.o_quick_create_unfolded');
|
||||
this.$input = this.$('input');
|
||||
|
||||
// destroy the quick create when the user clicks outside
|
||||
core.bus.on('click', this, this._onWindowClicked);
|
||||
|
||||
this._update();
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
@@ -73,6 +85,7 @@ var ColumnQuickCreate = Widget.extend({
|
||||
_add: function () {
|
||||
var value = this.$input.val().trim();
|
||||
if (!value.length) {
|
||||
this._cancel();
|
||||
return;
|
||||
}
|
||||
this.$input.val('');
|
||||
@@ -95,8 +108,8 @@ var ColumnQuickCreate = Widget.extend({
|
||||
* @private
|
||||
*/
|
||||
_update: function () {
|
||||
this.$header.toggle(this.folded);
|
||||
this.$quick_create.toggle(!this.folded);
|
||||
this.$quickCreateFolded.toggle(this.folded);
|
||||
this.$quickCreateUnfolded.toggle(!this.folded);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -111,28 +124,6 @@ var ColumnQuickCreate = Widget.extend({
|
||||
event.stopPropagation();
|
||||
this._add();
|
||||
},
|
||||
/**
|
||||
* Cancels the quick creation when the input loses the focus
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onFocusout: function () {
|
||||
var hasFocus = this.$(':focus').length > 0;
|
||||
if (hasFocus) {
|
||||
return;
|
||||
}
|
||||
this._cancel();
|
||||
},
|
||||
/**
|
||||
* Stops the propagation of the event to prevent the quick create from
|
||||
* toggling when the user clicks in the input
|
||||
*
|
||||
* @private
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
_onInputClicked: function (event) {
|
||||
event.stopPropagation();
|
||||
},
|
||||
/**
|
||||
* Cancels quick creation on escape keydown event
|
||||
*
|
||||
@@ -140,7 +131,7 @@ var ColumnQuickCreate = Widget.extend({
|
||||
* @param {KeyEvent} event
|
||||
*/
|
||||
_onKeydown: function (event) {
|
||||
if (event.keyCode === 27) { // escape
|
||||
if (event.keyCode === $.ui.keyCode.ESCAPE) {
|
||||
this._cancel();
|
||||
}
|
||||
},
|
||||
@@ -151,23 +142,64 @@ var ColumnQuickCreate = Widget.extend({
|
||||
* @param {KeyEvent} event
|
||||
*/
|
||||
_onKeypress: function (event) {
|
||||
if (event.keyCode === 13) { // enter
|
||||
if (event.keyCode === $.ui.keyCode.ENTER) {
|
||||
this._add();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* In all browsers the 'focus/blur' event is triggered before a button's
|
||||
* 'click' (it's actually triggered by mousedown).
|
||||
* The quick create is hidden on blur of its input but still needs to create
|
||||
* the data when the "Add" button is clicked.
|
||||
* This problem is adressed by suppressing the 'focus/blur' event from the
|
||||
* Add button entirely by preventing the browser's default mousedown handler.
|
||||
* Opens a dialog containing examples of Kanban processes
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onShowExamples: function () {
|
||||
var self = this;
|
||||
var dialog = new Dialog(this, {
|
||||
$content: $(QWeb.render('KanbanView.ExamplesDialog', {
|
||||
examples: this.examples,
|
||||
})),
|
||||
buttons: [{
|
||||
classes: 'btn-primary pull-right',
|
||||
close: true,
|
||||
text: _t('Got it'),
|
||||
}],
|
||||
size: "large",
|
||||
title: "Kanban Examples",
|
||||
}).open();
|
||||
dialog.on('closed', this, function () {
|
||||
self.$input.focus();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onUnfold: function () {
|
||||
if (this.folded) {
|
||||
this.toggleFold();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* When a click happens outside the quick create, we want to close it.
|
||||
*
|
||||
* @private
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
_onMousedown: function (event) {
|
||||
event.preventDefault();
|
||||
_onWindowClicked: function (event) {
|
||||
// ignore clicks if the quick create is not in the dom
|
||||
if (!document.contains(this.el)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore clicks in modals
|
||||
if ($(event.target).closest('.modal').length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore clicks if target is inside the quick create
|
||||
if (this.el.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._cancel();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
odoo.define('web.kanban_examples_registry', function (require) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* This file instantiates and exports a registry. The purpose of this registry
|
||||
* is to store static data displayed in a dialog to help the end user to
|
||||
* configure its columns in the grouped Kanban view.
|
||||
*
|
||||
* To activate a link on the ColumnQuickCreate widget on open such a dialog, the
|
||||
* attribute 'examples' on the root arch node must be set to a valid key in this
|
||||
* registry.
|
||||
*
|
||||
* Each value in this registry must be an array of Objects containing the
|
||||
* following keys:
|
||||
* - name (string)
|
||||
* - columns (Array[string])
|
||||
* - description (string, optional) BE CAREFUL [*]
|
||||
*
|
||||
* [*] The description is added with a t-raw so the translated texts must be
|
||||
* properly escaped.
|
||||
*/
|
||||
|
||||
var Registry = require('web.Registry');
|
||||
|
||||
return new Registry();
|
||||
|
||||
});
|
||||
@@ -97,16 +97,15 @@ var KanbanRenderer = BasicRenderer.extend({
|
||||
var templates = findInNode(this.arch, function (n) { return n.tag === 'templates';});
|
||||
transformQwebTemplate(templates, state.fields);
|
||||
this.qweb.add_template(utils.json_node_to_xml(templates));
|
||||
|
||||
this.examples = params.examples;
|
||||
this.recordOptions = _.extend({}, params.record_options, {
|
||||
qweb: this.qweb,
|
||||
viewType: 'kanban',
|
||||
});
|
||||
this.columnOptions = _.extend({}, params.column_options, { qweb: this.qweb });
|
||||
this.columnOptions = _.extend({}, params.column_options);
|
||||
if (this.columnOptions.hasProgressBar) {
|
||||
this.columnOptions.progressBarStates = {};
|
||||
}
|
||||
|
||||
this._setState(state);
|
||||
},
|
||||
/**
|
||||
@@ -291,7 +290,9 @@ var KanbanRenderer = BasicRenderer.extend({
|
||||
|
||||
// Enable column quickcreate
|
||||
if (this.createColumnEnabled) {
|
||||
this.quickCreate = new ColumnQuickCreate(this);
|
||||
this.quickCreate = new ColumnQuickCreate(this, {
|
||||
examples: this.examples,
|
||||
});
|
||||
this.quickCreate.appendTo(fragment).then(function () {
|
||||
// Open it directly if there is no column yet
|
||||
if (!self.state.data.length) {
|
||||
@@ -301,7 +302,6 @@ var KanbanRenderer = BasicRenderer.extend({
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
/**
|
||||
* Renders an ungrouped kanban view in a fragment.
|
||||
|
||||
@@ -7,6 +7,7 @@ var config = require('web.config');
|
||||
var KanbanModel = require('web.KanbanModel');
|
||||
var KanbanRenderer = require('web.KanbanRenderer');
|
||||
var KanbanController = require('web.KanbanController');
|
||||
var kanbanExamplesRegistry = require('web.kanban_examples_registry');
|
||||
var utils = require('web.utils');
|
||||
|
||||
var _lt = core._lt;
|
||||
@@ -70,9 +71,12 @@ var KanbanView = BasicView.extend({
|
||||
deletable: activeActions.delete,
|
||||
read_only_mode: params.readOnlyMode,
|
||||
};
|
||||
var examples = this.arch.attrs.examples;
|
||||
if (examples) {
|
||||
this.rendererParams.examples = kanbanExamplesRegistry.get(examples);
|
||||
}
|
||||
|
||||
this.controllerParams.on_create = this.arch.attrs.on_create;
|
||||
|
||||
this.controllerParams.readOnlyMode = false;
|
||||
this.controllerParams.hasButtons = true;
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
.o_kanban_examples_dialog {
|
||||
.o-flex-display();
|
||||
padding: 0px;
|
||||
background: @odoo-color-silver;
|
||||
|
||||
// sidebar
|
||||
.o_kanban_examples_dialog_nav {
|
||||
.o-flex(0, 0, auto);
|
||||
border-right: 1px solid @gray-lighter-dark;
|
||||
background: white;
|
||||
|
||||
> ul > li {
|
||||
margin: 0;
|
||||
border-bottom: 1px solid @gray-lighter-dark;
|
||||
|
||||
> a {
|
||||
padding: 12px 15px;
|
||||
color: @odoo-main-text-color;
|
||||
border-left: 3px solid transparent;
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
&.active > a {
|
||||
color: @headings-color;
|
||||
border-left-color: @odoo-brand-optional;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// content
|
||||
.o_kanban_examples_dialog_content {
|
||||
.o-flex(1, 1, 100%);
|
||||
|
||||
.o_kanban_examples_description {
|
||||
padding: 16px 16px 0;
|
||||
text-align: justify;
|
||||
}
|
||||
.o_kanban_examples {
|
||||
.o-flex-display();
|
||||
padding: 0 6px;
|
||||
|
||||
.o_kanban_examples_group {
|
||||
.o-flex(1, 1, 100%);
|
||||
margin: 10px;
|
||||
|
||||
.o_kanban_examples_ghost {
|
||||
padding: 5px;
|
||||
margin-top: -1px;
|
||||
border: thin solid @gray-lighter-dark;
|
||||
background-color: white;
|
||||
.o_ghost_content {
|
||||
height: 12px;
|
||||
background: @gray-lighter-dark;
|
||||
&.o_ghost_tag {
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
.o_ghost_avatar {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -547,45 +547,58 @@
|
||||
|
||||
// Kanban Grouped Layout - "Create new column" column
|
||||
.o_column_quick_create {
|
||||
.o-kanban-slim-col();
|
||||
|
||||
.o_column_header {
|
||||
.o-kanban-header-title();
|
||||
position: relative;
|
||||
opacity: 0.5;
|
||||
|
||||
.o_kanban_title {
|
||||
.o-kanban-v-title();
|
||||
.o_quick_create_folded {
|
||||
cursor: pointer;
|
||||
padding: 12px 16px;
|
||||
white-space: nowrap;
|
||||
color: @text-muted;
|
||||
opacity: 0.6;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.o_kanban_add_column {
|
||||
.o-kanban-icon(1);
|
||||
display: inline-block;
|
||||
padding: 10px 14px;
|
||||
background-color: @odoo-color-silver-dark;
|
||||
}
|
||||
.o_kanban_title {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.o_kanban_quick_create {
|
||||
display: none;
|
||||
width: @o-kanban-default-record-width;
|
||||
.o_quick_create_unfolded {
|
||||
padding: @o-kanban-inside-vgutter @o-kanban-inside-hgutter;
|
||||
width: @o-kanban-small-record-width;
|
||||
height: 100%;
|
||||
margin: 0 (-@o-kanban-group-padding/2);
|
||||
border: none;
|
||||
box-shadow: 0 0 20px -10px;
|
||||
background-color: @gray-lighter;
|
||||
|
||||
input, input:focus, input:hover {
|
||||
font-size: 16px;
|
||||
background: @gray-lighter;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.o_kanban_header {
|
||||
height: 50px;
|
||||
|
||||
&:hover {
|
||||
.o_column_header, .o_kanban_add_column {
|
||||
opacity: 1;
|
||||
.o_kanban_examples {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input, input:focus, input:hover {
|
||||
margin-top: 3px;
|
||||
font-size: 16px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.o_kanban_add {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.o_kanban_muted_record {
|
||||
background: @gray-lighter-dark;
|
||||
height: 70px;
|
||||
margin: 10px 0px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,17 +39,25 @@
|
||||
|
||||
<t t-name="KanbanView.ColumnQuickCreate">
|
||||
<div class="o_column_quick_create">
|
||||
<div class="o_column_header">
|
||||
<div class="o_quick_create_folded">
|
||||
<span class="o_kanban_add_column"><i class="fa fa-plus"/></span>
|
||||
<span class="o_kanban_title">Add new Column</span>
|
||||
<span class="o_kanban_title">Add a new Column</span>
|
||||
</div>
|
||||
<div class="o_kanban_quick_create">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control o_input" placeholder="Column title"/>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary o_kanban_add" type="button">Add</button>
|
||||
</span>
|
||||
<div class="o_quick_create_unfolded">
|
||||
<div class="o_kanban_header">
|
||||
<div class="input-group">
|
||||
<input type="text" class="o_input" placeholder="Column title"/>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-sm btn-primary o_kanban_add" type="button">Add</button>
|
||||
</span>
|
||||
</div>
|
||||
<t t-if="widget.examples">
|
||||
<span class="btn-link o_kanban_examples">See Examples</span>
|
||||
</t>
|
||||
</div>
|
||||
<div class="o_kanban_muted_record"/>
|
||||
<div class="o_kanban_muted_record"/>
|
||||
<div class="o_kanban_muted_record"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
@@ -97,8 +105,48 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Mobile templates -->
|
||||
<!-- Note: this dialog isn't responsive, but it is not accessible on mobile -->
|
||||
<div t-name="KanbanView.ExamplesDialog" class="o_kanban_examples_dialog">
|
||||
<div class="o_kanban_examples_dialog_nav">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<t t-foreach="examples" t-as="example">
|
||||
<li t-att-class="example_index == 0 and 'active' or None">
|
||||
<a data-toggle="tab" t-attf-href="#example_#{example_index}"> <t t-esc="example.name"/> </a>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="o_kanban_examples_dialog_content">
|
||||
<div class="tab-content">
|
||||
<t t-foreach="examples" t-as="example">
|
||||
<div t-attf-id="example_#{example_index}" t-attf-class="tab-pane fade #{example_index == 0 and 'active in' or ''}">
|
||||
<div t-if="example.description" class="o_kanban_examples_description">
|
||||
<t t-raw="example.description"/> <!-- description is supposed to be properly escaped -->
|
||||
</div>
|
||||
<div class="o_kanban_examples">
|
||||
<t t-foreach="example.columns" t-as="column">
|
||||
<div class="o_kanban_examples_group">
|
||||
<h6><b><t t-esc="column"/></b></h6>
|
||||
<t t-foreach="_.random(1,4)">
|
||||
<t t-call="KanbanView.ExamplesDialog.RecordGhost"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<t t-name="KanbanView.ExamplesDialog.RecordGhost">
|
||||
<div class="o_kanban_examples_ghost">
|
||||
<div class="o_ghost_content"/>
|
||||
<div class="o_ghost_content o_ghost_tag"/>
|
||||
<img class="pull-right o_ghost_avatar" src="/base/static/img/avatar.png"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Mobile templates -->
|
||||
<t t-name="KanbanView.MobileTabs">
|
||||
<div class="o_kanban_mobile_tabs">
|
||||
<t t-foreach="data" t-as="group">
|
||||
|
||||
@@ -2,6 +2,7 @@ odoo.define('web.kanban_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
var KanbanColumnProgressBar = require('web.KanbanColumnProgressBar');
|
||||
var kanbanExamplesRegistry = require('web.kanban_examples_registry');
|
||||
var KanbanRenderer = require('web.KanbanRenderer');
|
||||
var KanbanView = require('web.KanbanView');
|
||||
var testUtils = require('web.test_utils');
|
||||
@@ -1636,17 +1637,20 @@ QUnit.module('Views', {
|
||||
assert.notOk(kanban.$('.o_column_quick_create input').is(':visible'),
|
||||
"the input should not be visible");
|
||||
|
||||
kanban.$('.o_column_quick_create').click();
|
||||
kanban.$('.o_quick_create_folded').click();
|
||||
|
||||
assert.ok(kanban.$('.o_column_quick_create input').is(':visible'),
|
||||
"the input should be visible");
|
||||
|
||||
// discard the column creation and click it again
|
||||
kanban.$('.o_column_quick_create').click();
|
||||
kanban.$('.o_column_quick_create input').trigger($.Event('keydown', {
|
||||
keyCode: $.ui.keyCode.ESCAPE,
|
||||
which: $.ui.keyCode.ESCAPE,
|
||||
}));
|
||||
assert.notOk(kanban.$('.o_column_quick_create input').is(':visible'),
|
||||
"the input should not be visible after discard");
|
||||
|
||||
kanban.$('.o_column_quick_create').click();
|
||||
kanban.$('.o_quick_create_folded').click();
|
||||
assert.ok(kanban.$('.o_column_quick_create input').is(':visible'),
|
||||
"the input should be visible");
|
||||
|
||||
@@ -1915,14 +1919,14 @@ QUnit.module('Views', {
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('cancel column quick create by pressing ESCAPE', function (assert) {
|
||||
assert.expect(4);
|
||||
QUnit.test('quick create several columns in a row', function (assert) {
|
||||
assert.expect(10);
|
||||
|
||||
var kanban = createView({
|
||||
View: KanbanView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<kanban class="o_kanban_test">' +
|
||||
arch: '<kanban>' +
|
||||
'<field name="product_id"/>' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div><field name="foo"/></div>' +
|
||||
@@ -1931,23 +1935,100 @@ QUnit.module('Views', {
|
||||
groupBy: ['product_id'],
|
||||
});
|
||||
|
||||
assert.strictEqual(kanban.$('.o_column_quick_create').length, 1, "should have a quick create column");
|
||||
assert.notOk(kanban.$('.o_column_quick_create input').is(':visible'),
|
||||
"the input should not be visible");
|
||||
assert.strictEqual(kanban.$('.o_kanban_group').length, 2,
|
||||
"should have two columns");
|
||||
assert.strictEqual(kanban.$('.o_column_quick_create').length, 1,
|
||||
"should have a ColumnQuickCreate widget");
|
||||
assert.strictEqual(kanban.$('.o_column_quick_create .o_quick_create_folded:visible').length, 1,
|
||||
"the ColumnQuickCreate should be folded");
|
||||
assert.strictEqual(kanban.$('.o_column_quick_create .o_quick_create_unfolded:visible').length, 0,
|
||||
"the ColumnQuickCreate should be folded");
|
||||
|
||||
kanban.$('.o_column_quick_create').click();
|
||||
// add a new column
|
||||
kanban.$('.o_column_quick_create .o_quick_create_folded').click();
|
||||
assert.strictEqual(kanban.$('.o_column_quick_create .o_quick_create_folded:visible').length, 0,
|
||||
"the ColumnQuickCreate should be unfolded");
|
||||
assert.strictEqual(kanban.$('.o_column_quick_create .o_quick_create_unfolded:visible').length, 1,
|
||||
"the ColumnQuickCreate should be unfolded");
|
||||
kanban.$('.o_column_quick_create input').val('New Column 1');
|
||||
kanban.$('.o_column_quick_create .btn-primary').click();
|
||||
assert.strictEqual(kanban.$('.o_kanban_group').length, 3,
|
||||
"should now have three columns");
|
||||
|
||||
assert.ok(kanban.$('.o_column_quick_create input').is(':visible'),
|
||||
"the input should be visible");
|
||||
// add another column
|
||||
assert.strictEqual(kanban.$('.o_column_quick_create .o_quick_create_folded:visible').length, 0,
|
||||
"the ColumnQuickCreate should still be unfolded");
|
||||
assert.strictEqual(kanban.$('.o_column_quick_create .o_quick_create_unfolded:visible').length, 1,
|
||||
"the ColumnQuickCreate should still be unfolded");
|
||||
kanban.$('.o_column_quick_create input').val('New Column 2');
|
||||
kanban.$('.o_column_quick_create .btn-primary').click();
|
||||
assert.strictEqual(kanban.$('.o_kanban_group').length, 4,
|
||||
"should now have four columns");
|
||||
|
||||
// discard the column creation by pressing ESCAPE
|
||||
kanban.$('.o_column_quick_create input').trigger($.Event('keydown', {
|
||||
keyCode: $.ui.keyCode.ESCAPE,
|
||||
which: $.ui.keyCode.ESCAPE,
|
||||
}));
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
assert.notOk(kanban.$('.o_column_quick_create input').is(':visible'),
|
||||
"the input should not be visible anymore");
|
||||
QUnit.test('quick create column and examples', function (assert) {
|
||||
assert.expect(12);
|
||||
|
||||
kanbanExamplesRegistry.add('test', [{
|
||||
name: "A first example",
|
||||
columns: ["Column 1", "Column 2", "Column 3"],
|
||||
description: "Some description",
|
||||
}, {
|
||||
name: "A second example",
|
||||
columns: ["Col 1", "Col 2"],
|
||||
}]);
|
||||
|
||||
var kanban = createView({
|
||||
View: KanbanView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<kanban examples="test">' +
|
||||
'<field name="product_id"/>' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div><field name="foo"/></div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
groupBy: ['product_id'],
|
||||
});
|
||||
|
||||
assert.strictEqual(kanban.$('.o_column_quick_create').length, 1,
|
||||
"should have a ColumnQuickCreate widget");
|
||||
|
||||
// open the quick create
|
||||
kanban.$('.o_column_quick_create .o_quick_create_folded').click();
|
||||
|
||||
assert.strictEqual(kanban.$('.o_column_quick_create .o_kanban_examples:visible').length, 1,
|
||||
"should have a link to see examples");
|
||||
|
||||
// click to see the examples
|
||||
kanban.$('.o_column_quick_create .o_kanban_examples').click();
|
||||
|
||||
assert.strictEqual($('.modal .o_kanban_examples_dialog').length, 1,
|
||||
"should have open the examples dialog");
|
||||
assert.strictEqual($('.modal .o_kanban_examples_dialog_nav li').length, 2,
|
||||
"should have two examples (in the menu)");
|
||||
assert.strictEqual($('.modal .o_kanban_examples_dialog_nav a').text(),
|
||||
' A first example A second example ', "example names should be correct");
|
||||
assert.strictEqual($('.modal .o_kanban_examples_dialog_content .tab-pane').length, 2,
|
||||
"should have two examples");
|
||||
|
||||
var $firstPane = $('.modal .o_kanban_examples_dialog_content .tab-pane:first');
|
||||
assert.strictEqual($firstPane.find('.o_kanban_examples_group').length, 3,
|
||||
"there should be 3 stages in the first example");
|
||||
assert.strictEqual($firstPane.find('h6').text(), 'Column 1Column 2Column 3',
|
||||
"column titles should be correct");
|
||||
assert.strictEqual($firstPane.find('.o_kanban_examples_description').text().trim(),
|
||||
"Some description", "the correct description should be displayed");
|
||||
|
||||
var $secondPane = $('.modal .o_kanban_examples_dialog_content .tab-pane:nth(1)');
|
||||
assert.strictEqual($secondPane.find('.o_kanban_examples_group').length, 2,
|
||||
"there should be 2 stages in the second example");
|
||||
assert.strictEqual($secondPane.find('h6').text(), 'Col 1Col 2',
|
||||
"column titles should be correct");
|
||||
assert.strictEqual($secondPane.find('.o_kanban_examples_description').text().trim(),
|
||||
"", "there should be no description for the second example");
|
||||
|
||||
kanban.destroy();
|
||||
});
|
||||
@@ -2762,35 +2843,6 @@ QUnit.module('Views', {
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('don\'t fold column quick create after creation', function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var kanban = createView({
|
||||
View: KanbanView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<kanban on_create="quick_create">' +
|
||||
'<field name="product_id"/>' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div><field name="foo"/></div>' +
|
||||
'</t></templates>' +
|
||||
'</kanban>',
|
||||
groupBy: ['product_id'],
|
||||
});
|
||||
|
||||
// add a new column
|
||||
kanban.$('.o_kanban_group:first .o_kanban_quick_add').click();
|
||||
var $quickCreate = kanban.$('.o_kanban_quick_create');
|
||||
$quickCreate.find('input').val('new partner').trigger('input');
|
||||
$quickCreate.find('button.o_kanban_add').click();
|
||||
assert.strictEqual(this.data.partner.records.length, 5,
|
||||
"should have created a 'new partner' column");
|
||||
|
||||
assert.strictEqual(kanban.$('.o_kanban_add:visible').length, 1,
|
||||
"the add button should still be visible");
|
||||
kanban.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('basic support for widgets', function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
|
||||
@@ -122,6 +122,7 @@
|
||||
<link rel="stylesheet" type="text/less" href="/web/static/src/less/list_view.less"/>
|
||||
<link rel="stylesheet" type="text/less" href="/web/static/src/less/list_view_extra.less"/>
|
||||
<link rel="stylesheet" type="text/less" href="/web/static/src/less/kanban_dashboard.less"/>
|
||||
<link rel="stylesheet" type="text/less" href="/web/static/src/less/kanban_examples_dialog.less"/>
|
||||
<link rel="stylesheet" type="text/less" href="/web/static/src/less/kanban_column_progressbar.less"/>
|
||||
<link rel="stylesheet" type="text/less" href="/web/static/src/less/kanban_view.less"/>
|
||||
<link rel="stylesheet" type="text/less" href="/web/static/src/less/kanban_view_mobile.less"/>
|
||||
@@ -217,6 +218,7 @@
|
||||
<script type="text/javascript" src="/web/static/src/js/views/kanban/kanban_column_quick_create.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/views/kanban/kanban_model.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/views/kanban/kanban_controller.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/views/kanban/kanban_examples_registry.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/views/kanban/kanban_record.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/views/kanban/kanban_record_quick_create.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/views/kanban/kanban_renderer.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user