[IMP] mail: add feedback option to kanban activities

In the kanban view, there was no possibility to add a feedback when
closing an activity this commit adds the same popover as the one we have
in the form view.

This commit also add the templates to this popover with the possible to
directly send the template or open the composer.

Task #1870662
Closes #26280
This commit is contained in:
RomainLibert
2018-08-10 12:08:09 +02:00
parent 3f3593a4fd
commit fd02fec8d8
4 changed files with 191 additions and 5 deletions
+108 -2
View File
@@ -447,6 +447,8 @@ var KanbanActivity = AbstractActivityField.extend({
'show.bs.dropdown': '_onDropdownShow',
'click .o_schedule_activity': '_onScheduleActivity',
'click .o_mark_as_done': '_onMarkActivityDone',
'click .o_activity_template_preview': '_onPreviewMailTemplate',
'click .o_activity_template_send': '_onSendMailTemplate',
},
/**
@@ -460,6 +462,7 @@ var KanbanActivity = AbstractActivityField.extend({
});
this.selection = selection;
this._setState(record);
this._draftFeedback = {};
},
//------------------------------------------------------------
@@ -522,6 +525,19 @@ var KanbanActivity = AbstractActivityField.extend({
},
//------------------------------------------------------------
// Public
//------------------------------------------------------------
/**
* @param {integer} previousActivityTypeID
* @return {$.Promise}
*/
scheduleActivity: function (previousActivityTypeID) {
var callback = this._reload.bind(this, { activity: true, thread: true });
return this._scheduleActivity(false, previousActivityTypeID, callback);
},
//------------------------------------------------------------
// Handlers
//------------------------------------------------------------
@@ -538,8 +554,61 @@ var KanbanActivity = AbstractActivityField.extend({
*/
_onMarkActivityDone: function (ev) {
ev.stopPropagation();
var activityID = $(ev.currentTarget).data('activity-id');
this._sendActivityFeedback(activityID).then(this._reload.bind(this));
var self = this;
var $popoverElement = $(ev.currentTarget);
var activityID = $popoverElement.data('activity-id');
var previousActivityTypeID = $popoverElement.data('previous-activity-type-id');
if (!$popoverElement.data('bs.popover')) {
$popoverElement.popover({
template: $(Popover.Default.template).addClass('o_mail_activity_feedback')[0].outerHTML, // Ugly but cannot find another way
container: $popoverElement.parent(), // FIXME Ugly and should probably be in the body as by default but form view should handle destroying popovers
title : _t("Feedback"),
html: true,
trigger:'click',
content : function () {
var $popover = $(QWeb.render('mail.activity_feedback_form', { 'previous_activity_type_id': previousActivityTypeID }));
$popover.find('#activity_feedback').val(self._draftFeedback[activityID]);
$popover.on('click', function (ev) {
ev.stopPropagation();
});
$popover.on('click', '.o_activity_popover_done', function (ev) {
ev.stopPropagation();
self._markActivityDone({
activityID: activityID,
feedback: _.escape($popover.find('#activity_feedback').val()),
});
});
$popover.on('click', '.o_activity_popover_done_next', function (ev) {
ev.stopPropagation();
self._markActivityDoneAndScheduleNext({
activityID: activityID,
activityTypeID: $popoverElement.data('previous-activity-type-id'),
feedback: _.escape($popover.find('#activity_feedback').val()),
});
});
$popover.on('click', '.o_activity_popover_discard', function (ev) {
ev.stopPropagation();
$popoverElement.popover('hide');
});
return $popover;
},
}).on('shown.bs.popover', function () {
var $popover = $($(this).data("bs.popover").tip);
$(".o_mail_activity_feedback.popover").not($popover).popover("hide");
$popover.addClass('o_mail_activity_feedback').attr('tabindex', 0);
$popover.find('#activity_feedback').focus();
$popover.off('focusout');
$popover.focusout(function (e) {
// outside click of popover hide the popover
// e.relatedTarget is the element receiving the focus
if (!$popover.is(e.relatedTarget) && !$popover.find(e.relatedTarget).length) {
self._draftFeedback[activityID] = $popover.find('#activity_feedback').val();
$popover.popover('hide');
}
});
}).popover('show');
}
},
/**
* @private
@@ -550,6 +619,43 @@ var KanbanActivity = AbstractActivityField.extend({
var activityID = $(ev.currentTarget).data('activity-id') || false;
return this._scheduleActivity(activityID, false, this._reload.bind(this));
},
/**
* @private
* @param {MouseEvent} ev
*/
_onPreviewMailTemplate: function (ev) {
ev.stopPropagation();
var templateID = $(ev.target).data('template-id');
var action = {
name: _t('Compose Email'),
type: 'ir.actions.act_window',
res_model: 'mail.compose.message',
views: [[false, 'form']],
target: 'new',
context: {
default_res_id: this.res_id,
default_model: this.model,
default_use_template: true,
default_template_id: templateID,
force_email: true,
},
};
return this.do_action(action, { on_close: function(){}});
},
/**
* @private
* @param {MouseEvent} ev
*/
_onSendMailTemplate: function (ev) {
ev.stopPropagation();
var templateID = $(ev.target).data('template-id');
return this._rpc({
model: this.model,
method: 'activity_send_mail',
args: [[this.res_id], templateID],
})
.then(this._reload.bind(this, {activity: true, thread: true, followers: true}));
},
});
field_registry
+11 -2
View File
@@ -164,8 +164,18 @@
}
}
.o_activity_template_preview,.o_activity_template_send {
font-weight: bold;
color: $o-brand-primary;
cursor: pointer;
&:hover:not(.active) {
color: darken($o-brand-primary, 15%);
border-color: darken($o-brand-primary, 15%);
}
}
div.o_activity {
width: 290px;
min-width: 290px;
padding: 0px 0px;
> :last-child {
@@ -201,7 +211,6 @@
width: 85%;
padding: 0px 5px 0px 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
@@ -38,6 +38,19 @@
<div>
<span class="fa fa-clock-o fa-fw" role="img" aria-label="Deadline" title="Deadline"/>
<span t-att-title="log.date_deadline"><t t-esc="log.label_delay" /></span>
<div class="mt16" t-att-data-activity-id="log.id" t-att-data-previous-activity-type-id="log.activity_type_id[0]">
<t t-if="log.mail_template_ids">
<t t-foreach="log.mail_template_ids" t-as="mail_template">
<div>
<i class="fa fa-envelope-o" aria-label="Mail" title="Mail" role="img"></i>
<span t-esc="mail_template.name"/>:
<span class="o_activity_template_preview" t-att-data-template-id="mail_template.id">Preview</span>
<span class="text-muted">or</span>
<span class="o_activity_template_send" t-att-data-template-id="mail_template.id">Send Now</span>
</div>
</t>
</t>
</div>
<t t-if="log.user_id[0] != uid">
<span class="ml4 fa fa-user" role="img" aria-label="User" title="User"/>
<span><t t-esc="log.user_id[1]" /></span>
@@ -45,7 +58,7 @@
</div>
</div>
<div class="float-right">
<span class="o_mark_as_done o_activity_link o_activity_link_kanban fa fa-check-circle fa-2x mt4" t-att-data-activity-id="log.id" title="Mark as done" role="img" aria-label="Mark as done"/>
<span class="o_mark_as_done o_activity_link o_activity_link_kanban fa fa-check-circle fa-2x mt4" t-att-data-previous-activity-type-id="log.activity_type_id[0]" t-att-data-activity-id="log.id" title="Mark as done" role="img" aria-label="Mark as done"/>
</div>
</li>
</t>
+58
View File
@@ -409,6 +409,7 @@ QUnit.test('kanban activity widget with an activity', function (assert) {
// mark activity as done
$record.find('.o_mark_as_done').click();
$record.find('.o_activity_popover_done').click();
$record = kanban.$('.o_kanban_record').first(); // the record widget has been reset
assert.strictEqual(rpcCount, 5, 'should have done an RPC to mark activity as done, and a read');
assert.ok($record.find('.o_mail_activity .o_activity_color_default:not(.o_activity_color_today)').length,
@@ -420,6 +421,63 @@ QUnit.test('kanban activity widget with an activity', function (assert) {
kanban.destroy();
});
QUnit.test('kanban activity widget popover test', function (assert) {
assert.expect(3);
this.data.partner.records[0].activity_ids = [1];
this.data.partner.records[0].activity_state = 'today';
this.data['mail.activity'].records = [{
id: 1,
display_name: "An activity",
date_deadline: moment().format("YYYY-MM-DD"), // now
state: "today",
user_id: 2,
create_user_id: 2,
activity_type_id: 1,
}];
var rpcCount = 0;
var kanban = createView({
View: KanbanView,
model: 'partner',
data: this.data,
arch: '<kanban>' +
'<field name="activity_state"/>' +
'<templates><t t-name="kanban-box">' +
'<div><field name="activity_ids" widget="kanban_activity"/></div>' +
'</t></templates>' +
'</kanban>',
mockRPC: function (route, args) {
if (route === '/web/dataset/call_kw/mail.activity/action_feedback') {
rpcCount++;
var current_ids = this.data.partner.records[0].activity_ids;
var done_ids = args.args[0];
this.data.partner.records[0].activity_ids = _.difference(current_ids, done_ids);
this.data.partner.records[0].activity_state = false;
return $.when();
}
return this._super(route, args);
},
});
var $record = kanban.$('.o_kanban_record').first();
$record.find('.o_activity_btn').click();
// Click on button and see popover no RPC call
$record.find('.o_mark_as_done').click();
assert.equal(rpcCount, 0, "");
// Click on discard no RPC call
$record.find('.o_activity_popover_discard').click();
assert.equal(rpcCount, 0, "");
// Click on button and then on done and schedule next
// RPC call
$record.find('.o_activity_popover_done_next').click();
assert.equal(rpcCount, 1, "");
kanban.destroy();
});
QUnit.test('chatter: post, receive and star messages', function (assert) {
var done = assert.async();
assert.expect(27);