diff options
| author | sri-odoo <sri@odoo.com> | 2018-09-14 11:42:07 +0200 |
|---|---|---|
| committer | XavierDo <xdo@odoo.com> | 2018-09-18 13:44:00 +0200 |
| commit | ed2773e381585ee3fd0c6b90e6f97f8f1237e6f4 (patch) | |
| tree | a536d6d14787abf00319c4c95ae8ad9cde917781 /addons/mail | |
| parent | 47496b9fe5555ef23932fcfc3f2d6d6ee945daae (diff) | |
[REF] mail: activities list, ‘mark as done’ sliding panel
Refactor ‘mark as done’ popup JS in order to use a sliding panel layout instead.
Sliding panels are currently needed in kanban views, where activities are listed in a dropdown that can't handle popups because of CSS overflow and lack of space.
Since this new system check for default bootstrap data-toggle attributes, it could be easily used elsewhere changing the buttons's data-toggle and providing the necessary container for the “mark as done” form.
Diffstat (limited to 'addons/mail')
| -rw-r--r-- | addons/mail/static/src/js/activity.js | 101 | ||||
| -rw-r--r-- | addons/mail/static/src/scss/mail_activity.scss | 77 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/web_kanban_activity.xml | 77 | ||||
| -rw-r--r-- | addons/mail/static/tests/activity_tests.js | 8 | ||||
| -rw-r--r-- | addons/mail/static/tests/chatter_tests.js | 7 |
5 files changed, 155 insertions, 115 deletions
diff --git a/addons/mail/static/src/js/activity.js b/addons/mail/static/src/js/activity.js index cabcd469b5c..65a4f0a6f2e 100644 --- a/addons/mail/static/src/js/activity.js +++ b/addons/mail/static/src/js/activity.js @@ -271,39 +271,55 @@ var BasicActivity = AbstractField.extend({ ev.stopPropagation(); ev.preventDefault(); var self = this; - var $popoverElement = $(ev.currentTarget); - var activityID = $popoverElement.data('activity-id'); - var previousActivityTypeID = $popoverElement.data('previous-activity-type-id'); - var forceNextActivity = $popoverElement.data('force-next-activity'); - if (!$popoverElement.data('bs.popover')) { - $popoverElement.popover({ + var $markDoneBtn = $(ev.currentTarget); + var activityID = $markDoneBtn.data('activity-id'); + var previousActivityTypeID = $markDoneBtn.data('previous-activity-type-id'); + var forceNextActivity = $markDoneBtn.data('force-next-activity'); + + if ($markDoneBtn.data('toggle') == 'collapse') { + var $actLi = $markDoneBtn.parents('.o_log_activity'); + var $panel = self.$('#o_activity_form_' + activityID); + + if (!$panel.data('bs.collapse')) { + var $form = $(QWeb.render('mail.activity_feedback_form', { previous_activity_type_id: previousActivityTypeID, force_next: forceNextActivity})); + $panel.append($form); + self._onMarkActivityDoneActions($markDoneBtn, $form, activityID); + + // Close and reset any other open panels + _.each($panel.siblings('.o_activity_form'), function (el) { + if ($(el).data('bs.collapse')) { + $(el).empty().collapse('dispose').removeClass('show'); + } + }); + + // Scroll to selected activity + $markDoneBtn.parents('.o_activity_log_container').scrollTo($actLi.position().top, 100); + } + + // Empty and reset panel on close + $panel.on('hidden.bs.collapse', function () { + if ($panel.data('bs.collapse')) { + $actLi.removeClass('o_activity_selected'); + $panel.collapse('dispose'); + $panel.empty(); + } + }); + + this.$('.o_activity_selected').removeClass('o_activity_selected'); + $actLi.toggleClass('o_activity_selected'); + $panel.collapse('toggle'); + + } else if (!$markDoneBtn.data('bs.popover')) { + $markDoneBtn.popover({ template: $(Popover.Default.template).addClass('o_mail_activity_feedback')[0].outerHTML, // Ugly but cannot find another way - container: $popoverElement, + container: $markDoneBtn, title : _t("Feedback"), html: true, trigger:'click', placement: 'right', // FIXME: this should work, maybe a bug in the popper lib content : function () { var $popover = $(QWeb.render('mail.activity_feedback_form', { previous_activity_type_id: previousActivityTypeID, force_next: forceNextActivity})); - $popover.find('#activity_feedback').val(self._draftFeedback[activityID]); - $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, - feedback: _.escape($popover.find('#activity_feedback').val()), - }); - }); - $popover.on('click', '.o_activity_popover_discard', function (ev) { - ev.stopPropagation(); - $popoverElement.popover('hide'); - }); + self._onMarkActivityDoneActions($markDoneBtn, $popover, activityID); return $popover; }, }).on('shown.bs.popover', function () { @@ -316,6 +332,39 @@ var BasicActivity = AbstractField.extend({ } }, /** + * Bind all necessary actions to the 'mark as done' form + * + * @private + * @param {Object} $form + * @param {integer} activityID + */ + _onMarkActivityDoneActions: function ($btn, $form, activityID) { + var self = this; + $form.find('#activity_feedback').val(self._draftFeedback[activityID]); + $form.on('click', '.o_activity_popover_done', function (ev) { + ev.stopPropagation(); + self._markActivityDone({ + activityID: activityID, + feedback: _.escape($form.find('#activity_feedback').val()), + }); + }); + $form.on('click', '.o_activity_popover_done_next', function (ev) { + ev.stopPropagation(); + self._markActivityDoneAndScheduleNext({ + activityID: activityID, + feedback: _.escape($form.find('#activity_feedback').val()), + }); + }); + $form.on('click', '.o_activity_popover_discard', function (ev) { + ev.stopPropagation(); + if ($btn.data('bs.popover')) { + $btn.popover('hide'); + } else if ($btn.data('toggle') == 'collapse') { + self.$('#o_activity_form_' + activityID).collapse('hide'); + } + }); + }, + /** * @private * @param {MouseEvent} ev * @returns {$.Deferred} diff --git a/addons/mail/static/src/scss/mail_activity.scss b/addons/mail/static/src/scss/mail_activity.scss index cdd8bbe1cb5..16ad01a83b0 100644 --- a/addons/mail/static/src/scss/mail_activity.scss +++ b/addons/mail/static/src/scss/mail_activity.scss @@ -123,13 +123,13 @@ } .o_activity_color_planned { - color: theme-color('success'); + color: darken(theme-color('success'), 10%); } .o_activity_color_overdue { - color: theme-color('danger'); + color: darken(theme-color('danger'), 10%); } .o_activity_color_today { - color: theme-color('warning'); + color: darken(theme-color('warning'), 10%); } } @@ -160,62 +160,45 @@ } } - .o_activity_link_kanban { - visibility: hidden; - @include media-breakpoint-down(sm) { - visibility: visible; - } - } - - .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 { min-width: 290px; padding: 0px 0px; - > :last-child { - background-color: $o-brand-lightsecondary; - line-height: 3em; - } - ul.o_activity_log { + .o_activity_log_container { max-height: 300px; overflow-y: auto; + } + + ul.o_activity_log { color: $body-color; li { - width: 100%; - display: inline-block; + .o_activity_title_entry { + display: flex; + align-items: baseline; + } + + .o_edit_button { + opacity: 0.5; + } + + &:hover .o_edit_button{ + opacity: 1; + } + + .o_activity_link_kanban { + font-size: 1.5em; + @include o-hover-text-color($text-muted, theme-color('success')); + @include o-hover-opacity(0.5, 1); + } - &.o_activity_label { - line-height: 2.5em; - padding: 0px 5px; - background-color: $o-brand-lightsecondary; + &.o_activity_selected { + border-bottom: 0; } - &.o_log_activity { - padding: 8px 10px 8px 0px; - vertical-align: middle; - &:hover { - background-color: theme-color('light'); - .o_activity_link_kanban { - visibility: visible; - } - } - - .o_activity_title { - width: 85%; - padding: 0px 5px 0px 10px; - white-space: nowrap; - text-overflow: ellipsis; - } + &.o_activity_form > div { + padding-top: 0.5em; + padding-bottom: 0.7em; } } } diff --git a/addons/mail/static/src/xml/web_kanban_activity.xml b/addons/mail/static/src/xml/web_kanban_activity.xml index 25d25c1d09b..eb71ba044c2 100644 --- a/addons/mail/static/src/xml/web_kanban_activity.xml +++ b/addons/mail/static/src/xml/web_kanban_activity.xml @@ -20,58 +20,65 @@ <span role="menuitem" t-if="_.isEmpty(records)" class="dropdown-item-text text-center o_no_activity"> No activities planned. </span> - <div t-else="" aria-haspopup="true" role="menu"> - <ul class="nav o_activity_log" role="menu"> + <div t-else="" aria-haspopup="true" role="menu" class="o_activity_log_container dropdown-item bg-100 p-0"> + <ul class="o_activity_log list-group list-group-flush mb-2" role="menu"> <t t-foreach="_.keys(records)" t-as="key"> <t t-set="logs" t-value="records[key]" /> - <li role="menuitem" class="nav-item o_activity_label"> - <strong t-attf-class="o_activity_color_#{key}"> - <t t-esc="selection[key]" /> (<t t-esc="logs.length"/>) - </strong> + <t t-set="contextual_class" t-value="key == 'planned' ? 'success' : (key == 'today' ? 'warning' : 'danger') "/> + + <li role="menuitem" t-attf-class="o_activity_label list-group-item list-group-item list-group-item-light d-flex justify-content-between align-items-center o_activity_color_{{key}} {{!key_first ? 'mt-2' : ''}}"> + <strong><t t-esc="selection[key]"/></strong> + <span t-attf-class="badge badge-pill badge-{{contextual_class}} border-0 mr-0"><t t-esc="logs.length"/></span> </li> <t t-foreach="logs" t-as="log"> <t t-set="edit_class" t-value="'o_edit_activity'"/> <t t-if="log.force_next"> <t t-set="edit_class" t-value=""/> </t> - <li t-att-data-activity-id="log.id" t-attf-class="nav-item o_log_activity #{edit_class}" role="menuitem"> - <div class="o_activity_title float-left"> - <span t-attf-class="fa #{log.icon} fa-fw" role="img" aria-label="Log" title="Log"/> - <strong> - <t t-esc="log.title_action or log.activity_type_id[1]" /> - </strong> - <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-force-next-activity="log.force_next" 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> + <li t-att-data-activity-id="log.id" t-attf-class="list-group-item o_log_activity d-flex #{edit_class} #{log_last ? 'border-bottom' : ''}" role="menuitem"> + <div class="o_activity_title"> + <div t-attf-class="o_activity_title_entry {{! log.force_next ? 'align-items-center' : 'mb-1'}}"> + <span t-attf-class="fa #{log.icon ? log.icon : 'fa-bell' } fa-fw mr-2 text-center text-muted" role="img" aria-label="Log" title="Log"/> + <strong class="text-dark"><t t-esc="log.title_action or log.activity_type_id[1]"/></strong> + <button t-if="! log.force_next" class="btn btn-sm btn-link py-0 o_edit_button"><i class="fa fa-pencil"/></button> + </div> + <div class="o_activity_title_entry mt-1" t-if="log.state != 'today'"> + <span class="fa fa-clock-o fa-fw mr-2 text-center text-muted" role="img" aria-label="Deadline" title="Deadline"/> + <small t-att-title="log.date_deadline"><t t-esc="log.label_delay" /></small> + </div> + <t t-if="log.mail_template_ids"> + <div t-foreach="log.mail_template_ids" t-as="mail_template" class="o_activity_title_entry mt-2" t-att-data-activity-id="log.id" t-att-data-force-next-activity="log.force_next" t-att-data-previous-activity-type-id="log.activity_type_id[0]"> + <i class="fa fa-envelope-o fa-fw mr-2 text-center text-muted" aria-label="Mail" title="Mail" role="img"></i> + <small> + <div class="mb-1" t-esc="mail_template.name + ':'"/> + <a class="o_activity_template_preview" t-att-data-template-id="mail_template.id" href="#"><b>Preview</b></a> + <small>or</small> + <a class="o_activity_template_send" t-att-data-template-id="mail_template.id" href="#"><b>Send Now</b></a> + </small> </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> - </t> + </t> + <div t-if="log.user_id[0] != uid and log.mail_template_ids" class="o_activity_title_entry"> + <span class="fa fa-user fa-fw mr-2 text-center text-muted" role="img" aria-label="User" title="User"/> + <span><t t-esc="log.user_id[1]" /></span> </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-force-next-activity="log.force_next" 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 class="flex-grow-1 text-right"> + <a t-att-data-force-next-activity="log.force_next" + t-att-data-previous-activity-type-id="log.activity_type_id[0]" + t-att-data-activity-id="log.id" + t-attf-href="#o_mark_done_form{{log.id}}" + class="o_mark_as_done o_activity_link o_activity_link_kanban fa fa-check-circle" + data-toggle="collapse" title="Mark as done" role="img" aria-label="Mark as done"/> </div> </li> + <li t-attf-id="o_activity_form_{{log.id}}" class="o_activity_form list-group-item border-top-0 py-0 mb-2 collapse"></li> </t> </t> </ul> </div> - <div role="menuitem" class="o_schedule_activity text-center"> - <a role="button" class="btn btn-link btn-block"> + <div class="dropdown-divider mt-0"/> + <div role="menuitem" class="o_schedule_activity dropdown-header py-1 text-center"> + <a role="button" class="btn btn-secondary mb-1"> <i class="fa fa-plus fa-fw"></i><strong>Schedule an activity</strong> </a> </div> diff --git a/addons/mail/static/tests/activity_tests.js b/addons/mail/static/tests/activity_tests.js index 8dfa518da5f..9f7f7fd8808 100644 --- a/addons/mail/static/tests/activity_tests.js +++ b/addons/mail/static/tests/activity_tests.js @@ -308,13 +308,13 @@ QUnit.test('activity view: activity widget', function (assert) { today.find('.o_closest_deadline').click(); assert.ok(dropdown.hasClass('show'), "dropdown should be displayed"); assert.ok(dropdown.find('.o_activity_color_today:contains(Today)').length, "Title should be today"); - assert.ok(dropdown.find('.o_activity_title div div div:first span:contains(template8)').length, + assert.ok(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first div:contains(template8)').length, "template8 should be available"); - assert.ok(dropdown.find('.o_activity_title div div div:nth-child(2) span:contains(template9)').length, + assert.ok(dropdown.find('.o_activity_title_entry[data-activity-id="2"]:eq(1) div:contains(template9)').length, "template9 should be available"); - dropdown.find('.o_activity_title div div div:first .o_activity_template_preview').click(); - dropdown.find('.o_activity_title div div div:first .o_activity_template_send').click(); + dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_preview').click(); + dropdown.find('.o_activity_title_entry[data-activity-id="2"]:first .o_activity_template_send').click(); var overdue = activity.$('table tbody tr:first td:nth-child(3).overdue'); overdue.find('.o_closest_deadline').click(); dropdown = overdue.find('.dropdown-menu.o_activity'); diff --git a/addons/mail/static/tests/chatter_tests.js b/addons/mail/static/tests/chatter_tests.js index 00705b0c26e..b69bc6825e5 100644 --- a/addons/mail/static/tests/chatter_tests.js +++ b/addons/mail/static/tests/chatter_tests.js @@ -492,7 +492,7 @@ QUnit.test('kanban activity widget with no activity', function (assert) { }); QUnit.test('kanban activity widget with an activity', function (assert) { - assert.expect(11); + assert.expect(12); this.data.partner.records[0].activity_ids = [1]; this.data.partner.records[0].activity_state = 'today'; @@ -539,8 +539,9 @@ QUnit.test('kanban activity widget with an activity', function (assert) { $record.find('.o_activity_btn').click(); assert.strictEqual(rpcCount, 2, 'a read should have been done to fetch the activity details'); assert.strictEqual($record.find('.o_activity_title').length, 1, "should have an activity scheduled"); - var label_text = $record.find('.o_activity_label .o_activity_color_today').text(); - assert.ok(label_text.indexOf('Today (1)') >= 0, "should display the correct label and count"); + var label = $record.find('.o_activity_log .o_activity_color_today'); + assert.strictEqual(label.find('strong').text(), "Today", "should display the correct label"); + assert.strictEqual(label.find('.badge-warning').text(), "1", "should display the correct count"); // click on the activity button to close the dropdown $record.find('.o_activity_btn').click(); |
