[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:
committed by
Aurélien Warnon
parent
8fa2a6fe11
commit
35bb9ffd52
@@ -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<seats_reserved)"
|
||||
multi_edit="1"
|
||||
sample="1">
|
||||
<field name="name"/>
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user