diff --git a/addons/project/models/project.py b/addons/project/models/project.py
index 8bf82cc5328..d8f697261e1 100644
--- a/addons/project/models/project.py
+++ b/addons/project/models/project.py
@@ -203,17 +203,17 @@ class Project(models.Model):
help="Internal email associated with this project. Incoming emails are automatically synchronized "
"with Tasks (or optionally Issues if the Issue Tracker module is installed).")
privacy_visibility = fields.Selection([
- ('followers', 'On invitation only'),
- ('employees', 'Visible by all employees'),
- ('portal', 'Visible by following customers'),
+ ('followers', 'Visible by invited employees'),
+ ('employees', 'Visible by employees'),
+ ('portal', 'Visible by portal users and employees'),
],
string='Privacy', required=True,
default='portal',
help="Defines the visibility of the tasks of the project:\n"
- "- On invitation only: employees may only see the followed project and tasks.\n"
- "- Visible by all employees: employees may see all project and tasks.\n"
- "- Visible by following customers: employees may see everything."
- " if website is activated, portal users may see project and tasks followed by.\n"
+ "- Visible by invited employees: employees may only see the followed project and tasks.\n"
+ "- Visible by employees: employees may see all project and tasks.\n"
+ "- Visible by portal users and employees: employees may see everything."
+ " Portal users may see project and tasks followed by.\n"
" them or by someone of their company.")
doc_count = fields.Integer(compute='_compute_attached_docs_count', string="Number of documents attached")
date_start = fields.Date(string='Start Date')
@@ -428,9 +428,9 @@ class Task(models.Model):
name = fields.Char(string='Title', tracking=True, required=True, index=True)
description = fields.Html(string='Description')
priority = fields.Selection([
- ('0', 'Low'),
- ('1', 'Normal'),
- ], default='0', index=True, string="Priority")
+ ('0', 'Normal'),
+ ('1', 'Important'),
+ ], default='0', index=True, string="Priority")
sequence = fields.Integer(string='Sequence', index=True, default=10,
help="Gives the sequence order when displaying a list of tasks.")
stage_id = fields.Many2one('project.task.type', string='Stage', ondelete='restrict', tracking=True, index=True,
diff --git a/addons/project/static/src/js/project_task_kanban_examples.js b/addons/project/static/src/js/project_task_kanban_examples.js
index a9d24ce2994..fb65b30445d 100644
--- a/addons/project/static/src/js/project_task_kanban_examples.js
+++ b/addons/project/static/src/js/project_task_kanban_examples.js
@@ -27,25 +27,29 @@ function escFormat() {
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),
+ description: escFormat(_t('Prioritize Tasks by using the %s icon.'+
+ '%s Use the %s button to signalize to your colleagues that a task is ready for the next stage.'+
+ '%s Use the %s to signalize a problem or a need for discussion on a task.'), star, ' ', greenBullet, ' ', redBullet),
+ bullets: [greenBullet, redBullet, 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),
+ 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),
+ bullets: [greenBullet, redBullet],
}, {
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),
+ bullets: [greenBullet, redBullet],
}, {
name: _t('Getting Things Done (GTD)'),
columns: [_t('Inbox'), _t('Today'), _t('This Week'), _t('This Month'), _t('Long Term')],
@@ -56,18 +60,24 @@ kanbanExamplesRegistry.add('project', [{
}, {
name: _t('Consulting'),
columns: [_t('New Projects'), _t('Resources Allocation'), _t('In Progress'), _t('Done')],
+ description: escFormat(_t('Manage the lifecycle of your project using the kanban view. Add newly acquired project, assign them and use the %s and %s to define if the project is ready for the next step.'), greenBullet, redBullet),
+ bullets: [greenBullet, redBullet],
}, {
name: _t('Research Project'),
columns: [_t('Brainstorm'), _t('Research'), _t('Draft'), _t('Final Document')],
+ description: escFormat(_t('Handle your idea gahtering within Tasks of your new Project and discuss them in the chatter of the tasks. Use the %s and %s to signalize what is the current status of your Idea'), greenBullet, redBullet),
+ bullets: [greenBullet, redBullet],
}, {
name: _t('Website Redesign'),
columns: [_t('Page Ideas'), _t('Copywriting'), _t('Design'), _t('Live')],
+ description: escFormat(_t('Handle your idea gahtering within Tasks of your new Project and discuss them in the chatter of the tasks. Use the %s and %s to signalize what is the current status of your Idea'), greenBullet, redBullet),
}, {
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),
+ bullets: [star],
}]);
});
diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml
index fb4ace2f093..73d2de26495 100644
--- a/addons/project/views/project_views.xml
+++ b/addons/project/views/project_views.xml
@@ -444,7 +444,7 @@
-
+
@@ -467,7 +467,7 @@
-
+
diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js
index 2383a0ac730..15c804f5a8e 100644
--- a/addons/web/static/src/js/views/kanban/kanban_controller.js
+++ b/addons/web/static/src/js/views/kanban/kanban_controller.js
@@ -33,6 +33,9 @@ var KanbanController = BasicController.extend({
column_toggle_fold: '_onToggleColumn',
kanban_column_records_toggle_active: '_onToggleActiveRecords',
}),
+ events: _.extend({}, BasicController.prototype.events, {
+ click: '_onClick',
+ }),
/**
* @override
* @param {Object} params
@@ -302,6 +305,23 @@ var KanbanController = BasicController.extend({
this.$('.o_kanban_record:first').focus();
}
},
+ /**
+ * Bounce the 'Create' button.
+ *
+ * @private
+ * @param {MouseEvent} ev
+ **/
+ _onClick: function (ev) {
+ var state = this.model.get(this.handle, {raw: true});
+ if (!state.count && this.buttons) {
+ var classesList = ['o_kanban_view', 'o_kanban_group', 'o_column_quick_create', 'o_view_nocontent_smiling_face'];
+ var $target = $(ev.target);
+ var hasClassList = _.map(classesList, function(klass){ return $target.hasClass(klass) });
+ if (_.some(hasClassList)) {
+ this.$buttons.find('.o-kanban-button-new').odooBounce();
+ }
+ }
+ },
/**
* @private
* @param {OdooEvent} ev
@@ -321,12 +341,7 @@ var KanbanController = BasicController.extend({
this.model
.deleteRecords([column.db_id], relatedModelName)
.done(function () {
- if (column.isEmpty()) {
- self.renderer.removeWidget(column);
- self._updateButtons();
- } else {
- self.reload();
- }
+ self.update({}, {reload: !column.isEmpty()});
});
},
/**
diff --git a/addons/web/static/src/js/views/kanban/kanban_renderer.js b/addons/web/static/src/js/views/kanban/kanban_renderer.js
index 316fb9114cd..51d75a167c7 100644
--- a/addons/web/static/src/js/views/kanban/kanban_renderer.js
+++ b/addons/web/static/src/js/views/kanban/kanban_renderer.js
@@ -166,15 +166,6 @@ var KanbanRenderer = BasicRenderer.extend({
this.quickCreate.toggleFold();
this._toggleNoContentHelper();
},
- /**
- * Removes a widget (record if ungrouped, column if grouped) from the view.
- *
- * @param {Widget} widget the instance of the widget to remove
- */
- removeWidget: function (widget) {
- this.widgets.splice(this.widgets.indexOf(widget), 1);
- widget.destroy();
- },
/**
* Updates a given column with its new state.
*
@@ -275,6 +266,16 @@ var KanbanRenderer = BasicRenderer.extend({
previousCard.focus();
}
},
+ /**
+ * Render the Example Ghost Kanban card on the background
+ *
+ * @private
+ * @param {DocumentFragment} fragment
+ */
+ _renderExampleBackground: function (fragment) {
+ var $background = $(qweb.render('KanbanView.ExamplesBackground'));
+ $background.appendTo(fragment);
+ },
/**
* Renders empty invisible divs in a document fragment.
*
@@ -351,9 +352,9 @@ var KanbanRenderer = BasicRenderer.extend({
// Open it directly if there is no column yet
if (!self.state.data.length) {
self.quickCreate.toggleFold();
+ self._renderExampleBackground(fragment);
}
});
-
}
}
},
@@ -546,6 +547,7 @@ var KanbanRenderer = BasicRenderer.extend({
_onQuickCreateColumnUpdated: function (ev) {
ev.stopPropagation();
this._toggleNoContentHelper();
+ this._updateExampleBackground();
},
/**
* @private
@@ -596,6 +598,21 @@ var KanbanRenderer = BasicRenderer.extend({
_onStartQuickCreate: function () {
this._toggleNoContentHelper(true);
},
+ /**
+ * Hide or display the background example:
+ * - displayed when quick create column is display and there is no column else
+ * - hidden otherwise
+ *
+ * @private
+ **/
+ _updateExampleBackground: function () {
+ var $elem = this.$('.o_kanban_example_background_container');
+ if (!this.state.data.length && !$elem.length) {
+ this._renderExampleBackground(this.$el);
+ } else {
+ $elem.remove();
+ }
+ },
});
return KanbanRenderer;
diff --git a/addons/web/static/src/scss/kanban_examples_dialog.scss b/addons/web/static/src/scss/kanban_examples_dialog.scss
index 65eadc9e0b4..2251896e3a0 100644
--- a/addons/web/static/src/scss/kanban_examples_dialog.scss
+++ b/addons/web/static/src/scss/kanban_examples_dialog.scss
@@ -1,3 +1,4 @@
+// container ghost card in modal
.o_kanban_examples_dialog {
display: flex;
padding: 0px;
@@ -37,35 +38,59 @@
padding: 16px 16px 0;
text-align: justify;
}
+ }
+}
+
+// container ghost card in background
+.o_kanban_example_background_container {
+ display: flex;
+ padding: 0px;
+ opacity: 0.4;
+
+ // content
+ .o_kanban_example_background {
+ flex: 1 1 100%;
+
.o_kanban_examples {
- display: flex;
- padding: 0 6px;
-
.o_kanban_examples_group {
- flex: 1 1 100%;
- margin: 10px;
-
.o_kanban_examples_ghost {
- padding: 5px;
- margin-top: -1px;
- border: thin solid gray('300');
- background-color: white;
- .o_ghost_content {
- height: 12px;
- background: gray('300');
- &.o_ghost_tag {
- display: inline-block;
- width: 40%;
- margin-top: 10px;
- }
- }
- .o_ghost_avatar {
- height: 20px;
- width: 20px;
- margin-top: 6px;
- }
+ width: 224px;
+ margin-bottom: 10px;
}
}
}
}
}
+
+
+// kanban ghost card
+.o_kanban_examples {
+ display: flex;
+ padding: 0 6px;
+
+ .o_kanban_examples_group {
+ flex: 1 1 100%;
+ margin: 10px;
+
+ .o_kanban_examples_ghost {
+ padding: 5px;
+ margin-top: -1px;
+ border: thin solid gray('300');
+ background-color: white;
+ .o_ghost_content {
+ height: 12px;
+ background: gray('300');
+ &.o_ghost_tag {
+ display: inline-block;
+ width: 40%;
+ margin-top: 10px;
+ }
+ }
+ .o_ghost_avatar {
+ height: 20px;
+ width: 20px;
+ margin-top: 6px;
+ }
+ }
+ }
+}
diff --git a/addons/web/static/src/xml/kanban.xml b/addons/web/static/src/xml/kanban.xml
index 220395fef02..06ba6b71eb6 100644
--- a/addons/web/static/src/xml/kanban.xml
+++ b/addons/web/static/src/xml/kanban.xml
@@ -56,10 +56,10 @@
-
-
- Esc to discard
+
+
+
@@ -158,10 +158,41 @@