[IMP] various: add minor UI improvements

PURPOSE

Slightly modify various apps to improve the user experience. Changes include
notably labels and views fine tuning, roundings, and small css fixes.

SPECIFICATIONS

- Remove the decorator from the event list view as it's a bit confusing
- Ensure mailing KPIs are now shown with 2 decimal places
- Re-order the mailing stat buttons
- Improve the background / font colors of the cover block
- Improve the labels of:
  - Title and confirm button of the /button and /link modals
  - Confirm button when archiving a record

Task-3204554

closes odoo/odoo#128108

Related: odoo/enterprise#43937
Signed-off-by: Warnon Aurélien (awa) <awa@odoo.com>
This commit is contained in:
Aditya Sharma
2023-10-03 19:19:52 +00:00
committed by Aurélien Warnon
parent 8fa2a6fe11
commit 35bb9ffd52
13 changed files with 55 additions and 49 deletions
+1 -2
View File
@@ -15,7 +15,7 @@
type="action" class="oe_stat_button" icon="fa-line-chart">
<div class="o_stat_info">
<span class="o_stat_text">
Registration statistics
Registration
</span>
</div>
</button>
@@ -117,7 +117,6 @@
<field name="model">event.event</field>
<field name="arch" type="xml">
<tree string="Events"
decoration-danger="(seats_max and seats_max&lt;seats_reserved)"
multi_edit="1"
sample="1">
<field name="name"/>
+11 -10
View File
@@ -22,6 +22,7 @@ from odoo import api, fields, models, tools, _
from odoo.addons.base_import.models.base_import import ImportValidationError
from odoo.exceptions import UserError, ValidationError
from odoo.osv import expression
from odoo.tools.float_utils import float_round
_logger = logging.getLogger(__name__)
@@ -216,11 +217,11 @@ class MassMailing(models.Model):
replied = fields.Integer(compute="_compute_statistics")
bounced = fields.Integer(compute="_compute_statistics")
failed = fields.Integer(compute="_compute_statistics")
received_ratio = fields.Integer(compute="_compute_statistics", string='Received Ratio')
opened_ratio = fields.Integer(compute="_compute_statistics", string='Opened Ratio')
replied_ratio = fields.Integer(compute="_compute_statistics", string='Replied Ratio')
bounced_ratio = fields.Integer(compute="_compute_statistics", string='Bounced Ratio')
clicks_ratio = fields.Integer(compute="_compute_clicks_ratio", string="Number of Clicks")
received_ratio = fields.Float(compute="_compute_statistics", string='Received Ratio')
opened_ratio = fields.Float(compute="_compute_statistics", string='Opened Ratio')
replied_ratio = fields.Float(compute="_compute_statistics", string='Replied Ratio')
bounced_ratio = fields.Float(compute="_compute_statistics", string='Bounced Ratio')
clicks_ratio = fields.Float(compute="_compute_clicks_ratio", string="Number of Clicks")
next_departure = fields.Datetime(compute="_compute_next_departure", string='Scheduled date')
# UX
warning_message = fields.Char(
@@ -287,7 +288,7 @@ class MassMailing(models.Model):
GROUP BY stats.mass_mailing_id
""", [tuple(self.ids) or (None,)])
mass_mailing_data = self.env.cr.dictfetchall()
mapped_data = dict([(m['id'], 100 * m['nb_clicks'] / m['nb_mails']) for m in mass_mailing_data])
mapped_data = dict([(m['id'], float_round(100 * m['nb_clicks'] / m['nb_mails'], precision_digits=2)) for m in mass_mailing_data])
for mass_mailing in self:
mass_mailing.clicks_ratio = mapped_data.get(mass_mailing.id, 0)
@@ -329,10 +330,10 @@ class MassMailing(models.Model):
""", (tuple(self.ids), ))
for row in self.env.cr.dictfetchall():
total = (row['expected'] - row['canceled']) or 1
row['received_ratio'] = 100.0 * row['delivered'] / total
row['opened_ratio'] = 100.0 * row['opened'] / total
row['replied_ratio'] = 100.0 * row['replied'] / total
row['bounced_ratio'] = 100.0 * row['bounced'] / total
row['received_ratio'] = float_round(100.0 * row['delivered'] / total, precision_digits=2)
row['opened_ratio'] = float_round(100.0 * row['opened'] / total, precision_digits=2)
row['replied_ratio'] = float_round(100.0 * row['replied'] / total, precision_digits=2)
row['bounced_ratio'] = float_round(100.0 * row['bounced'] / total, precision_digits=2)
self.browse(row.pop('mailing_id')).update(row)
def _compute_next_departure(self):
+9 -8
View File
@@ -6,6 +6,7 @@ from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo.tools.float_utils import float_round
class UtmCampaign(models.Model):
@@ -37,10 +38,10 @@ class UtmCampaign(models.Model):
help="Selection to determine the winner mailing that will be sent.")
# stat fields
received_ratio = fields.Integer(compute="_compute_statistics", string='Received Ratio')
opened_ratio = fields.Integer(compute="_compute_statistics", string='Opened Ratio')
replied_ratio = fields.Integer(compute="_compute_statistics", string='Replied Ratio')
bounced_ratio = fields.Integer(compute="_compute_statistics", string='Bounced Ratio')
received_ratio = fields.Float(compute="_compute_statistics", string='Received Ratio')
opened_ratio = fields.Float(compute="_compute_statistics", string='Opened Ratio')
replied_ratio = fields.Float(compute="_compute_statistics", string='Replied Ratio')
bounced_ratio = fields.Float(compute="_compute_statistics", string='Bounced Ratio')
@api.depends('ab_testing_winner_mailing_id')
def _compute_ab_testing_completed(self):
@@ -110,10 +111,10 @@ class UtmCampaign(models.Model):
total = (stats['expected'] - stats['cancel']) or 1
delivered = stats['sent'] - stats['bounce']
vals = {
'received_ratio': 100.0 * delivered / total,
'opened_ratio': 100.0 * stats['open'] / total,
'replied_ratio': 100.0 * stats['reply'] / total,
'bounced_ratio': 100.0 * stats['bounce'] / total
'received_ratio': float_round(100.0 * delivered / total, precision_digits=2),
'opened_ratio': float_round(100.0 * stats['open'] / total, precision_digits=2),
'replied_ratio': float_round(100.0 * stats['reply'] / total, precision_digits=2),
'bounced_ratio': float_round(100.0 * stats['bounce'] / total, precision_digits=2)
}
campaign.update(vals)
@@ -50,7 +50,7 @@ class TestMailingABTesting(MassMailCommon):
self.ab_testing_mailing_2.mailing_trace_ids[:15].set_opened()
self.ab_testing_mailing_ids.invalidate_recordset()
self.assertEqual(self.ab_testing_mailing_1.opened_ratio, 66)
self.assertEqual(self.ab_testing_mailing_1.opened_ratio, 66.67)
self.assertEqual(self.ab_testing_mailing_2.opened_ratio, 50)
with self.mock_mail_gateway():
@@ -82,7 +82,7 @@ class TestMailingABTesting(MassMailCommon):
self.ab_testing_mailing_2.mailing_trace_ids[:15].set_opened()
self.ab_testing_mailing_ids.invalidate_recordset()
self.assertEqual(self.ab_testing_mailing_1.opened_ratio, 66)
self.assertEqual(self.ab_testing_mailing_1.opened_ratio, 66.67)
self.assertEqual(self.ab_testing_mailing_2.opened_ratio, 50)
with self.mock_mail_gateway():
@@ -165,7 +165,7 @@ class TestMailingABTesting(MassMailCommon):
self.ab_testing_mailing_2.mailing_trace_ids[:15].set_opened()
self.ab_testing_mailing_ids.invalidate_recordset()
self.assertEqual(self.ab_testing_mailing_1.opened_ratio, 66)
self.assertEqual(self.ab_testing_mailing_1.opened_ratio, 66.67)
self.assertEqual(self.ab_testing_mailing_2.opened_ratio, 50)
with self.mock_mail_gateway():
@@ -130,14 +130,6 @@
</div>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_view_delivered"
id="button_view_delivered"
type="object"
context="{'search_default_filter_delivered': True}"
invisible="state in ('draft', 'test')"
class="oe_stat_button">
<field name="received_ratio" string="Received" widget="percentpie"/>
</button>
<button name="action_view_opened"
type="object"
context="{'search_default_filter_opened': True}"
@@ -145,6 +137,13 @@
class="oe_stat_button">
<field name="opened_ratio" string="Opened" widget="percentpie"/>
</button>
<button name="action_view_replied"
type="object"
context="{'search_default_filter_replied': True}"
invisible="state in ('draft', 'test')"
class="oe_stat_button">
<field name="replied_ratio" string="Replied" widget="percentpie"/>
</button>
<button name="action_view_clicked"
type="object"
context="{'search_default_filter_clicked': True}"
@@ -152,12 +151,13 @@
class="oe_stat_button">
<field name="clicks_ratio" string="Clicked" widget="percentpie"/>
</button>
<button name="action_view_replied"
<button name="action_view_delivered"
id="button_view_delivered"
type="object"
context="{'search_default_filter_replied': True}"
context="{'search_default_filter_delivered': True}"
invisible="state in ('draft', 'test')"
class="oe_stat_button">
<field name="replied_ratio" string="Replied" widget="percentpie"/>
<field name="received_ratio" string="Received" widget="percentpie"/>
</button>
<button name="action_view_bounced"
type="object"
@@ -413,12 +413,14 @@
<xpath expr="//div[hasclass('alert-info')]/div[hasclass('o_mails_in_queue')]/strong" position="move"/>
</xpath>
<xpath expr="//div[hasclass('alert-info')]/div[hasclass('o_mails_in_queue')]" position="replace"/>
<xpath expr="//div[hasclass('oe_button_box')]" position="inside">
<xpath expr="//button[@name='action_view_opened']" position="before">
<button name="action_view_traces_sent"
invisible="state in ('draft', 'test')"
type="object" class="oe_stat_button" icon="fa-paper-plane">
<field name="sent" widget="statinfo" string="Sent"/>
</button>
</xpath>
<xpath expr="//div[hasclass('oe_button_box')]" position="inside">
<button name="action_view_traces_canceled"
invisible="state in ('draft', 'test')"
type="object" class="oe_stat_button" icon="fa-paper-plane-o">
@@ -5,8 +5,9 @@
<div class="container">
<div class="row">
<div class="col-lg-12 oe_img_bg pt56 pb48" style="background-image: url('/web/image/mass_mailing.s_cover_default_image');">
<h1 style="text-align: center;"><font style="font-size: 62px; font-weight: bold;">Catchy Headline</font></h1>
<p class="lead" style="text-align: center;">Write one or two paragraphs describing your product, services or a specific feature.<br/> To be successful your content needs to be useful to your readers.</p>
<div class="o_we_bg_filter bg-black-50"/>
<h1 class="text-white" style="text-align: center;"><font style="font-size: 62px; font-weight: bold;">Catchy Headline</font></h1>
<p class="lead text-white" style="text-align: center;">Write one or two paragraphs describing your product, services or a specific feature.<br/> To be successful your content needs to be useful to your readers.</p>
</div>
</div>
</div>
@@ -42,7 +42,7 @@ class MassMailing(models.Model):
'name': _("Leads Analysis"),
'res_model': 'crm.lead',
'type': 'ir.actions.act_window',
'view_mode': 'graph,pivot,tree,form',
'view_mode': 'tree,pivot,graph,form',
}
def _prepare_statistics_email_values(self):
@@ -53,7 +53,7 @@ class MassMailing(models.Model):
'name': _("Sales Analysis"),
'res_model': 'sale.report',
'type': 'ir.actions.act_window',
'view_mode': 'graph,pivot,tree,form',
'view_mode': 'tree,pivot,graph,form',
}
def action_redirect_to_invoiced(self):
@@ -79,7 +79,7 @@ class MassMailing(models.Model):
'name': _("Invoices Analysis"),
'res_model': 'account.invoice.report',
'type': 'ir.actions.act_window',
'view_mode': 'graph,pivot,tree,form',
'view_mode': 'tree,pivot,graph,form',
}
def _prepare_statistics_email_values(self):
@@ -69,7 +69,7 @@ class TestMailingStatistics(TestMassMailCommon):
kpi_values = body_html.xpath('//table[@data-field="mail"]//*[hasclass("kpi_value")]/text()')
self.assertEqual(
[t.strip().strip('%') for t in kpi_values],
['100', str(mailing.opened_ratio), str(mailing.replied_ratio)]
['100.0', str(mailing.opened_ratio), str(mailing.replied_ratio)]
)
# test body content: clicks (a bit hackish but hey we are in stable)
kpi_click_values = body_html.xpath('//table//tr[contains(@style,"color: #888888")]/td[contains(@style,"width: 30%")]/text()')
@@ -63,7 +63,7 @@ class TestMailingStatistics(TestMassSMSCommon):
kpi_values = body_html.xpath('//table[@data-field="sms"]//*[hasclass("kpi_value")]/text()')
self.assertEqual(
[t.strip().strip('%') for t in kpi_values],
['100', str(mailing.opened_ratio), str(mailing.replied_ratio)]
['100.0', str(float(mailing.opened_ratio)), str(float(mailing.replied_ratio))]
)
# test body content: clicks (a bit hackish but hey we are in stable)
kpi_click_values = body_html.xpath('//table//tr[contains(@style,"color: #888888")]/td[contains(@style,"width: 30%")]/text()')
@@ -152,6 +152,7 @@ export class KanbanHeader extends Component {
archiveGroup() {
this.dialog.add(ConfirmationDialog, {
body: _t("Are you sure that you want to archive all the records from this column?"),
confirmLabel: _t("Archive All"),
confirm: async () => {
await this.group.list.archive();
this.props.progressBarState?.updateCounts(this.group);
@@ -2174,7 +2174,7 @@ export class Wysiwyg extends Component {
}
_getBannerCommand(title, alertClass, iconClass, description, priority) {
return {
category: _t('Banner'),
category: _t('Banners'),
name: title,
priority: priority,
description: description,
@@ -2241,10 +2241,10 @@ export class Wysiwyg extends Component {
const editorOptions = this.options;
const categories = [{ name: _t('Banners'), priority: 65 },];
const commands = [
this._getBannerCommand(_t('Banner Info'), 'info', 'fa-info-circle', _t('Insert an info banner section'), 24),
this._getBannerCommand(_t('Banner Success'), 'success', 'fa-check-circle', _t('Insert a success banner section'), 23),
this._getBannerCommand(_t('Banner Warning'), 'warning', 'fa-exclamation-triangle', _t('Insert a warning banner section'), 22),
this._getBannerCommand(_t('Banner Danger'), 'danger', 'fa-exclamation-circle', _t('Insert a danger banner section'), 21),
this._getBannerCommand(_t('Banner Info'), 'info', 'fa-info-circle', _t('Insert an info banner'), 24),
this._getBannerCommand(_t('Banner Success'), 'success', 'fa-check-circle', _t('Insert a success banner'), 23),
this._getBannerCommand(_t('Banner Warning'), 'warning', 'fa-exclamation-triangle', _t('Insert a warning banner'), 22),
this._getBannerCommand(_t('Banner Danger'), 'danger', 'fa-exclamation-circle', _t('Insert a danger banner'), 21),
{
category: _t('Structure'),
name: _t('Quote'),
+3 -2
View File
@@ -17,7 +17,8 @@
<!-- Link Dialog (allows to choose a style and content for a link on the page) -->
<div t-name="web_editor.LinkDialog">
<Dialog size="'xl'">
<t t-set="title">Insert a Link / Button</t>
<Dialog size="'xl'" title="title">
<div class="o_link_dialog">
<div class="row" t-ref="linkComponentWrapper">
<form class="col-lg-8">
@@ -100,7 +101,7 @@
</div>
</div>
<t t-set-slot="footer">
<button class="btn btn-primary" t-on-click="this.onSave">Save</button>
<button class="btn btn-primary" t-on-click="this.onSave">Insert</button>
<button class="btn btn-secondary" t-on-click="this.props.close">Discard</button>
</t>
</div>