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 @@
+ + + + + Avatar
+ + +
+
+
+ +
+
Column
+ + + +
+
+
+
+
+
+ +
+
+
+ Avatar +
+ + +