diff options
| author | Géry Debongnie <ged@odoo.com> | 2015-06-15 16:31:27 +0200 |
|---|---|---|
| committer | Géry Debongnie <ged@odoo.com> | 2015-06-15 16:31:27 +0200 |
| commit | 2c7367a8cc8d9e200b1fa2083ce0d80f8f4e4bd3 (patch) | |
| tree | af916cff4d94554b1e9667913e3feb331ec81a13 | |
| parent | 5cb10d4fc9594f0c6825757c24d66d63664ce026 (diff) | |
| parent | 82186a93bb3f93c36667835df68fa31dcacf97a2 (diff) | |
[MERGE] new kanban view
This branch introduces the new kanban view. It is not completely
compatible with the previous version. Here are a few importants
points to be aware of:
* the kanban view template is rendered directly, not put inside a div.
It means that if a template has more than one root, chaos will ensue
* kanban view has been split into several files, and various js modules
have changed. For example, the KanbanRecord class is now exported by
the 'web_kanban.Record' module. So, require statements need to be
updated
* records do not have access to the view (i.e., record.view.is_action_enabled('edit'))
You can use the 'editable' and 'deletable' key instead
123 files changed, 3441 insertions, 2365 deletions
diff --git a/addons/account/static/src/js/account_journal_dashboard_widget.js b/addons/account/static/src/js/account_journal_dashboard_widget.js index 9818309403e..c152a90442d 100644 --- a/addons/account/static/src/js/account_journal_dashboard_widget.js +++ b/addons/account/static/src/js/account_journal_dashboard_widget.js @@ -1,9 +1,9 @@ odoo.define('journal_dashboard', function (require) { 'use strict'; -var kanban_common = require('web_kanban.common'); +var kanban_widgets = require('web_kanban.widgets'); -var JournalDashboardGraph = kanban_common.AbstractField.extend({ +var JournalDashboardGraph = kanban_widgets.AbstractField.extend({ start: function() { var self = this; self.graph_type = self.$node.attr('graph_type') @@ -86,6 +86,6 @@ var JournalDashboardGraph = kanban_common.AbstractField.extend({ }); -kanban_common.registry.add('dashboard_graph', JournalDashboardGraph); +kanban_widgets.registry.add('dashboard_graph', JournalDashboardGraph); });
\ No newline at end of file diff --git a/addons/account/views/account_journal_dashboard_view.xml b/addons/account/views/account_journal_dashboard_view.xml index 78ccd40bcb7..5ebe9e36659 100644 --- a/addons/account/views/account_journal_dashboard_view.xml +++ b/addons/account/views/account_journal_dashboard_view.xml @@ -194,7 +194,7 @@ <a type="object" name="toggle_favorite"><i t-att-class="(record.show_on_dashboard.raw_value ? 'oe_star_on' : 'oe_star_off') + ' fa fa-star o_dashboard_star'" title="Click to add/remove from favorite"></i></a> </div> <div class="col-xs-6 text-right"> - <a t-if="widget.view.is_action_enabled('edit')" type="edit">Settings</a> + <a t-if="widget.editable" type="edit">Settings</a> </div> </div> </t> diff --git a/addons/account/views/account_payment_view.xml b/addons/account/views/account_payment_view.xml index a413a55477e..7a53640131e 100644 --- a/addons/account/views/account_payment_view.xml +++ b/addons/account/views/account_payment_view.xml @@ -79,12 +79,13 @@ <field name="state" widget="statusbar" statusbar_visible="posted,reconciled"/> </header> <sheet> - <div class="oe_right oe_button_box"> - <button class="oe_inline oe_stat_button" name="button_journal_entries" - string="Journal Items" type="object" groups="account.group_account_manager" + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" name="button_journal_entries" + string="Journal Items" type="object" + groups="account.group_account_manager" attrs="{'invisible':[('move_line_ids','=',[])]}" icon="fa-bars"/> <field name="move_line_ids" invisible="1"/> - <button class="oe_inline oe_stat_button" name="button_invoices" + <button class="oe_stat_button" name="button_invoices" string="Invoices" type="object" attrs="{'invisible':[('has_invoices','=',False)]}" icon="fa-bars"/> <field name="has_invoices" invisible="1"/> diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml index 9bb4c45df0d..3cb209eecbf 100644 --- a/addons/account/views/account_view.xml +++ b/addons/account/views/account_view.xml @@ -442,8 +442,8 @@ <field name="state" widget="statusbar" statusbar_visible="open,confirm"/> </header> <sheet> - <div class="oe_right oe_button_box" name="import_buttons"> - <button class="oe_inline oe_stat_button" name="button_journal_entries" + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" name="button_journal_entries" string="Journal Items" type="object" attrs="{'invisible':[('move_line_ids','=',[])]}" icon="fa-bars"/> <field name="move_line_ids" invisible="1"/> diff --git a/addons/account/views/partner_view.xml b/addons/account/views/partner_view.xml index c4299986216..ab6bfcf4118 100644 --- a/addons/account/views/partner_view.xml +++ b/addons/account/views/partner_view.xml @@ -77,7 +77,7 @@ <field name="priority" eval="20"/> <field name="groups_id" eval="[(4, ref('account.group_account_invoice'))]"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> + <div name="button_box" position="inside"> <button type="action" class="oe_stat_button" id="invoice_button" @@ -85,10 +85,13 @@ name="%(account.action_invoice_refund_out_tree)d" attrs="{'invisible': [('customer', '=', False)]}" context="{'search_default_partner_id': active_id,'default_partner_id': active_id}"> - <div><strong><field name="total_invoiced"/></strong><br/>Invoiced</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_value"><field name="total_invoiced"/></span> + <span class="o_stat_text">Invoiced</span> + </div> </button> <field name="currency_id" invisible="1"/> - </xpath> + </div> </field> </record> @@ -99,12 +102,12 @@ <field name="priority" eval="22"/> <field name="groups_id" eval="[(4, ref('analytic.group_analytic_accounting'))]"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" type="action" name="%(account.action_open_partner_analytic_accounts)d" + <div name="button_box" position="inside"> + <button class="oe_stat_button" type="action" name="%(account.action_open_partner_analytic_accounts)d" icon="fa-book"> <field string="Contracts" name="contracts_count" widget="statinfo"/> </button> - </xpath> + </div> </field> </record> @@ -132,10 +135,10 @@ <field name="groups_id" eval="[(4, ref('account.group_account_invoice'))]"/> <field name="arch" type="xml"> <page name="sales_purchases" position="after"> - <page string="Accounting" col="4" name="accounting" attrs="{'invisible': [('is_company','=',False),('parent_id','!=',False)]}"> + <page string="Accounting" name="accounting" attrs="{'invisible': [('is_company','=',False),('parent_id','!=',False)]}"> <group> <group string="Sale" name="acc_sale"> - <button name="%(action_account_partner_followup_note)d" type="action" string="Log a followup note"/><br /> + <button name="%(action_account_partner_followup_note)d" class="oe_link" type="action" string="Log a followup note" colspan="2"/> <field name="property_payment_term" widget="selection"/> <field name="credit"/> <field name="credit_limit"/> @@ -154,16 +157,16 @@ </group> </group> <group string="Bank Accounts" name="bank_accounts"> - <field name="bank_ids" context="{'default_partner_id': active_id, 'form_view_ref': 'base.view_partner_bank_form'}" nolabel="1"> - <tree string="Bank Details"> - <field name="state" invisible="1"/> - <field name="sequence" invisible="1"/> - <field name="acc_number"/> - <field name="bank_name"/> - <field name="owner_name"/> - </tree> - </field> - </group> + <field name="bank_ids" context="{'default_partner_id': active_id, 'form_view_ref': 'base.view_partner_bank_form'}" nolabel="1"> + <tree string="Bank Details"> + <field name="state" invisible="1"/> + <field name="sequence" invisible="1"/> + <field name="acc_number"/> + <field name="bank_name"/> + <field name="owner_name"/> + </tree> + </field> + </group> </page> <page string="Accounting" name="accounting_disabled" attrs="{'invisible': ['|',('is_company','=',True),('parent_id','=',False)]}"> <div> @@ -174,4 +177,4 @@ </field> </record> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/addons/account_analytic_analysis/account_analytic_analysis_view.xml b/addons/account_analytic_analysis/account_analytic_analysis_view.xml index b7a9c1973fa..c9f18cb7fee 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis_view.xml +++ b/addons/account_analytic_analysis/account_analytic_analysis_view.xml @@ -374,18 +374,18 @@ <field name="inherit_id" ref="analytic.view_account_analytic_account_form"/> <field eval="20" name="priority"/> <field name="arch" type="xml"> - <xpath expr='//div[@name="buttons"]' position='inside'> - <button class="oe_inline oe_stat_button" + <div name="button_box" position='inside'> + <button class="oe_stat_button" string="To invoice" icon="fa-money" name="%(action_hr_tree_invoiced_all)d" type="action" context="{'search_default_account_id': [active_id], 'default_account_id': active_id}"/> - <button class="oe_inline oe_stat_button" string="Invoices" + <button class="oe_stat_button" string="Invoices" icon="fa-book" name="%(action_contract_invoices_all)d" type="action"/> - </xpath> + </div> </field> </record> diff --git a/addons/account_analytic_default/account_analytic_default_view.xml b/addons/account_analytic_default/account_analytic_default_view.xml index fd38be3165d..c43ddff78e0 100644 --- a/addons/account_analytic_default/account_analytic_default_view.xml +++ b/addons/account_analytic_default/account_analytic_default_view.xml @@ -79,12 +79,12 @@ <field name="model">product.product</field> <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" name= "%(action_product_default_list)d" type="action" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name= "%(action_product_default_list)d" type="action" groups="analytic.group_analytic_accounting" icon="fa-xing"> <field string="Analytic Rules" name="rules_count" widget="statinfo" /> </button> - </xpath> + </div> </field> </record> @@ -93,12 +93,12 @@ <field name="model">product.template</field> <field name="inherit_id" ref="product.product_template_only_form_view"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" name= "action_view_rules" type="object" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name= "action_view_rules" type="object" groups="analytic.group_analytic_accounting" icon="fa-xing"> <field string="Analytic Rules" name="rules_count" widget="statinfo" /> </button> - </xpath> + </div> </field> </record> diff --git a/addons/account_asset/views/account_asset_view.xml b/addons/account_asset/views/account_asset_view.xml index 1062a574243..a9fc98331b9 100644 --- a/addons/account_asset/views/account_asset_view.xml +++ b/addons/account_asset/views/account_asset_view.xml @@ -99,10 +99,10 @@ <field name="state" widget="statusbar" statusbar_visible="draft,open"/> </header> <sheet> - <div class="oe_button_box oe_right"> - <button class="oe_inline oe_stat_button" name="open_entries" type="object" icon="fa-pencil"> - <field string="Entries" name="entry_count" widget="statinfo" /> - </button> + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" name="open_entries" type="object" icon="fa-pencil"> + <field string="Entries" name="entry_count" widget="statinfo" /> + </button> </div> <div class="oe_title"> <label for="name" class="oe_edit_only"/> diff --git a/addons/analytic/views/analytic_view.xml b/addons/analytic/views/analytic_view.xml index 273b7373207..a86d5cbdb4f 100644 --- a/addons/analytic/views/analytic_view.xml +++ b/addons/analytic/views/analytic_view.xml @@ -8,8 +8,7 @@ <field name="arch" type="xml"> <form string="Analytic Account" version="7.0"> <sheet string="Analytic Account"> - <div class="oe_right oe_button_box" name="buttons"> - </div> + <div class="oe_button_box" name="button_box"/> <div class="oe_title"> <label for="name" class="oe_edit_only"/> diff --git a/addons/auth_signup/res_users_view.xml b/addons/auth_signup/res_users_view.xml index 449ba7381fd..7c749271e77 100644 --- a/addons/auth_signup/res_users_view.xml +++ b/addons/auth_signup/res_users_view.xml @@ -24,7 +24,7 @@ </div> </xpath> <!-- add Reset Password button --> - <xpath expr="//div[@class='oe_right oe_button_box']//button" position="replace"> + <xpath expr="//div[@name='button_box']//button" position="replace"> <button string="Send Reset Password Instructions" class="oe_link" type="object" name="action_reset_password" diff --git a/addons/base_setup/res_partner_view.xml b/addons/base_setup/res_partner_view.xml index 15b80151831..3d48396ee7d 100644 --- a/addons/base_setup/res_partner_view.xml +++ b/addons/base_setup/res_partner_view.xml @@ -8,7 +8,7 @@ <field name="model">res.partner</field> <field name="inherit_id" ref="base.res_partner_kanban_view"/> <field name="arch" type="xml"> - <xpath expr="//div[@class='oe_kanban_partner_categories']" position="inside"> + <xpath expr="//div[contains(@class, 'oe_kanban_partner_categories')]" position="inside"> <span class="oe_kanban_list_many2many"> <field name="category_id"/> </span> diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 42b20752a91..cfc52e0fdd4 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -121,8 +121,8 @@ on_change="onchange_stage_id(stage_id)"/> </header> <sheet> - <div class="oe_right oe_button_box" name="buttons"> - <button class="oe_inline oe_stat_button" type="action" + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" type="action" context="{'default_opportunity_id': active_id, 'search_default_opportunity_id': active_id, 'default_partner_id': partner_id, 'default_duration': 1.0}" name="%(crm.crm_phonecall_category_phone_incoming0)d" icon="fa-phone"> <field string="Calls" name="calls_count" widget="statinfo"/> @@ -284,7 +284,7 @@ </field> </record> - <!-- CRM Lead Kanban View --> + <!-- CRM Lead Kanban View --> <record model="ir.ui.view" id="crm_case_kanban_view_leads"> <field name="name">CRM - Leads Kanban</field> <field name="model">crm.lead</field> @@ -300,15 +300,19 @@ <field name="message_summary"/> <field name="message_unread"/> <field name="tag_ids"/> + <field name="partner_id"/> <templates> <field name="date_deadline"/> <t t-name="kanban-box"> - <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click"> - <div class="oe_dropdown_toggle oe_dropdown_kanban"> - <i class="fa fa-bars fa-lg"/> - <ul class="oe_dropdown_menu"> - <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit Opportunity</a></li></t> - <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> + <div t-attf-class="#{kanban_color(record.color.raw_value)} o_kanban_small oe_kanban_global_click"> + <div class="o_dropdown_kanban dropdown"> + + <a class="dropdown-toggle btn" data-toggle="dropdown" href="#" > + <span class="fa fa-bars fa-lg"/> + </a> + <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"> + <t t-if="widget.editable"><li><a type="edit">Edit Opportunity</a></li></t> + <t t-if="widget.deletable"><li><a type="delete">Delete</a></li></t> <li><a name="%(mail.action_email_compose_message_wizard)d" type="action" >Send Email</a></li> <li><a type="action" name="%(crm.crm_phonecall_category_phone_incoming0)d">Calls</a></li> <li><a name="action_schedule_meeting" type="object">Schedule Meeting</a></li> @@ -317,32 +321,17 @@ </div> <div class="oe_kanban_content"> <div> - <strong><field name="name"/></strong> - <t t-if="record.planned_revenue.raw_value"> - - <b><t t-esc="record.planned_revenue.value"/> - <field name="company_currency"/></b> - </t> + <field name="tag_ids"/> </div> <div> - <field name="partner_id"/> - </div> - <div class="oe_deadline"> - <t t-if="record.date_deadline.raw_value and record.date_deadline.raw_value lt (new Date())" t-set="red">oe_kanban_text_red</t> - <span t-attf-class="#{red || ''}"><field name="date_action"/></span> - <t t-if="record.date_action.raw_value"> : </t> - <field name="title_action"/> - </div> - <div class="oe_kanban_footer_left"> - <field name="tag_ids"/> + <strong><field name="name"/></strong> </div> - <div class="oe_kanban_bottom_left"> - <t t-raw="record.message_summary.raw_value"/> + <div class="text-muted"> + <span t-if="record.planned_revenue.raw_value"><t t-esc="record.planned_revenue.value"/><field name="company_currency"/></span> <span t-if="record.partner_id.value"> - <t t-esc="record.partner_id.value"/></span> </div> - <div class="oe_kanban_bottom_right"> + <div class="o_kanban_footer"> + <field name="priority" widget="priority" groups="base.group_user"/> <t t-raw="record.message_summary.raw_value"/> <img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar pull-right"/> - <div class="pull-left" groups="base.group_user"> - <field name="priority" widget="priority"/> - </div> </div> </div> <div class="oe_clear"></div> @@ -417,13 +406,13 @@ domain="['&', ('team_ids', '=', team_id), '|', ('type', '=', type), ('type', '=', 'both')]"/> </header> <sheet> - <div name="buttons" class="oe_right oe_button_box"> - <button class="oe_inline oe_stat_button" type="action" + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" type="action" name="%(crm.crm_phonecall_category_phone_incoming0)d" icon="fa-phone" context="{'default_opportunity_id': active_id, 'search_default_opportunity_id': active_id, 'default_partner_id': partner_id, 'default_duration': 1.0}"> <field string="Calls" name="calls_count" widget="statinfo"/> </button> - <button class="oe_inline oe_stat_button" type="object" + <button class="oe_stat_button" type="object" context="{'partner_id': partner_id}" name="action_schedule_meeting" icon="fa-calendar"> <field string="Meetings" name="meeting_count" widget="statinfo"/> diff --git a/addons/crm/crm_phonecall_view.xml b/addons/crm/crm_phonecall_view.xml index 7a68bab2d93..ac3a9493395 100644 --- a/addons/crm/crm_phonecall_view.xml +++ b/addons/crm/crm_phonecall_view.xml @@ -87,19 +87,28 @@ <field name="state" nolabel="1" widget="statusbar" clickable="True"/> </header> <sheet string="Phone Call"> - <div class="oe_right oe_button_box"> - <button class="oe_inline oe_stat_button" type="object" name="action_button_convert2opportunity" + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" type="object" name="action_button_convert2opportunity" icon="fa-star" attrs="{'invisible':[ '|', ('opportunity_id','!=',False), ('state', 'not in', ('open', 'done'))]}" help="Convert To Opportunity"> - <div>Convert To<br/>Opportunity</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Convert To</span> + <span class="o_stat_text">Opportunity</span> + </div> </button> - <button class="oe_inline oe_stat_button" type="action" + <button class="oe_stat_button" type="action" name="%(phonecall_to_phonecall_act)d" icon="fa-phone"> - <div>Schedule<br/>Other Call</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Schedule</span> + <span class="o_stat_text">Other Call</span> + </div> </button> - <button class="oe_inline oe_stat_button" name="action_make_meeting" type="object" icon="fa-calendar fa-fw"> - <div>Schedule<br/>A Meeting</div> + <button class="oe_stat_button" name="action_make_meeting" type="object" icon="fa-calendar fa-fw"> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Schedule</span> + <span class="o_stat_text">A Meeting</span> + </div> </button> </div> <div class="oe_title"> diff --git a/addons/crm/res_partner_view.xml b/addons/crm/res_partner_view.xml index 2049f729b99..07d50551378 100644 --- a/addons/crm/res_partner_view.xml +++ b/addons/crm/res_partner_view.xml @@ -68,12 +68,8 @@ <field name="meeting_count"/> </field> <xpath expr="//div[@class='oe_kanban_partner_links']" position="inside"> - <a name="%(relate_partner_opportunities)d" type="action" t-if="record.opportunity_count.value>0"> - <t t-esc="record.opportunity_count.value"/> Opportunities - </a> - <a name="%(calendar_event_partner)d" type="action" t-if="record.meeting_count.value>0"> - <t t-esc="record.meeting_count.value"/> Meetings - </a> + <span class="badge" t-if="record.opportunity_count.value>0"><i class="fa fa-fw fa-star"/><t t-esc="record.opportunity_count.value"/></span> + <span class="badge" t-if="record.meeting_count.value>0"><i class="fa fa-fw fa-calendar"/><t t-esc="record.meeting_count.value"/></span> </xpath> </field> </record> @@ -89,31 +85,30 @@ <field name="user_id" position="after"> <field name="team_id" completion="1" groups="base.group_multi_salesteams"/> </field> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button o_res_partner_tip_opp" type="action" + <div name="button_box" position="inside"> + <button class="oe_stat_button o_res_partner_tip_opp" type="action" attrs="{'invisible': [('customer', '=', False)]}" name="%(crm.crm_lead_opportunities)d" icon="fa-star" context="{'search_default_partner_id': active_id}"> <field string="Opportunities" name="opportunity_count" widget="statinfo"/> </button> - <button class="oe_inline oe_stat_button" type="object" + <button class="oe_stat_button" type="object" name="schedule_meeting" icon="fa-calendar" context="{'partner_id': active_id}"> <field string="Meetings" name="meeting_count" widget="statinfo"/> </button> - <button class="oe_inline oe_stat_button" type="action" + <button class="oe_stat_button" type="action" name="%(crm.crm_phonecall_category_phone_incoming0)d" icon="fa-phone" context="{'search_default_partner_id': active_id, 'default_duration': 1.0}"> <field string="Calls" name="phonecall_count" widget="statinfo"/> </button> - </xpath> + </div> </data> </field> </record> </data> </openerp> - diff --git a/addons/crm_claim/res_partner_view.xml b/addons/crm_claim/res_partner_view.xml index 11bf9e5a3ba..a2bcbf7a80f 100644 --- a/addons/crm_claim/res_partner_view.xml +++ b/addons/crm_claim/res_partner_view.xml @@ -8,14 +8,14 @@ <field name="inherit_id" ref="base.view_partner_form"/> <field name="priority" eval="50"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" type="action" + <div name="button_box" position="inside"> + <button class="oe_stat_button" type="action" context="{'search_default_partner_id': active_id, 'default_partner_id': active_id}" name="%(crm_claim_category_claim0)d" icon="fa-comments" groups="base.group_sale_salesman"> <field string="Claims" name="claim_count" widget="statinfo"/> </button> - </xpath> + </div> </field> </record> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/addons/crm_project_issue/project_issue_view.xml b/addons/crm_project_issue/project_issue_view.xml index 0b5dc781ba7..4552939fbb6 100644 --- a/addons/crm_project_issue/project_issue_view.xml +++ b/addons/crm_project_issue/project_issue_view.xml @@ -35,14 +35,17 @@ <field name="model">crm.lead</field> <field name="inherit_id" ref="crm.crm_case_form_view_leads" /> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" type="action" name="%(convert_lead2projectissue_wizard_action)d" icon="fa-bug" + <div name="button_box" position="inside"> + <button class="oe_stat_button" type="action" name="%(convert_lead2projectissue_wizard_action)d" icon="fa-bug" help="Convert to Issue"> - <div>Convert To<br/>Issue</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Convert To</span> + <span class="o_stat_text">Issue</span> + </div> </button> - </xpath> + </div> </field> </record> </data> -</openerp>
\ No newline at end of file +</openerp> diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index ea53ba9f9b0..11c0587c3ab 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -65,19 +65,19 @@ <field name="state" widget="statusbar" statusbar_visible="draft,confirm,done"/> </header> <sheet> - <div class="oe_title"> - <label for="name" class="oe_edit_only"/> - <h1><field name="name"/></h1> - </div> - <div class="oe_right oe_button_box" groups="base.group_user"> + <div class="oe_button_box" name="button_box" groups="base.group_user"> <button name="%(event.act_event_registration_from_event)d" type="action" - class="oe_stat_button oe_inline oe_event_attendee_button" + class="oe_stat_button oe_event_attendee_button" icon="fa-github" help="Register with this event"> <field name="seats_reserved" widget="statinfo" string="Attendees"/> </button> </div> + <div class="oe_title"> + <label for="name" class="oe_edit_only"/> + <h1><field name="name"/></h1> + </div> <group name="main_field_group"> <group> <field name="address_id" context="{'show_address': 1}" options='{"always_reload": True}'/> @@ -165,7 +165,7 @@ <div class="oe_dropdown_toggle oe_dropdown_kanban"> <i class="fa fa-bars fa-lg"/> <ul class="oe_dropdown_menu"> - <t t-if="widget.view.is_action_enabled('delete')"> + <t t-if="widget.deletable"> <li><a type="delete">Delete</a></li> </t> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> diff --git a/addons/fleet/fleet_view.xml b/addons/fleet/fleet_view.xml index 99825594dc8..296a46ae6e1 100644 --- a/addons/fleet/fleet_view.xml +++ b/addons/fleet/fleet_view.xml @@ -190,20 +190,7 @@ <field name="state_id" widget="statusbar" clickable="True" /> </header> <sheet> - <field name="image_medium" widget='image' class="oe_left oe_avatar"/> - <div class="oe_title"> - <label for="model_id" class="oe_edit_only"/> - <h1> - <field name="model_id" class="oe_inline" on_change="on_change_model(model_id)"/> - </h1> - <label for="license_plate" class="oe_edit_only"/> - <h2> - <field name="license_plate" class="oe_inline"/> - </h2> - <label for="tag_ids" class="oe_edit_only"/> - <field name="tag_ids" widget="many2many_tags" /> - </div> - <div class="oe_right oe_button_box"> + <div class="oe_button_box" name="button_box"> <button name="return_action_to_open" type="object" class="oe_stat_button" @@ -245,6 +232,19 @@ <field name="odometer_count" widget="statinfo" string="Odometer"/> </button> </div> + <field name="image_medium" widget='image' class="oe_left oe_avatar"/> + <div class="oe_title"> + <label for="model_id" class="oe_edit_only"/> + <h1> + <field name="model_id" class="oe_inline" on_change="on_change_model(model_id)"/> + </h1> + <label for="license_plate" class="oe_edit_only"/> + <h2> + <field name="license_plate" class="oe_inline"/> + </h2> + <label for="tag_ids" class="oe_edit_only"/> + <field name="tag_ids" widget="many2many_tags" /> + </div> <group col="2" string="General Properties"> <group > <field name="driver_id" /> diff --git a/addons/gamification/views/badge.xml b/addons/gamification/views/badge.xml index 386a1e19974..16a13e72960 100644 --- a/addons/gamification/views/badge.xml +++ b/addons/gamification/views/badge.xml @@ -42,8 +42,7 @@ <button string="Grant this Badge" type="action" name="%(action_grant_wizard)d" class="oe_highlight" attrs="{'invisible': [('remaining_sending','=',0)]}" /> </header> <sheet> - <div class="oe_right oe_button_box"> - </div> + <div class="oe_button_box" name="button_box"/> <field name="image" widget='image' class="oe_left oe_avatar"/> <div class="oe_title"> <label for="name" class="oe_edit_only"/> diff --git a/addons/gamification/views/challenge.xml b/addons/gamification/views/challenge.xml index c5f717f37a0..efd0c9f041c 100644 --- a/addons/gamification/views/challenge.xml +++ b/addons/gamification/views/challenge.xml @@ -40,6 +40,19 @@ <field name="state" widget="statusbar" clickable="True"/> </header> <sheet> + <!-- action buttons --> + <div class="oe_button_box" name="button_box"> + <button type="action" + name="%(goals_from_challenge_act)d" + class="oe_stat_button" + icon="fa-gift" + attrs="{'invisible': [('state','=','draft')]}"> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Related</span> + <span class="o_stat_text">Goals</span> + </div> + </button> + </div> <div class="oe_title"> <label for="name" class="oe_edit_only"/> @@ -53,16 +66,6 @@ </div> </div> - <!-- action buttons --> - <div class="oe_right oe_button_box"> - <button type="action" - name="%(goals_from_challenge_act)d" - class="oe_stat_button" - icon="fa-gift" - attrs="{'invisible': [('state','=','draft')]}"> - <div>Related<br/>Goals</div> - </button> - </div> <group> <group> <field name="period" attrs="{'readonly':[('state','!=','draft')]}"/> diff --git a/addons/hr/hr_dashboard.xml b/addons/hr/hr_dashboard.xml index 884e0cbc68c..9567eda3ed7 100644 --- a/addons/hr/hr_dashboard.xml +++ b/addons/hr/hr_dashboard.xml @@ -47,7 +47,7 @@ </div> </div> - <div t-if="widget.view.is_action_enabled('edit')" class="o_kanban_card_manage_settings row"> + <div t-if="widget.editable" class="o_kanban_card_manage_settings row"> <div class="col-xs-8"> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </div> diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml index be7dfecdfdf..a57db47ccfb 100644 --- a/addons/hr/hr_view.xml +++ b/addons/hr/hr_view.xml @@ -23,6 +23,7 @@ <field name="arch" type="xml"> <form string="Employee"> <sheet> + <div class="oe_button_box" name="button_box"/> <field name="image_medium" widget='image' class="oe_left oe_avatar"/> <div class="oe_title"> <label for="name" class="oe_edit_only"/> @@ -36,11 +37,8 @@ <label for="work_phone" class="oe_edit_only"/> <field name="work_phone"/> </div> - <div class="oe_right oe_button_box" name="button_box"> - <!-- Put here related buttons --> - </div> <notebook> - <page string="Public Information"> + <page name="public" string="Public Information"> <group> <group string="Contact Information"> <field name="address_id" on_change="onchange_address_id(address_id)" @@ -141,14 +139,14 @@ </search> </field> </record> - <!-- HR Kanban View --> + <!-- HR Kanban View --> <record model="ir.ui.view" id="hr_kanban_view_employees"> <field name="name">HR - Employess Kanban</field> <field name="model">hr.employee</field> <field name="priority">10</field> <field name="arch" type="xml"> - <kanban> + <kanban class="o_hr_employee_kanban"> <field name="last_login"/> <field name="message_is_follower"/> <field name="message_follower_ids"/> @@ -157,40 +155,40 @@ <field name="message_unread"/> <templates> <t t-name="kanban-box"> - <div class="row oe_employee_details"> - <div class="col-md-4"> - <a type="open"><img t-att-src="kanban_image('hr.employee', 'image_medium', record.id.value)" class="img-responsive oe_employee_image"/></a> + <div class="o_kanban_two_column oe_kanban_global_click oe_employee_details"> + <div class="o_kanban_image"> + <img t-att-src="kanban_image('hr.employee', 'image_medium', record.id.value)"/> + <div class="o_employee_summary_icons"> + <t t-raw="record.message_summary.raw_value"/> + <span title='Messages'><i class='fa fa-comments'/><t t-esc="record.message_ids.raw_value.length"/></span> + <span title='Followers'><i class='fa fa-users'/><t t-esc="record.message_follower_ids.raw_value.length"/></span> </div> - <div class="col-md-8"> - <h4> - <a type="open"><field name="name"/><t t-if="record.login.raw_value"> (<field name="login"/>)</t></a> - </h4> - <ul> - <li id="last_login"> - <span t-if="record.last_login.raw_value && record.last_login.raw_value.is().today()" class="oe_kanban_button" style="font-size: 100%%"> - <t t-esc="moment(record.last_login.raw_value).format('LT')"/> - </span> - </li> - <li t-if="record.job_id.raw_value"><field name="job_id"/></li> - <li t-if="record.work_location.raw_value"><field name="work_location"/></li> - <li t-if="record.work_phone.raw_value"><i class="fa fa-phone"></i> <field name="work_phone"/></li> - <li t-if="record.mobile_phone.raw_value"><i class="fa fa-mobile"></i> <field name="mobile_phone"/></li> - <li t-if="record.work_email.raw_value"><i class="fa fa-envelope"></i><a t-attf-href="mailto:#{record.work_email.value}"> <field name="work_email"/></a></li> - </ul> - <div class="oe_kanban_bottom_left"> - <t t-raw="record.message_summary.raw_value"/> - <span title='Messages'><i class='fa fa-comments'/><t t-esc="record.message_ids.raw_value.length"/></span> - <span title='Followers'><i class='fa fa-users'/><t t-esc="record.message_follower_ids.raw_value.length"/></span> - </div> - <div class="oe_followers" groups="base.group_user"> - <button t-if="record.message_is_follower.raw_value" name="action_unfollow" type="object" class="oe_follower oe_following"> - <span class="oe_unfollow">Unfollow</span> - <span class="oe_following">Following</span> - </button> - <button t-if="! record.message_is_follower.raw_value" name="action_follow" type="object" class="oe_follower oe_notfollow">Follow</button> - </div> + </div> + <div class="oe_kanban_details oe_employee_details_content"> + <strong> + <field name="name"/><t t-if="record.login.raw_value"> (<field name="login"/>)</t> + </strong> + <ul> + <li id="last_login"> + <span t-if="record.last_login.raw_value && record.last_login.raw_value.is().today()" class="oe_kanban_button" style="font-size: 100%%"> + <t t-esc="moment(record.last_login.raw_value).format('LT')"/> + </span> + </li> + <li t-if="record.job_id.raw_value"><field name="job_id"/></li> + <li t-if="record.work_location.raw_value"><field name="work_location"/></li> + <li t-if="record.work_email.raw_value"><i class="fa fa-envelope"></i> <field name="work_email"/></li> + <li t-if="record.work_phone.raw_value"><i class="fa fa-phone"></i> <field name="work_phone"/></li> + <li t-if="record.mobile_phone.raw_value"><i class="fa fa-mobile"></i> <field name="mobile_phone"/></li> + </ul> + <div class="oe_followers o_kanban_button"> + <button t-if="record.message_is_follower.raw_value" name="action_unfollow" type="object" class="o_follow_btn o_following btn btn-primary"> + <span class="o_unfollow">Unfollow</span> + <span class="o_following">Following</span> + </button> + <button t-if="!record.message_is_follower.raw_value" name="action_follow" type="object" class="o_follow_btn o_not_following btn btn-primary">Follow</button> </div> </div> + </div> </t> </templates> </kanban> @@ -352,14 +350,14 @@ <field name="state" widget="statusbar" statusbar_visible="recruit,open"/> </header> <sheet> + <div class="oe_right" name="button_box"/> <div class="oe_title"> <label for="name" class="oe_edit_only"/> - <h1><field name="name" class="oe_inline"/></h1> + <h1><field name="name"/></h1> </div> - <div class="oe_right" name="buttons"/> <group name="employee_data"> - <field name="department_id" groups="hr.group_multi_departments" class="oe_inline"/> - <field name="no_of_recruitment" class="oe_inline" /> + <field name="department_id" groups="hr.group_multi_departments"/> + <field name="no_of_recruitment"/> </group> <div attrs="{'invisible': [('state', '!=', 'recruit')]}"> <label for="description"/> @@ -520,4 +518,4 @@ groups="group_multi_departments"/> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/addons/hr/static/src/js/hr.js b/addons/hr/static/src/js/hr.js deleted file mode 100644 index 9fda01cef95..00000000000 --- a/addons/hr/static/src/js/hr.js +++ /dev/null @@ -1,8 +0,0 @@ -odoo.define('hr.hr', function(require) { -"use strict"; - -var common = require('web_kanban.common'); - - -}); - diff --git a/addons/hr/static/src/less/hr_dashboard.less b/addons/hr/static/src/less/hr_dashboard.less index eada8b85395..e0dee9037ee 100644 --- a/addons/hr/static/src/less/hr_dashboard.less +++ b/addons/hr/static/src/less/hr_dashboard.less @@ -1,7 +1,11 @@ -.oe_kanban_view.o_kanban_dashboard.o_hr_kanban { - .oe_kanban_record { - max-width: 500px; - min-width: 400px; - min-height: 200px; - } +.o_kanban_view { + .o_kanban_record.oe_employee_details { + .oe_employee_details_content { + padding-left: 16px; + + > h4 { + margin-top: 0; + } + } + } }
\ No newline at end of file diff --git a/addons/hr/views/hr.xml b/addons/hr/views/hr.xml index 44fb6d8655e..e76ec3cc4ce 100644 --- a/addons/hr/views/hr.xml +++ b/addons/hr/views/hr.xml @@ -6,7 +6,6 @@ <link rel="stylesheet" href="/hr/static/src/css/hr.css"/> <link rel="stylesheet" href="/hr/static/src/less/hr_dashboard.less"/> <script type="text/javascript" src="/hr/static/src/js/suggestions.js"></script> - <script type="text/javascript" src="/hr/static/src/js/hr.js"></script> </xpath> </template> </data> diff --git a/addons/hr_contract/hr_contract_view.xml b/addons/hr_contract/hr_contract_view.xml index 59cf0d4e4c4..327d08fc68b 100644 --- a/addons/hr_contract/hr_contract_view.xml +++ b/addons/hr_contract/hr_contract_view.xml @@ -19,7 +19,7 @@ <field name="inherit_id" ref="hr.view_employee_form"/> <field name="arch" type="xml"> <data> - <xpath expr="//div[@name='button_box']" position="inside"> + <div name="button_box" position="inside"> <button name="%(act_hr_employee_2_hr_contract)d" class="oe_stat_button" icon="fa-book" @@ -27,7 +27,7 @@ groups="base.group_hr_manager"> <field name="contracts_count" widget="statinfo" string="Contracts"/> </button> - </xpath> + </div> <xpath expr="//field[@name='coach_id']" position="after"> <field name="manager"/> </xpath> diff --git a/addons/hr_equipment/views/hr_equipment_views.xml b/addons/hr_equipment/views/hr_equipment_views.xml index ef61997f586..2229b861651 100644 --- a/addons/hr_equipment/views/hr_equipment_views.xml +++ b/addons/hr_equipment/views/hr_equipment_views.xml @@ -92,8 +92,8 @@ <div class="oe_dropdown_toggle oe_dropdown_kanban"> <i class="fa fa-bars fa-lg" /> <ul class="oe_dropdown_menu"> - <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit...</a></li></t> - <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> + <t t-if="widget.editable"><li><a type="edit">Edit...</a></li></t> + <t t-if="widget.deletable"><li><a type="delete">Delete</a></li></t> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> </div> @@ -192,13 +192,7 @@ <field name="arch" type="xml"> <form string="Equipments"> <sheet> - <div class="oe_title"> - <label for="name" class="oe_edit_only" string="Name"/> - <h1><field name="name" string="Name"/></h1> - <label for="serial_no" class="oe_edit_only"/> - <field name="serial_no"/> - </div> - <div class="oe_right oe_button_box" name="buttons"> + <div class="oe_button_box" name="button_box"> <button name="%(hr_equipment_request_action_from_equipment)d" type="action" class="oe_stat_button" @@ -206,6 +200,12 @@ <field string="Maintenance" name="maintenance_count" widget="statinfo"/> </button> </div> + <div class="oe_title"> + <label for="name" class="oe_edit_only" string="Name"/> + <h1><field name="name" string="Name"/></h1> + <label for="serial_no" class="oe_edit_only"/> + <field name="serial_no"/> + </div> <group> <group> <field name="category_id" groups="base.group_equipment_manager" on_change="1" options="{"no_open": True}"/> @@ -265,8 +265,8 @@ <div class="oe_dropdown_toggle oe_dropdown_kanban"> <i class="fa fa-bars fa-lg" /> <ul class="oe_dropdown_menu"> - <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit...</a></li></t> - <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> + <t t-if="widget.editable"><li><a type="edit">Edit...</a></li></t> + <t t-if="widget.deletable"><li><a type="delete">Delete</a></li></t> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> </div> @@ -392,13 +392,7 @@ <field name="arch" type="xml"> <form string="Equipment Categories"> <sheet> - <div class="oe_title"> - <label for="name" class="oe_edit_only" string="Category Name"/> - <h1> - <field name="name"/> - </h1> - </div> - <div class="oe_right oe_button_box" name="buttons"> + <div class="oe_button_box" name="button_box"> <button name="%(hr_equipment_action_from_category_form)d" class="oe_stat_button" icon="fa-laptop" @@ -412,6 +406,12 @@ <field string="Maintenance" name="maintenance_count" widget="statinfo"/> </button> </div> + <div class="oe_title"> + <label for="name" class="oe_edit_only" string="Category Name"/> + <h1> + <field name="name"/> + </h1> + </div> <group> <group col="4"> <field name="user_id"/> diff --git a/addons/hr_evaluation/hr_evaluation_view.xml b/addons/hr_evaluation/hr_evaluation_view.xml index 9cf0a3e37ab..b1337d3a027 100644 --- a/addons/hr_evaluation/hr_evaluation_view.xml +++ b/addons/hr_evaluation/hr_evaluation_view.xml @@ -147,14 +147,14 @@ <field name="evaluation_date"/> </group> </xpath> - <xpath expr="//div[@name='button_box']" position="inside"> + <div name="button_box" position="inside"> <button name="%(act_hr_employee_2_hr__evaluation_interview)d" class="oe_stat_button" icon="fa-sitemap" type="action"> <field name="appraisal_count" widget="statinfo" string="Appraisals"/> </button> - </xpath> + </div> </field> </record> @@ -284,7 +284,7 @@ <field name="state" widget="statusbar" statusbar_visible="waiting_answer,done"/> </header> <sheet> - <div class="oe_right oe_button_box" name="button_box"> + <div class="oe_button_box" name="button_box"> <button string="Send Reminder Email" name="%(mail.action_email_compose_message_wizard)d" type="action" states="waiting_answer" context="{'default_body': 'Hello,\n\nKindly post your response for the survey interview.\n\nThanks', diff --git a/addons/hr_gamification/static/src/js/gamification.js b/addons/hr_gamification/static/src/js/gamification.js index 765acb257e4..a7a0201900a 100644 --- a/addons/hr_gamification/static/src/js/gamification.js +++ b/addons/hr_gamification/static/src/js/gamification.js @@ -1,11 +1,11 @@ odoo.define('hr_gamification.hr_gamification', function(require) { "use strict"; -var common = require('web_kanban.common'); +var KanbanRecord = require('web_kanban.Record'); -common.KanbanRecord.include({ +KanbanRecord.include({ on_card_clicked: function() { - if (this.view.dataset.model === 'gamification.badge.user') { + if (this.model === 'gamification.badge.user') { var action = { type: 'ir.actions.act_window', res_model: 'gamification.badge', diff --git a/addons/hr_gamification/views/gamification.xml b/addons/hr_gamification/views/gamification.xml index c3299a3c24b..3684cba5924 100644 --- a/addons/hr_gamification/views/gamification.xml +++ b/addons/hr_gamification/views/gamification.xml @@ -7,9 +7,9 @@ <field name="model">gamification.badge</field> <field name="inherit_id" ref="gamification.badge_form_view"/> <field name="arch" type="xml"> - <xpath expr="//div[@class='oe_right oe_button_box']" position="inside"> + <div name="button_box" position="inside"> <button string="Granted Employees" type="object" name="get_granted_employees" attrs="{'invisible': [('stat_count','=',0)]}" /> - </xpath> + </div> </field> </record> diff --git a/addons/hr_holidays/hr_holidays_view.xml b/addons/hr_holidays/hr_holidays_view.xml index e98d4234033..164cefa9c97 100644 --- a/addons/hr_holidays/hr_holidays_view.xml +++ b/addons/hr_holidays/hr_holidays_view.xml @@ -589,7 +589,7 @@ </div> </group> </xpath> - <xpath expr="//div[@name='button_box']" position="inside"> + <div name="button_box" position="inside"> <button name="%(act_hr_employee_holiday_request_approved)d" icon="fa-calendar" class="oe_stat_button" @@ -604,7 +604,7 @@ groups="base.group_hr_user"> <field name="leaves_count" widget="statinfo"/> </button> - </xpath> + </div> </field> </record> diff --git a/addons/hr_payroll/hr_payroll_view.xml b/addons/hr_payroll/hr_payroll_view.xml index 9114e4bd684..0874c6e61b1 100644 --- a/addons/hr_payroll/hr_payroll_view.xml +++ b/addons/hr_payroll/hr_payroll_view.xml @@ -221,7 +221,7 @@ <field name="state" widget="statusbar" statusbar_visible="draft,confirm"/> </header> <sheet> - <div class="oe_right oe_button_box"> + <div class="oe_button_box" name="button_box"> <button name="%(act_payslip_lines)d" class="oe_stat_button" icon="fa-money" @@ -391,7 +391,7 @@ <field name="inherit_id" ref="hr.view_employee_form"/> <field name="arch" type="xml"> <data> - <xpath expr="//div[@name='button_box']" position="inside"> + <div name="button_box" position="inside"> <button name="%(act_hr_employee_payslip_list)d" class="oe_stat_button" icon="fa-money" @@ -399,7 +399,7 @@ groups="base.group_hr_user"> <field name="payslip_count" widget="statinfo" string="Payslips"/> </button> - </xpath> + </div> </data> </field> </record> diff --git a/addons/hr_recruitment/hr_recruitment_dashboard.xml b/addons/hr_recruitment/hr_recruitment_dashboard.xml index 75d4a4564a1..e0965de2039 100644 --- a/addons/hr_recruitment/hr_recruitment_dashboard.xml +++ b/addons/hr_recruitment/hr_recruitment_dashboard.xml @@ -123,7 +123,7 @@ </div> </div> - <div t-if="widget.view.is_action_enabled('edit')" class="o_kanban_card_manage_settings row"> + <div t-if="widget.editable" class="o_kanban_card_manage_settings row"> <div class="col-xs-8"> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </div> diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index c3685c5f880..65c32f7eb3e 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -62,10 +62,12 @@ <field name="stage_id" widget="statusbar" clickable="True" options="{'fold_field': 'fold'}"/> </header> <sheet> - <div class="oe_right oe_button_box"> + <div class="oe_button_box" name="button_box"> <button name="action_makeMeeting" class="oe_stat_button" icon="fa-calendar" type="object" help="Schedule interview with this applicant"> - <div>Meetings</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Meetings</span> + </div> </button> <button name="action_start_survey" class="oe_stat_button" @@ -74,7 +76,10 @@ help="Answer related job question" context="{'survey_id': survey}" attrs="{'invisible':[('survey','=',False)]}"> - <div>Start<br/>Interview</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Start</span> + <span class="o_stat_text">Interview</span> + </div> </button> <button name="action_print_survey" class="oe_stat_button" @@ -82,7 +87,10 @@ type="object" help="Print interview report" attrs="{'invisible':[('survey','=',False)]}"> - <div>Print<br/>Interview</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Print</span> + <span class="o_stat_text">Interview</span> + </div> </button> <button name="action_get_attachment_tree_view" class="oe_stat_button" @@ -268,7 +276,7 @@ <div class="oe_dropdown_toggle oe_dropdown_kanban"> <i class="fa fa-bars fa-lg"/> <ul class="oe_dropdown_menu"> - <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> + <t t-if="widget.deletable"><li><a type="delete">Delete</a></li></t> <li><a name="action_makeMeeting" type="object">Schedule Interview</a></li> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> @@ -375,14 +383,11 @@ <field name="arch" type="xml"> <group name="employee_data" position="inside"> <label for="survey_id" groups="base.group_user"/> - <div groups="base.group_user"> - <field name="survey_id" class="oe_inline"/> - <button string="Print Interview" name="action_print_survey" type="object" attrs="{'invisible':[('survey_id','=',False)]}" class="oe_inline oe_link"/> - </div> - <label for="address_id"/> - <div> - <field name="address_id" context="{'show_address': 1}" domain= "[('is_company', '=', True )]"/> + <div groups="base.group_user" class="o_row"> + <field name="survey_id"/> + <button string="Print Interview" name="action_print_survey" type="object" attrs="{'invisible':[('survey_id','=',False)]}" class="oe_link"/> </div> + <field name="address_id" context="{'show_address': 1}" domain= "[('is_company', '=', True )]"/> </group> <group name="employee_data" position="after"> <notebook> @@ -392,46 +397,45 @@ </notebook> </group> <xpath expr="//field[@name='department_id']" position="after"> - <label for="alias_name" string="Specific Email Address" attrs="{'invisible': [('alias_domain', '=', False)]}" help ="Define a specific contact address for this job position. If you keep it empty, the default email address will be used which is in human resources settings"/> - <div name="alias_def" attrs="{'invisible': [('alias_domain', '=', False)]}"> - <field name="alias_id" class="oe_read_only oe_inline" - string="Email Alias" required="0"/> - <div class="oe_edit_only oe_inline" name="edit_alias" style="display: inline;" > - <field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline" readonly="1"/> - </div> + <label for="alias_name" string="Specific Email Address" attrs="{'invisible': [('alias_domain', '=', False)]}" help="Define a specific contact address for this job position. If you keep it empty, the default email address will be used which is in human resources settings"/> + <div name="alias_def" attrs="{'invisible': [('alias_domain', '=', False)]}"> + <field name="alias_id" class="oe_read_only" string="Email Alias" required="0"/> + <div class="oe_edit_only" name="edit_alias"> + <field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline" readonly="1"/> </div> + </div> </xpath> <xpath expr="//field[@name='department_id']" position="after"> - <field name="user_id" class="oe_inline"/> - </xpath> - <xpath expr="//div[@name='buttons']" position="inside"> - <div class="oe_button_box" > - <button class="oe_stat_button" - icon="fa-file-o" - name="%(action_hr_job_applications)d" - context="{'default_user_id': user_id}" - type="action"> - <field name="application_count" widget="statinfo" string="Applications"/> - </button> - <button class="oe_stat_button" - icon="fa-users" - name="%(action_hr_job_no_employee)d" - type="action"> - <field name="no_of_employee" widget="statinfo" string="Employees"/> - </button> - <button class="oe_stat_button" - icon="fa-book" - name="action_get_attachment_tree_view" - type="object"> - <field name="documents_count" widget="statinfo" string="Documents"/> - </button> - <button class="oe_stat_button" type="action" - name="%(action_hr_job_sources)d" icon="fa-share" - context="{'default_job_id': active_id}"> - <div>Trackers</div> - </button> - </div> + <field name="user_id"/> </xpath> + <div name="button_box" position="inside"> + <button class="oe_stat_button" + icon="fa-file-o" + name="%(action_hr_job_applications)d" + context="{'default_user_id': user_id}" + type="action"> + <field name="application_count" widget="statinfo" string="Applications"/> + </button> + <button class="oe_stat_button" + icon="fa-users" + name="%(action_hr_job_no_employee)d" + type="action"> + <field name="no_of_employee" widget="statinfo" string="Employees"/> + </button> + <button class="oe_stat_button" + icon="fa-book" + name="action_get_attachment_tree_view" + type="object"> + <field name="documents_count" widget="statinfo" string="Documents"/> + </button> + <button class="oe_stat_button" type="action" + name="%(action_hr_job_sources)d" icon="fa-share" + context="{'default_job_id': active_id}"> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Trackers</span> + </div> + </button> + </div> </field> </record> @@ -502,6 +506,7 @@ <menuitem name="Recruitment" id="menu_hr_recruitment_root" + web_icon="hr_recruitment,static/description/icon.png" sequence="80"/> <menuitem id="menu_hr_recruitment_configuration" name="Configuration" parent="menu_hr_recruitment_root" @@ -695,4 +700,4 @@ </record> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/addons/hr_recruitment/static/src/js/job_position.js b/addons/hr_recruitment/static/src/js/job_position.js index 9361c66cf7d..214f161ad99 100644 --- a/addons/hr_recruitment/static/src/js/job_position.js +++ b/addons/hr_recruitment/static/src/js/job_position.js @@ -1,11 +1,11 @@ odoo.define('hr_recruitment.hr_recruitment', function(require) { "use strict"; -var common = require('web_kanban.common'); +var KanbanRecord = require('web_kanban.Record'); -common.KanbanRecord.include({ +KanbanRecord.include({ on_card_clicked: function() { - if (this.view.dataset.model === 'hr.job') { + if (this.model === 'hr.job') { this.$('.oe_applications a').first().click(); } else { this._super.apply(this, arguments); diff --git a/addons/hr_timesheet/hr_timesheet_view.xml b/addons/hr_timesheet/hr_timesheet_view.xml index 78636517250..b6026b8ae0e 100644 --- a/addons/hr_timesheet/hr_timesheet_view.xml +++ b/addons/hr_timesheet/hr_timesheet_view.xml @@ -118,10 +118,10 @@ <field name="invoice_on_timesheets"/> <label for="invoice_on_timesheets"/> </xpath> - <xpath expr='//div[@name="buttons"]' position='inside'> - <button class="oe_inline oe_stat_button" type="action" name="%(act_analytic_cost_revenue)d" + <div name="button_box" position="inside"> + <button class="oe_stat_button" type="action" name="%(act_analytic_cost_revenue)d" icon="fa-usd" string="Cost/Revenue" widget="statinfo"/> - </xpath> + </div> </field> </record> diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml index 29f79da1529..1ff77c6fe1c 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml @@ -87,12 +87,15 @@ <field name="state" widget="statusbar" statusbar_visible="new,confirm,done"/> </header> <sheet> - <div class="oe_right oe_button_box" name="buttons"> + <div class="oe_button_box" name="button_box"> <button type="action" name="%(act_hr_timesheet_sheet_sheet_2_hr_analytic_timesheet)d" class="oe_stat_button" icon="fa-clock-o"> - <div><strong><field name="total_timesheet" widget="float_time"/></strong><br/>Timesheet</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_value"><field name="total_timesheet" widget="float_time"/></span> + <span class="o_stat_text">Timesheet</span> + </div> </button> <button type="action" groups="base.group_hr_attendance" class="oe_stat_button" icon="fa-users" @@ -378,7 +381,7 @@ <field name="model">hr.employee</field> <field name="inherit_id" ref="hr_timesheet.hr_timesheet_employee_extd_form"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='button_box']" position="inside"> + <div name="button_box" position="inside"> <button name="%(act_hr_employee_2_hr_timesheet)d" type="action" class="oe_stat_button" @@ -386,7 +389,7 @@ groups="base.group_hr_manager"> <field name="timesheet_count" widget="statinfo" string="Timesheets"/> </button> - </xpath> + </div> </field> </record> diff --git a/addons/im_livechat/views/im_livechat_channel_views.xml b/addons/im_livechat/views/im_livechat_channel_views.xml index f4be00b331c..34d5bb66696 100644 --- a/addons/im_livechat/views/im_livechat_channel_views.xml +++ b/addons/im_livechat/views/im_livechat_channel_views.xml @@ -71,6 +71,14 @@ <field name="arch" type="xml"> <form> <sheet> + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" type="action" attrs="{'invisible':[('nbr_session','=', 0)]}" name="%(im_chat_session_action_from_channel)d" icon="fa-comments"> + <field string="Sessions" name="nbr_session" widget="statinfo"/> + </button> + <button name="action_view_rating" attrs="{'invisible':[('rating_percentage_satisfaction','=', -1)]}" class="oe_stat_button" type="object" icon="fa-smile-o"> + <field string="% Happy" name="rating_percentage_satisfaction" widget="statinfo"/> + </button> + </div> <field name="image" widget='image' class="oe_avatar oe_left" options='{"preview_image": "image_medium"}'/> <div class="oe_title"> <label for="name" class="oe_edit_only"/> @@ -83,14 +91,6 @@ <field name="are_you_inside" invisible="1"/> </div> </div> - <div class="oe_right oe_button_box" name="buttons"> - <button class="oe_inline oe_stat_button" type="action" attrs="{'invisible':[('nbr_session','=', 0)]}" name="%(im_chat_session_action_from_channel)d" icon="fa-comments"> - <field string="Sessions" name="nbr_session" widget="statinfo"/> - </button> - <button name="action_view_rating" attrs="{'invisible':[('rating_percentage_satisfaction','=', -1)]}" class="oe_stat_button" type="object" icon="fa-smile-o"> - <field string="% Happy" name="rating_percentage_satisfaction" widget="statinfo"/> - </button> - </div> <group> <group string="Operators"> <field name="user_ids" widget="many2many_kanban" nolabel="1" colspan="2"> diff --git a/addons/mail/views/mail_alias_views.xml b/addons/mail/views/mail_alias_views.xml index c4d1e67dd37..f399e51a7b4 100644 --- a/addons/mail/views/mail_alias_views.xml +++ b/addons/mail/views/mail_alias_views.xml @@ -9,8 +9,7 @@ <field name="arch" type="xml"> <form string="Alias"> <sheet> - <h2><field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline"/></h2> - <div class="oe_right oe_button_box"> + <div class="oe_button_box" name="button_box"> <button name="open_document" string="Open Document" type="object" class="oe_link" attrs="{'invisible': ['|', ('alias_model_id', '=', False), ('alias_force_thread_id', '=', 0)]}"/> @@ -18,6 +17,7 @@ type="object" class="oe_link" attrs="{'invisible': ['|', ('alias_parent_model_id', '=', False), ('alias_parent_thread_id', '=', 0)]}"/> </div> + <h2><field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline"/></h2> <group> <field name="alias_model_id"/> <field name="alias_force_thread_id"/> diff --git a/addons/mail/views/mail_template_views.xml b/addons/mail/views/mail_template_views.xml index 5ba30950ef0..a1d4d2dc1bf 100644 --- a/addons/mail/views/mail_template_views.xml +++ b/addons/mail/views/mail_template_views.xml @@ -7,30 +7,36 @@ <field name="arch" type="xml"> <form string="Templates"> <sheet> - <div class="oe_title"> - <label for="name" class="oe_edit_only"/><h1><field name="name" required="1"/></h1> - <group> - <field name="model_id" required="1" options="{'no_create': True}"/> - <field name="model" invisible="1"/> - </group> - </div> - <div class="oe_right oe_button_box" name="buttons"> + <div class="oe_button_box" name="button_box"> <field name="ref_ir_act_window" invisible="1"/> - <button class="oe_inline oe_stat_button" name="create_action" type="object" + <button class="oe_stat_button" name="create_action" type="object" attrs="{'invisible':[('ref_ir_act_window','!=',False)]}" icon="fa-plus" help="Display an option on related documents to open a composition wizard with this template"> - <div>Add<br/>Context Action</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Add</span> + <span class="o_stat_text">Context Action</span> + </div> </button> <button name="unlink_action" type="object" class="oe_stat_button" icon="fa-minus" attrs="{'invisible':[('ref_ir_act_window','=',False)]}" help="Remove the contextual action to use this template on related documents" widget="statinfo"> - <div>Remove<br/>Context Action</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Remove</span> + <span class="o_stat_text">Context Action</span> + </div> </button> - <button class="oe_inline oe_stat_button" name="%(wizard_email_template_preview)d" icon="fa-search-plus" string="Preview" + <button class="oe_stat_button" name="%(wizard_email_template_preview)d" icon="fa-search-plus" string="Preview" type="action" target="new" context="{'template_id':active_id}"/> </div> + <div class="oe_title"> + <label for="name" class="oe_edit_only"/><h1><field name="name" required="1"/></h1> + <group> + <field name="model_id" required="1" options="{'no_create': True}"/> + <field name="model" invisible="1"/> + </group> + </div> <notebook> <page string="Content"> <label for="subject"/> diff --git a/addons/marketing_campaign/marketing_campaign_view.xml b/addons/marketing_campaign/marketing_campaign_view.xml index 9442f30a219..de7359c8740 100644 --- a/addons/marketing_campaign/marketing_campaign_view.xml +++ b/addons/marketing_campaign/marketing_campaign_view.xml @@ -61,13 +61,16 @@ <field name="state" readonly="1" widget="statusbar" statusbar_visible="draft,running,done"/> </header> <sheet string="Campaign"> - <div class="oe_right oe_button_box" name="button_box"> + <div class="oe_button_box" name="button_box"> <button type="action" name="%(act_marketing_campaing_stat)d" class="oe_stat_button" icon="fa-keyboard-o" help="Marketing Campaign Statistics"> - <div>Campaign<br/>Statistics</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Campaign</span> + <span class="o_stat_text">Statistics</span> + </div> </button> <button type="action" name="%(act_marketing_campaing_segment_opened)d" diff --git a/addons/mass_mailing/static/src/js/mass_mailing.js b/addons/mass_mailing/static/src/js/mass_mailing.js index b43a138f2c2..3785cf87070 100644 --- a/addons/mass_mailing/static/src/js/mass_mailing.js +++ b/addons/mass_mailing/static/src/js/mass_mailing.js @@ -1,14 +1,14 @@ odoo.define('mass_mailing.mass_mailing', function (require) { var core = require('web.core'); -var common = require('web_kanban.common'); +var KanbanRecord = require('web_kanban.Record'); var KanbanView = require('web_kanban.KanbanView'); var _t = core._t; -common.KanbanRecord.include({ +KanbanRecord.include({ on_card_clicked: function (event) { - if (this.view.dataset.model === 'mail.mass_mailing.campaign') { + if (this.model === 'mail.mass_mailing.campaign') { this.$('.oe_mailings').click(); } else { this._super.apply(this, arguments); diff --git a/addons/mass_mailing/views/email_template.xml b/addons/mass_mailing/views/email_template.xml index 73fb508b44b..c2526fe3678 100644 --- a/addons/mass_mailing/views/email_template.xml +++ b/addons/mass_mailing/views/email_template.xml @@ -10,6 +10,12 @@ <field name="arch" type="xml"> <form string="Templates"> <sheet> + <div class="oe_button_box" name="button_box"> + <button name="%(mail.wizard_email_template_preview)d" string="Preview" + type="action" target="new" + context="{'template_id':active_id}"/> + <br /> + </div> <group> <group> <field name="name" required="True"/> @@ -19,14 +25,6 @@ <field name="model" invisible="True"/> <field name="use_default_to" invisible="1"/> </group> - <group> - <div class="oe_right oe_button_box" name="buttons"> - <button name="%(mail.wizard_email_template_preview)d" string="Preview" - type="action" target="new" - context="{'template_id':active_id}"/> - <br /> - </div> - </group> </group> <notebook> <page string="Body"> @@ -50,11 +48,11 @@ <div t-attf-class="oe_kanban_card oe_kanban_global_click oe_kanban_email_template"> <div class="oe_dropdown_toggle oe_dropdown_kanban"> <i class="fa fa-bars fa-lg"/> - <ul class="oe_dropdown_menu" t-if="widget.view.is_action_enabled('edit') or widget.view.is_action_enabled('delete')"> - <t t-if="widget.view.is_action_enabled('edit')"> + <ul class="oe_dropdown_menu" t-if="widget.editable or widget.deletable"> + <t t-if="widget.editable"> <li><a type="edit">Edit</a></li> </t> - <t t-if="widget.view.is_action_enabled('delete')"> + <t t-if="widget.deletable"> <li><a type="delete">Delete</a></li> </t> </ul> diff --git a/addons/mass_mailing/views/mass_mailing.xml b/addons/mass_mailing/views/mass_mailing.xml index 6b73230ca18..baebe825304 100644 --- a/addons/mass_mailing/views/mass_mailing.xml +++ b/addons/mass_mailing/views/mass_mailing.xml @@ -245,9 +245,9 @@ <field name="arch" type="xml"> <form string="Contact List"> <sheet> - <div class="oe_right oe_button_box" name="buttons"> + <div class="oe_button_box" name="button_box"> <button name="%(mass_mailing.action_view_mass_mailing_contacts_from_list)d" - type="action" icon="fa-user" class="oe_stat_button pull-right"> + type="action" icon="fa-user" class="oe_stat_button"> <field name="contact_nbr" string="Recipients" widget="statinfo"/> </button> </div> @@ -392,7 +392,7 @@ </strong></p> </div> <sheet> - <div class="oe_button_box pull-right" attrs="{'invisible': [('state', 'in', ('draft','test'))]}"> + <div class="oe_button_box" name="button_box" attrs="{'invisible': [('state', 'in', ('draft','test'))]}"> <button name="%(action_view_mail_mail_statistics_mailing)d" type="action" icon="fa-envelope-o" class="oe_stat_button" attrs="{'invisible': [('sent', '=', 0)]}" > @@ -499,7 +499,7 @@ <div class="oe_dropdown_toggle oe_dropdown_kanban"> <i class="fa fa-bars fa-lg"/> <ul class="oe_dropdown_menu"> - <t t-if="widget.view.is_action_enabled('delete')"> + <t t-if="widget.deletable"> <li><a type="delete">Delete</a></li> </t> </ul> @@ -705,43 +705,39 @@ <span><field name="stage_id" widget="statusbar" clickable="True"/></span> </header> <sheet> + <field name="total" invisible="1"/> + <div class="oe_button_box" name="button_box" + attrs="{'invisible': [('total', '=', 0)]}"> + <button name="%(action_view_mass_mailings_from_campaign)d" + type="action" class="oe_stat_button" icon="fa-envelope-o"> + <field name="total_mailings" widget="statinfo" string="Mailings"/> + </button> + <button name="%(action_view_mail_mail_statistics_mailing)d" type="action" + icon="fa-envelope-o" class="oe_stat_button" + attrs="{'invisible': [('sent', '=', 0)]}" > + <field name="sent" string="Emails Sent" widget="statinfo"/> + </button> + <button name="%(action_view_mass_mailing_contacts)d" + type="action" class="oe_stat_button"> + <field name="received_ratio" widget="percentpie" string="Received"/> + </button> + <button name="%(action_view_mass_mailing_contacts)d" + type="action" class="oe_stat_button"> + <field name="opened_ratio" widget="percentpie" string="Opened"/> + </button> + <button name="%(action_view_mass_mailing_contacts)d" + type="action" class="oe_stat_button"> + <field name="replied_ratio" widget="percentpie" string="Replied"/> + </button> + <button name="%(dropdb snipp)d" + type="action" class="oe_stat_button"> + <field name="clicks_ratio" widget="percentpie" string="Clicks"/> + </button> + </div> <group> - <group> - <field name="name" string="Campaign Name" on_change="on_change_campaign_name(name)"/> - <field name="user_id"/> - <field name="tag_ids" widget="many2many_tags"/> - </group> - <group> - <field name="total" invisible="1"/> - <div class="oe_right oe_button_box" name="buttons" - attrs="{'invisible': [('total', '=', 0)]}"> - <button name="%(action_view_mass_mailings_from_campaign)d" - type="action" class="oe_inline oe_stat_button" icon="fa-envelope-o"> - <field name="total_mailings" widget="statinfo" string="Mailings"/> - </button> - <button name="%(action_view_mail_mail_statistics_mailing)d" type="action" - icon="fa-envelope-o" class="oe_stat_button" - attrs="{'invisible': [('sent', '=', 0)]}" > - <field name="sent" string="Emails Sent" widget="statinfo"/> - </button> - <button name="%(action_view_mass_mailing_contacts)d" - type="action" class="oe_stat_button oe_inline"> - <field name="received_ratio" widget="percentpie" string="Received"/> - </button> - <button name="%(action_view_mass_mailing_contacts)d" - type="action" class="oe_stat_button oe_inline"> - <field name="opened_ratio" widget="percentpie" string="Opened"/> - </button> - <button name="%(action_view_mass_mailing_contacts)d" - type="action" class="oe_stat_button oe_inline"> - <field name="replied_ratio" widget="percentpie" string="Replied"/> - </button> - <button name="%(dropdb snipp)d" - type="action" class="oe_stat_button"> - <field name="clicks_ratio" widget="percentpie" string="Clicks"/> - </button> - </div> - </group> + <field name="name" string="Campaign Name" on_change="on_change_campaign_name(name)"/> + <field name="user_id"/> + <field name="tag_ids" widget="many2many_tags"/> </group> <notebook> <page string="Tracking"> @@ -787,10 +783,10 @@ <div class="oe_dropdown_toggle oe_dropdown_kanban"> <i class="fa fa-bars fa-lg"/> <ul class="oe_dropdown_menu"> - <t t-if="widget.view.is_action_enabled('edit')"> + <t t-if="widget.editable"> <li><a type="edit">Settings</a></li> </t> - <t t-if="widget.view.is_action_enabled('delete')"> + <t t-if="widget.deletable"> <li><a type="delete">Delete</a></li> </t> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml index 1913fe3e3ce..32c042f1604 100644 --- a/addons/mrp/mrp_view.xml +++ b/addons/mrp/mrp_view.xml @@ -1105,12 +1105,12 @@ <field name="inherit_id" ref="product.product_template_only_form_view"/> <field name="groups_id" eval="[(4, ref('mrp.group_mrp_user'))]"/> <field name="arch" type="xml"> - <div name="buttons" position="inside"> - <button class="oe_inline oe_stat_button" name="%(template_open_bom)d" type="action" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="%(template_open_bom)d" type="action" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-flask"> <field string="Bill of Materials" name="bom_count" widget="statinfo" /> </button> - <button class="oe_inline oe_stat_button" name="action_view_mos" type="object" + <button class="oe_stat_button" name="action_view_mos" type="object" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-list-alt"> <field string="Manufacturing" name="mo_count" widget="statinfo" /> </button> @@ -1124,12 +1124,12 @@ <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="groups_id" eval="[(4, ref('mrp.group_mrp_user'))]"/> <field name="arch" type="xml"> - <div name="buttons" position="inside"> - <button class="oe_inline oe_stat_button" name="action_view_bom" type="object" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="action_view_bom" type="object" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-flask"> <field string="Bill of Materials" name="bom_count" widget="statinfo" /> </button> - <button class="oe_inline oe_stat_button" name="%(act_product_mrp_production)d" type="action" + <button class="oe_stat_button" name="%(act_product_mrp_production)d" type="action" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-list-alt"> <field string="Manufacturing" name="mo_count" widget="statinfo" /> </button> diff --git a/addons/note/note_view.xml b/addons/note/note_view.xml index 06f313730be..dc49df48bb3 100644 --- a/addons/note/note_view.xml +++ b/addons/note/note_view.xml @@ -54,32 +54,32 @@ <templates> <t t-name="kanban-box"> - <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_global_click_edit oe_semantic_html_override oe_kanban_card"> - <div class="oe_dropdown_kanban oe_dropdown_toggle"> - <!-- dropdown menu --> - <i class="fa fa-bars fa-lg"/> - <ul class="oe_dropdown_menu"> - <li><a type="delete">Delete</a></li> - <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> - </ul> + <div t-attf-class="#{kanban_color(record.color.raw_value)} oe_kanban_global_click_edit oe_semantic_html_override oe_kanban_card"> + + <div class="o_dropdown_kanban dropdown"> + <a class="dropdown-toggle btn" data-toggle="dropdown" href="#" > + <span class="fa fa-bars fa-lg"/> + </a> + <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"> + <li><a type="delete">Delete</a></li> + <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> + </ul> </div> - <div style="display: inline" class="oe_dropdown_kanban"> + <span class="oe_dropdown_kanban"> <a name="onclick_note_is_done" type="object" t-if="record.open.raw_value"><i class="fa fa-check"/></a> <a name="onclick_note_not_done" type="object" t-if="!record.open.raw_value"><i class="fa fa-check"/></a> - </div> + </span> <!-- kanban note --> - <div t-attf-class="oe_kanban_content #{record.open.raw_value ? '' : 'note_text_line_through'}"> + <span t-attf-class="oe_kanban_content #{record.open.raw_value ? '' : 'note_text_line_through'}"> <!-- title --> <field name="name"/> - </div> - <div class="oe_clear"></div> + </span> + <div class="clearfix"></div> <field name="tag_ids"/> - <div class="oe_right"> <t t-foreach="record.message_follower_ids.raw_value" t-as="follower"> - <img t-att-src="kanban_image('res.partner', 'image_small', follower)" width="24" height="24" class="oe_kanban_avatar" t-att-data-member_id="follower"/> + <img t-att-src="kanban_image('res.partner', 'image_small', follower)" width="24" height="24" class="oe_kanban_avatar pull-right" t-att-data-member_id="follower"/> </t> - </div> - <div class="oe_clear"></div> + <div class="clearfix"></div> </div> </t> </templates> @@ -106,12 +106,14 @@ <field name="name">note.note.form</field> <field name="model">note.note</field> <field name="arch" type="xml"> - <form string="Note" class="oe_form_nomargin"> + <form string="Note" class="oe_form_nomargin o_note_form_view"> <header> - <field name="tag_ids" widget="many2many_tags" class="oe_inline" placeholder="Tags"/> + <field name="tag_ids" widget="many2many_tags" placeholder="Tags"/> <field name="stage_id" domain="[('user_id','=',uid)]" widget="statusbar" clickable="1"/> </header> - <field name="memo" widget="html" class="oe_memo" editor_height="450px" /> + <sheet> + <field name="memo" widget="html" class="oe_memo" editor_height="450px" /> + </sheet> <div class="oe_chatter"> <field name="message_follower_ids" widget="mail_followers"/> <field name="message_ids" widget="mail_thread"/> @@ -182,9 +184,9 @@ </field> </record> - <menuitem name="Notes" id="menu_note_notes" sequence="3" action="note.action_note_note"/> + <menuitem name="Notes" id="menu_note_notes" sequence="3" action="note.action_note_note" web_icon="note,static/description/icon.png"/> <menuitem name="Configuration" id="menu_note_configuration" sequence="100" parent="menu_note_notes" groups="base.group_configuration"/> <menuitem name="Stages" id="menu_notes_stage" parent="menu_note_configuration" action="note.action_note_stage" sequence="21" /> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml index 5af35bf7092..aeca4224ae7 100644 --- a/addons/point_of_sale/point_of_sale_view.xml +++ b/addons/point_of_sale/point_of_sale_view.xml @@ -806,14 +806,20 @@ </header> <sheet> - <div class="oe_right oe_button_box"> + <div class="oe_button_box" name="button_box"> <button class="oe_stat_button" name="%(action_pos_box_in)d" type="action" states="opened,closing_control" icon="fa-level-down"> - <div>Put<br/>Money In</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Put</span> + <span class="o_stat_text">Money In</span> + </div> </button> <button class="oe_stat_button" name="%(action_pos_box_out)d" type="action" states="opened,closing_control" icon="fa-level-up"> - <div>Take<br/>Money Out</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_text">Take</span> + <span class="o_stat_text">Money Out</span> + </div> </button> </div> <h1 class="oe_title"> diff --git a/addons/procurement/procurement_view.xml b/addons/procurement/procurement_view.xml index 5c3aad1c21e..6a28fbd58f7 100644 --- a/addons/procurement/procurement_view.xml +++ b/addons/procurement/procurement_view.xml @@ -34,9 +34,9 @@ <field name="state" readonly="1" widget="statusbar" statusbar_visible="draft,confirmed,running,done" /> </header> <sheet> - <div class="oe_right oe_button_box" name="button_box"> - <button name="do_view_procurements" class="oe_stat_button" icon="fa-bars" string="Group's Procurements" type="object"/> - </div> + <div class="oe_button_box" name="button_box"> + <button name="do_view_procurements" class="oe_stat_button" icon="fa-bars" string="Group's Procurements" type="object"/> + </div> <group> <group> <field name="product_id" on_change="onchange_product_id(product_id)"/> @@ -98,7 +98,7 @@ <field name="arch" type="xml"> <form string="Procurement group"> <sheet> - <div class="oe_right oe_button_box" name="button_box"> + <div class="oe_button_box" name="button_box"> <button name="%(do_view_procurements)d" string="Procurements" type="action"/> </div> <group> diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml index 4a245ed2e50..2a97f0d262a 100644 --- a/addons/product/product_view.xml +++ b/addons/product/product_view.xml @@ -19,7 +19,7 @@ <field string="Product Variant" name="product_variant_ids" filter_domain="['|', ('product_variant_ids.name','ilike',self), ('product_variant_ids.attribute_value_ids.attribute_id.name','ilike',self)]"/> <field name="company_id"/> <field name="pricelist_id" widget="selection" context="{'pricelist': self}" filter_domain="[]" groups="product.group_sale_pricelist"/> <!-- Keep widget=selection on this field to pass numeric `self` value, which is not the case for regular m2o widgets! --> - <group expand='0' string='Group by...'> + <group expand='0' name="group_by" string='Group by...'> <filter string='Category' domain="[]" context="{'group_by' : 'categ_id'}"/> <filter string='Default Unit of Measure' icon="terp-mrp" domain="[]" context="{'group_by' : 'uom_id'}"/> <filter string='Type' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'type'}"/> @@ -53,18 +53,15 @@ <field name='product_variant_count' invisible='1'/> <sheet> <field name="id" invisible="True"/> - <div class="oe_right oe_button_box" style="width: 300px;" name="buttons"> - </div> - <div class="oe_left" style="width: 500px;"> - <field name="image_medium" widget="image" class="oe_avatar oe_left"/> - <div class="oe_title" style="width: 390px;"> - <label class="oe_edit_only" for="name" string="Product Name"/> - <h1><field name="name" class="oe_inline"/></h1> - <div name="options" groups="base.group_user"> - <div> - <field name="sale_ok"/> - <label for="sale_ok"/> - </div> + <div class="oe_button_box" name="button_box"/> + <field name="image_medium" widget="image" class="oe_avatar"/> + <div class="oe_title"> + <label class="oe_edit_only" for="name" string="Product Name"/> + <h1><field name="name"/></h1> + <div name="options" groups="base.group_user"> + <div> + <field name="sale_ok"/> + <label for="sale_ok"/> </div> </div> </div> @@ -161,7 +158,7 @@ <field name="name" position="after"> <field name="product_tmpl_id" string="Product Template"/> </field> - <xpath expr="//group[@string='Group by...']" position="inside"> + <xpath expr="//group[@name='group_by']" position="inside"> <filter string='Product Template' name="template_id" domain="[]" context="{'group_by' : 'product_tmpl_id'}"/> </xpath> </field> @@ -255,14 +252,14 @@ <field name="barcode" attrs="{'invisible': [('product_variant_count', '>', 1)]}"/> <field name="default_code" attrs="{'invisible': [('product_variant_count', '>', 1)]}"/> </field> - <xpath expr="//page[@string='Sales']" position="after"> + <div name="button_box" position="inside"> + <button class="oe_stat_button" string="Variant Prices" name="%(variants_template_action)d" type="action" icon="fa-strikethrough"/> + <button class="oe_stat_button" name="%(product.product_variant_action)d" type="action" icon="fa-sitemap"> + <field string="List of Variants" name="product_variant_count" widget="statinfo" /> + </button> + </div> + <xpath expr="//page[@name='sales']" position="after"> <page name="variants" string="Variants"> - <div class="oe_right"> - <button class="oe_inline oe_stat_button" string="Variant Prices" name="%(variants_template_action)d" type="action" icon="fa-strikethrough"/> - <button class="oe_inline oe_stat_button" name="%(product.product_variant_action)d" type="action" icon="fa-sitemap"> - <field string="List of Variants" name="product_variant_count" widget="statinfo" /> - </button> - </div> <p class="oe_grey"> <strong>Warning</strong>: adding or deleting attributes will delete and recreate existing variants and lead @@ -291,8 +288,8 @@ <field name="product_variant_ids"/> <templates> <t t-name="kanban-box"> - <div class="oe_kanban_vignette oe_semantic_html_override"> - <a type="open"><img t-att-src="kanban_image('product.template', 'image_small', record.id.value)" class="oe_kanban_image"/></a> + <div class="o_kanban_two_column"> + <a type="open"><img t-att-src="kanban_image('product.template', 'image_small', record.id.value)" class="o_kanban_image"/></a> <div class="oe_kanban_details"> <h4> <a type="open"> @@ -867,4 +864,4 @@ </record> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/addons/project/project.py b/addons/project/project.py index b75a0e2fa95..fdffb68ed86 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -556,6 +556,12 @@ class task(osv.osv): res[task.id] = False return res + def _compute_displayed_image(self, cr, uid, ids, prop, arg, context=None): + res = {} + for line in self.browse(cr, uid, ids, context=context): + res[line.id] = line.attachment_ids and line.attachment_ids.filtered(lambda x: x.file_type_icon == 'webimage')[0] or None + return res + _columns = { 'active': fields.function(_is_template, store=True, string='Not a Template Task', type='boolean', help="This field is computed automatically and have the same behavior than the boolean 'active' field: if the task is linked to a template or unactivated project, it will be hidden unless specifically asked."), 'name': fields.char('Task Summary', track_visibility='onchange', size=128, required=True, select=True), @@ -592,7 +598,9 @@ class task(osv.osv): 'id': fields.integer('ID', readonly=True), 'color': fields.integer('Color Index'), 'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True), - } + 'attachment_ids': fields.one2many('ir.attachment', 'res_id', domain=lambda self: [('res_model', '=', self._name)], auto_join=True, string='Attachments'), + 'displayed_image_id': fields.function(_compute_displayed_image, relation='ir.attachment', type="many2one", string='Attachment'), + } _defaults = { 'stage_id': _get_default_stage_id, 'project_id': lambda self, cr, uid, ctx=None: ctx.get('default_project_id') if ctx is not None else False, diff --git a/addons/project/project_dashboard.xml b/addons/project/project_dashboard.xml index 1e5ccbd13bc..9d23ad05f40 100644 --- a/addons/project/project_dashboard.xml +++ b/addons/project/project_dashboard.xml @@ -27,7 +27,7 @@ <span><t t-esc="record.name.value"/></span> </div> <div t-if="record.alias_id.raw_value"> - <span><i class="fa fa-envelope-o"></i> <t t-esc="record.alias_id.value"/></span> + <span><i class="fa fa-envelope"></i> <t t-esc="record.alias_id.value"/></span> </div> </div> </div><div class="o_kanban_card_manage_pane o_invisible"> @@ -41,16 +41,14 @@ <div> <a name="%(action_view_task_history_cumulative_filter)d" type="action">Cumulative Flow</a> </div> - </div> - <div t-if="widget.view.is_action_enabled('edit')" class="o_project_kanban_colorpicker"> - <div class="col-xs-12"> - <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> + <div t-if="widget.editable" class="o_project_kanban_colorpicker"> + <ul class="oe_kanban_colorpicker" data-field="color"/> </div> </div> </div> <div class="o_project_kanban_manage"> <div class="text-left"> - <a class="o_kanban_manage_toggle_button">More <i class="fa fa-caret-down"/></a> + <a class="o_kanban_manage_toggle_button" href="#">More <i class="fa fa-caret-down"/></a> </div> </div> </div> diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 5861df6869a..36ff5bccf03 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -7,6 +7,7 @@ id="base.menu_main_pm" groups="group_project_manager,group_project_user" icon="fa-calendar" + web_icon="project,static/description/icon.png" sequence="50"/> <menuitem name="Search" @@ -88,6 +89,15 @@ </header> <sheet string="Project"> <field name="analytic_account_id" invisible="1" required="0"/> + <div class="oe_button_box" name="button_box" groups="base.group_user"> + <button class="oe_stat_button" type="action" attrs="{'invisible':[('use_tasks','=', False)]}" + name="%(act_project_project_2_project_task_all)d" icon="fa-tasks"> + <field string="Tasks" name="task_count" widget="statinfo" options="{'label_field': 'label_tasks'}"/> + </button> + <button class="oe_stat_button" name="attachment_tree_view" type="object" icon="fa-files-o"> + <field string="Documents" name="doc_count" widget="statinfo"/> + </button> + </div> <div class="oe_title"> <label for="name" class="oe_edit_only" string="Project Name"/> <h1> @@ -102,15 +112,6 @@ </div> </div> </div> - <div class="oe_right oe_button_box" name="buttons" groups="base.group_user"> - <button class="oe_inline oe_stat_button" type="action" attrs="{'invisible':[('use_tasks','=', False)]}" - name="%(act_project_project_2_project_task_all)d" icon="fa-tasks"> - <field string="Tasks" name="task_count" widget="statinfo" options="{'label_field': 'label_tasks'}"/> - </button> - <button class="oe_inline oe_stat_button" name="attachment_tree_view" type="object" icon="fa-files-o"> - <field string="Documents" name="doc_count" widget="statinfo"/> - </button> - </div> <notebook> <page string="Emails" attrs="{'invisible': [('alias_domain', '=', False)]}"> <group name="group_alias"> @@ -254,7 +255,6 @@ </record> <!-- Task --> - <record id="view_task_form2" model="ir.ui.view"> <field name="name">project.task.form</field> <field name="model">project.task</field> @@ -266,17 +266,15 @@ options="{'fold_field': 'fold'}"/> </header> <sheet string="Task"> - <div class="row"> - <h1 class="col-xs-11"> - <field class="oe_inline oe_left" name="priority" widget="priority"/> - <field name="name" placeholder="Task summary..." class="oe_inline"/> - </h1> - <div class="col-xs-1 text-right"> - <field name="kanban_state" class="oe_inline" widget="kanban_state_selection" + <div class="oe_title"> + <h1 class="o_row"> + <field name="priority" widget="priority"/> + <field name="name" placeholder="Task summary..."/> + <field name="kanban_state" widget="kanban_state_selection" options='{ "states_legend_field": "stage_id", "states_legend": {"normal": "legend_normal", "blocked": "legend_blocked", "done": "legend_done"}}'/> - </div> + </h1> </div> <group> <group> @@ -357,29 +355,35 @@ <field name="date_deadline"/> <field name="message_summary"/> <field name="tag_ids"/> + <field name="displayed_image_id"/> <templates> <t t-name="kanban-box"> <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click"> - <div class="oe_dropdown_toggle oe_dropdown_kanban" groups="base.group_user"> - <i class="fa fa-bars fa-lg"/> - <ul class="oe_dropdown_menu"> - <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit Task</a></li></t> - <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> + + <div class="o_dropdown_kanban dropdown" groups="base.group_user"> + <a class="dropdown-toggle btn" data-toggle="dropdown" href="#" > + <span class="fa fa-bars fa-lg"/> + </a> + <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"> + <t t-if="widget.editable"><li><a type="edit">Edit Task</a></li></t> + <t t-if="widget.deletable"><li><a type="delete">Delete</a></li></t> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> </div> - <div class="oe_kanban_content"> - <div><b><field name="name"/></b></div> <div> + <field name="tag_ids"/> + </div> + <div> + <strong><field name="name"/></strong> + </div> + <div class="text-muted"> <field name="project_id" invisible="context.get('default_project_id', False)"/><br/> <t t-if="record.date_deadline.raw_value and record.date_deadline.raw_value lt (new Date())" t-set="red">oe_kanban_text_red</t> <span t-attf-class="#{red || ''}"><i><field name="date_deadline"/></i></span> </div> - <div class="oe_kanban_footer_left" style="margin-top:5px;"> - <field name="tag_ids"/> - </div> <div class="oe_kanban_bottom_left"> + <field name="priority" widget="priority"/> <t t-raw="record.message_summary.raw_value"/> </div> <div class="oe_kanban_bottom_right"> @@ -387,10 +391,12 @@ <div class="pull-left" groups="base.group_user"> <field name="kanban_state" widget="kanban_state_selection" options='{"states_legend": {"normal": "legend_normal", "blocked": "legend_blocked", "done": "legend_done"}}'/> - <field name="priority" widget="priority"/> </div> </div> </div> + <div t-if="record.displayed_image_id.value"> + <field name="displayed_image_id" widget="attachment_image"/> + </div> <div class="oe_clear"></div> </div> </t> @@ -571,7 +577,7 @@ <p class="text-muted" colspan="2"> You can also give a tooltip about the use of the stars available in the kanban and form views. </p> - <label for="legend_priority" string=" " class="fa fa-star fa-lg oe_star_on oe_project_kanban_legend"/> + <label for="legend_priority" string=" " class="fa fa-star fa-lg o_star_on oe_project_kanban_legend"/> <field name="legend_priority" nolabel="1"/> <p class="text-muted" colspan="2"> @@ -685,4 +691,4 @@ <menuitem action="project_tags_action" id="menu_project_tags_act" parent="base.menu_definitions" groups="base.group_no_one"/> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/addons/project/res_partner_view.xml b/addons/project/res_partner_view.xml index ed52b8bcaf7..82448073bf1 100644 --- a/addons/project/res_partner_view.xml +++ b/addons/project/res_partner_view.xml @@ -10,17 +10,15 @@ <field name="priority" eval="50"/> <field name="groups_id" eval="[(4, ref('project.group_project_user'))]"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" type="action" name="%(action_view_task)d" + <div name="button_box" position="inside"> + <button class="oe_stat_button" type="action" name="%(action_view_task)d" context="{'search_default_partner_id': active_id, 'default_partner_id': active_id}" icon="fa-tasks"> <field string="Tasks" name="task_count" widget="statinfo"/> - </button> - - - </xpath> + </button> + </div> </field> </record> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/addons/project/static/src/js/project.js b/addons/project/static/src/js/project.js index 20927cebc2c..515918df0b0 100644 --- a/addons/project/static/src/js/project.js +++ b/addons/project/static/src/js/project.js @@ -1,14 +1,14 @@ odoo.define('project.update_kanban', function (require) { -var kanban_common = require('web_kanban.common'); +var KanbanRecord = require('web_kanban.Record'); var KanbanView = require('web_kanban.KanbanView'); var data = require('web.data'); var session = require('web.session'); -kanban_common.KanbanRecord.include({ +KanbanRecord.include({ on_card_clicked: function() { - if (this.view.dataset.model === 'project.project') { + if (this.model === 'project.project') { this.$('.o_project_kanban_boxes a').first().click(); } else { this._super.apply(this, arguments); diff --git a/addons/project/static/src/less/project_dashboard.less b/addons/project/static/src/less/project_dashboard.less index 9ce363aea0a..e47f36a4f0a 100644 --- a/addons/project/static/src/less/project_dashboard.less +++ b/addons/project/static/src/less/project_dashboard.less @@ -1,45 +1,37 @@ -.oe_kanban_view.o_kanban_dashboard.o_project_kanban { - - @odoo-brand-optional: #21b799; +.o_kanban_view.o_kanban_dashboard.o_project_kanban { .o_kanban_flexbox { - display: flex; + .o-flex-display(); position: relative; } - .oe_kanban_record { + .o_kanban_record { min-width: 300px; - max-width: 350px; - } - - /* - .oe_kanban_record:hover .o_project_kanban_manage { - visibility: visible; } - */ .o_project_kanban_main { - margin-bottom: 40px; // give space for manage section at the bottom - width: 75%; + // give space for manage section at the bottom + margin-bottom: 40px; + width: 70%; - .o_primary { - font-size: larger; + .o_kanban_card_content { + font-size: 12px; + .o_primary { + font-size: larger; + } } .o_project_kanban_manage { visibility: visible; position: absolute; bottom: 0; - margin-left: 10px; + margin-left: 16px; margin-bottom: 10px; } .o_project_kanban_colorpicker { padding: 8px; } .o_kanban_card_manage_pane { - border-color: lightgrey; - border-bottom-style: solid; - border-bottom-width: 1px; .o_kanban_card_manage_title { margin: 0; @@ -48,12 +40,16 @@ } .o_project_kanban_boxes { - display: flex; - flex-direction: column; - justify-content: flex-end; - align-content: stretch; + .o-flex-display(); + .o-flex-flow(column, nowrap); + .o-justify-content(flex-end); + .o-align-content(stretch); + + width: 30%; - width: 25%; + > a:hover { + text-decoration: none; + } .o_value { font-size: x-large; @@ -67,11 +63,11 @@ .o_project_kanban_box { text-align: center; padding: 15px 0 15px 0; - flex: 1 1 auto; - display: flex; - align-items: center; - flex-direction: column; - justify-content: center; + .o-flex(1, 1, auto); + .o-flex-display(); + .o-align-items(center); + .o-flex-flow(column, nowrap); + .o-justify-content(center); } .o_project_kanban_box:nth-child(even) { background-color: grey; @@ -80,4 +76,4 @@ background-color: @odoo-brand-optional; } } -} +}
\ No newline at end of file diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index cd4675a1ae3..b9b03b0bf60 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -36,7 +36,7 @@ <div name="project"> <field name="project_id" on_change="on_change_project(project_id)" class="oe_inline" context="{'default_use_issues':1}"/> <field name="can_escalate" invisible="1"/> - <button name="case_escalate" string="Escalate" type="object" class="oe_link" attrs="{'invisible':[('can_escalate','=',False)]}" groups="base.group_user"/> + <button name="case_escalate" string="Escalate" type="object" class="btn-default" attrs="{'invisible':[('can_escalate','=',False)]}" groups="base.group_user"/> </div> </group> <group> @@ -168,8 +168,8 @@ <div class="oe_dropdown_toggle oe_dropdown_kanban" groups="base.group_user"> <i class="fa fa-bars fa-lg"/> <ul class="oe_dropdown_menu"> - <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit Issue</a></li></t> - <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> + <t t-if="widget.editable"><li><a type="edit">Edit Issue</a></li></t> + <t t-if="widget.deletable"><li><a type="delete">Delete</a></li></t> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> </div> @@ -278,12 +278,12 @@ <field name="label_issues" class="oe_inline oe_input_align" attrs="{'invisible': [('use_issues', '=', False)]}"/> </div> </xpath> - <xpath expr='//div[@name="buttons"]' position='inside'> - <button class="oe_inline oe_stat_button" type="action" attrs="{'invisible':[('use_issues','=', False)]}" + <div name="button_box" position="inside"> + <button class="oe_stat_button" type="action" attrs="{'invisible':[('use_issues','=', False)]}" name="%(act_project_project_2_project_issue_all)d" icon="fa-bug"> <field string="Issues" name="issue_count" widget="statinfo" options="{'label_field': 'label_issues'}"/> </button> - </xpath> + </div> <xpath expr='//page[@name="project_stages"]' position="attributes"> <attribute name="attrs">{'invisible': [('use_tasks', '=', False),('use_issues','=',False)]}</attribute> </xpath> @@ -331,18 +331,16 @@ <field name="priority" eval="50"/> <field name="groups_id" eval="[(4, ref('project.group_project_user'))]"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" type="action" name="%(action_view_issues)d" + <div name="button_box" position="inside"> + <button class="oe_stat_button" type="action" name="%(action_view_issues)d" context="{'search_default_partner_id': active_id, 'default_partner_id': active_id}" attrs="{'invisible': [('customer', '=', False)]}" icon="fa-bug"> <field string="Issues" name="issue_count" widget="statinfo"/> </button> - - - </xpath> + </div> </field> </record> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/addons/project_timesheet/project_timesheet_view.xml b/addons/project_timesheet/project_timesheet_view.xml index 35b72c3773d..0d1d4cb7f99 100644 --- a/addons/project_timesheet/project_timesheet_view.xml +++ b/addons/project_timesheet/project_timesheet_view.xml @@ -40,10 +40,10 @@ <field name="effective_hours" widget="float_time" groups="project.group_time_work_estimation_tasks"/> </field> - <xpath expr='//div[@name="buttons"]' position="inside"> - <button class="oe_inline oe_stat_button" name="open_timesheets" type="object" attrs="{'invisible':[('invoice_on_timesheets','=',False)]}" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="open_timesheets" type="object" attrs="{'invisible':[('invoice_on_timesheets','=',False)]}" icon="fa-calendar" string="Timesheets"/> - </xpath> + </div> <xpath expr='//field[@name="partner_id"]' position='after'> <field name="pricelist_id" invisible="1"/> </xpath> diff --git a/addons/purchase/partner_view.xml b/addons/purchase/partner_view.xml index 060a7e3a2fa..a7a3d4a53ef 100644 --- a/addons/purchase/partner_view.xml +++ b/addons/purchase/partner_view.xml @@ -77,12 +77,12 @@ <field name="priority" eval="20"/> <field name="groups_id" eval="[(4, ref('purchase.group_purchase_user'))]"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" name="%(purchase.act_res_partner_2_purchase_order)d" type="action" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="%(purchase.act_res_partner_2_purchase_order)d" type="action" attrs="{'invisible': [('supplier', '=', False)]}" icon="fa-shopping-cart"> <field string="Purchases" name="purchase_order_count" widget="statinfo"/> </button> - </xpath> + </div> </field> </record> @@ -93,12 +93,12 @@ <field name="priority" eval="20"/> <field name="groups_id" eval="[(4, ref('account.group_account_user'))]"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" name="%(purchase.act_res_partner_2_supplier_invoices)d" type="action" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="%(purchase.act_res_partner_2_supplier_invoices)d" type="action" attrs="{'invisible': [('supplier', '=', False)]}" icon="fa-pencil-square-o" help="Supplier Bills"> <field string="Supplier Inv." name="supplier_invoice_count" widget="statinfo"/> </button> - </xpath> + </div> </field> </record> </data> diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml index af3de1e53ff..0b33340797b 100644 --- a/addons/purchase/purchase_view.xml +++ b/addons/purchase/purchase_view.xml @@ -694,12 +694,12 @@ <field name="inherit_id" ref="product.product_template_only_form_view"/> <field name="groups_id" eval="[(4, ref('purchase.group_purchase_user'))]"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" name="action_view_purchases" type="object" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="action_view_purchases" type="object" icon="fa-shopping-cart"> <field string="Purchases" name="purchase_count" widget="statinfo"/> </button> - </xpath> + </div> </field> </record> @@ -709,12 +709,12 @@ <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="groups_id" eval="[(4, ref('purchase.group_purchase_user'))]"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" name="action_view_purchases" type="object" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="action_view_purchases" type="object" icon="fa-shopping-cart"> <field string="Purchases" name="purchase_count" widget="statinfo"/> </button> - </xpath> + </div> </field> </record> diff --git a/addons/purchase/stock_view.xml b/addons/purchase/stock_view.xml index 1e40b0398cb..1021550734f 100644 --- a/addons/purchase/stock_view.xml +++ b/addons/purchase/stock_view.xml @@ -30,7 +30,7 @@ <field name="inherit_id" ref="purchase.purchase_order_form"/> <field name="arch" type="xml"> <xpath expr="//div[contains(@class, 'oe_title')]" position="before"> - <div class="oe_right oe_button_box" name="buttons"> + <div class="oe_button_box" name="button_box"> <button type="object" name="view_picking" class="oe_stat_button" diff --git a/addons/purchase_requisition/purchase_requisition_view.xml b/addons/purchase_requisition/purchase_requisition_view.xml index 9111e262484..14c45fc3cc6 100644 --- a/addons/purchase_requisition/purchase_requisition_view.xml +++ b/addons/purchase_requisition/purchase_requisition_view.xml @@ -39,11 +39,14 @@ <field name="state" widget="statusbar" statusbar_visible="draft,in_progress,open,done" statusbar_colors='{"in_progress":"blue"}'/> </header> <sheet> - <div class="oe_right oe_button_box" name="buttons"> + <div class="oe_button_box" name="button_box"> <button name="open_product_line" type="object" string="Product Lines" class="oe_stat_button" icon="fa-list" attrs="{'invisible': ['|', ('state', 'not in', ('open','done')), ('exclusive', '=', 'exclusive')]}" groups="purchase.group_advance_bidding"/> <button name="open_rfq" type="object" string="RFQs/Bids" class="oe_stat_button" icon="fa-list-alt" attrs="{'invisible': [('state', '=', 'draft')]}"/> + <button name="%(action_purchase_requisition_partner)d" type="action" + string="Request a Quotation" icon="gtk-execute" + attrs="{'invisible': ['|', ('line_ids','=',[]),('state', 'in', ('draft' ,'open','done'))]}"/> </div> <div class="pull-left"> <label for="name" class="oe_edit_only oe_inline"/> @@ -89,11 +92,6 @@ </group> </form> </field> - <div class="oe_right oe_button_box" style="margin-top: 10px"> - <button name="%(action_purchase_requisition_partner)d" type="action" - string="Request a Quotation" icon="gtk-execute" - attrs="{'invisible': ['|', ('line_ids','=',[]),('state', 'in', ('draft' ,'open','done'))]}"/> - </div> <separator string="Requests for Quotation"/> <field name="purchase_ids" options="{'reload_on_button': true}"> <tree string="Purchase Order"> diff --git a/addons/rating/static/src/js/rating.js b/addons/rating/static/src/js/rating.js index 87f16cd1a50..91447bd1be7 100644 --- a/addons/rating/static/src/js/rating.js +++ b/addons/rating/static/src/js/rating.js @@ -1,11 +1,11 @@ odoo.define('rating.kanban', function(require) { "use strict"; -var common = require('web_kanban.common'); +var KanbanRecord = require('web_kanban.Record'); -common.KanbanRecord.include({ +KanbanRecord.include({ on_card_clicked: function() { - if (this.view.dataset.model === 'rating.rating') { + if (this.model === 'rating.rating') { this.$('a').first().click(); } else { this._super.apply(this, arguments); diff --git a/addons/rating_project/views/project_view.xml b/addons/rating_project/views/project_view.xml index 8322dd0b096..8f56709d83f 100644 --- a/addons/rating_project/views/project_view.xml +++ b/addons/rating_project/views/project_view.xml @@ -8,7 +8,7 @@ <field name="inherit_id" ref="project.edit_project"/> <field name="priority" eval="20"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> + <div name="button_box" position="inside"> <button name="action_view_task_rating" attrs="{'invisible': ['|', '|', ('is_visible_happy_customer', '=', False), ('use_tasks','=', False), ('percentage_satisfaction_task', '=', -1)]}" class="oe_stat_button oe_percent" @@ -16,7 +16,7 @@ icon="fa-smile-o"> <field string="On tasks" name="percentage_satisfaction_task" widget="statinfo"/> </button> - </xpath> + </div> <xpath expr="//field[@name='privacy_visibility']" position="after"> <field name="is_visible_happy_customer"/> </xpath> @@ -38,4 +38,4 @@ </record> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/addons/rating_project_issue/views/project_issue_view.xml b/addons/rating_project_issue/views/project_issue_view.xml index 0f16b3bb460..660886e8b80 100644 --- a/addons/rating_project_issue/views/project_issue_view.xml +++ b/addons/rating_project_issue/views/project_issue_view.xml @@ -8,7 +8,7 @@ <field name="inherit_id" ref="project.edit_project"/> <field name="priority" eval="20"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> + <div name="button_box" position="inside"> <button name="action_view_issue_rating" attrs="{'invisible': ['|', '|', ('use_issues','=', False), ('is_visible_happy_customer','=', False), ('percentage_satisfaction_issue', '=', -1)]}" class="oe_stat_button oe_percent" @@ -16,7 +16,7 @@ icon="fa-smile-o"> <field string="On issues" name="percentage_satisfaction_issue" widget="statinfo"/> </button> - </xpath> + </div> </field> </record> diff --git a/addons/sale/res_partner_view.xml b/addons/sale/res_partner_view.xml index 2f08c015d2a..3c6fb2468f4 100644 --- a/addons/sale/res_partner_view.xml +++ b/addons/sale/res_partner_view.xml @@ -34,9 +34,7 @@ <field name="sale_order_count"/> </field> <xpath expr="//div[@class='oe_kanban_partner_links']" position="inside"> - <a name="%(sale.act_res_partner_2_sale_order)d" type="action" t-if="record.sale_order_count.value>0"> - <t t-esc="record.sale_order_count.value"/> Sales - </a> + <span t-if="record.sale_order_count.value>0" class="badge"><i class="fa fa-fw fa-strikethrough"/><t t-esc="record.sale_order_count.value"/></span> </xpath> </field> </record> @@ -48,16 +46,15 @@ <field name="priority" eval="20"/> <field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" type="action" name="%(sale.act_res_partner_2_sale_order)d" + <div name="button_box" position="inside"> + <button class="oe_stat_button" type="action" name="%(sale.act_res_partner_2_sale_order)d" attrs="{'invisible': [('customer', '=', False)]}" icon="fa-strikethrough"> <field string="Sales" name="sale_order_count" widget="statinfo"/> </button> - </xpath> + </div> </field> </record> </data> </openerp> - diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 79aa32f2075..8e20b5c5e36 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -143,14 +143,7 @@ <field name="state" widget="statusbar" statusbar_visible="draft,sent,progress,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}'/> </header> <sheet> - <div class="oe_title"> - <h1> - <label string="Quotation " attrs="{'invisible': [('state', 'not in', ('draft', 'sent'))]}"/> - <label string="Sales Order " attrs="{'invisible': [('state', 'in', ('draft', 'sent'))]}"/> - <field name="name" class="oe_inline" readonly="1"/> - </h1> - </div> - <div class="oe_right oe_button_box" name="buttons"> + <div class="oe_button_box" name="button_box"> <button name="action_view_invoice" type="object" class="oe_stat_button" @@ -159,6 +152,13 @@ <field name="invoice_count" widget="statinfo" string="Invoices"/> </button> </div> + <div class="oe_title"> + <h1> + <label string="Quotation " attrs="{'invisible': [('state', 'not in', ('draft', 'sent'))]}"/> + <label string="Sales Order " attrs="{'invisible': [('state', 'in', ('draft', 'sent'))]}"/> + <field name="name" class="oe_inline" readonly="1"/> + </h1> + </div> <group> <group> <field name="partner_id" on_change="onchange_partner_id(partner_id, context)" domain="[('customer','=',True)]" context="{'search_default_customer':1, 'show_address': 1}" options='{"always_reload": True}'/> @@ -591,12 +591,12 @@ <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" name="action_view_sales" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="action_view_sales" type="object" icon="fa-strikethrough"> <field string="Sales" name="sales_count" widget="statinfo" /> </button> - </xpath> + </div> </field> </record> @@ -606,12 +606,12 @@ <field name="inherit_id" ref="product.product_template_only_form_view"/> <field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" name="action_view_sales" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="action_view_sales" type="object" icon="fa-strikethrough"> <field string="Sales" name="sales_count" widget="statinfo" /> </button> - </xpath> + </div> </field> </record> diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 3f63289c592..92e4afe329c 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -29,21 +29,21 @@ <button attrs="{'invisible': [('probability', '>', 0)]}" string="New Quotation" name="%(sale_action_quotations_new)d" type="action" context="{'search_default_partner_id': partner_id, 'default_partner_id': partner_id}"/> </xpath> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" type="action" + <div name="button_box" position="inside"> + <button class="oe_stat_button" type="action" name="%(sale_action_quotations)d" icon="stat_button_icon fa fa-usd" context="{'search_default_partner_id': partner_id, 'default_partner_id': partner_id}"> <field name="sale_number" widget="statinfo" string="Quotes"/> </button> - <button class="oe_inline oe_stat_button" type="action" attrs="{'invisible': [('sale_amount_total', '=', 0)]}" + <button class="oe_stat_button" type="action" attrs="{'invisible': [('sale_amount_total', '=', 0)]}" name="%(sale_action_quotations)d" icon="stat_button_icon fa fa-usd" context="{'search_default_partner_id': partner_id, 'default_partner_id': partner_id}"> - <div> - <strong><field name="sale_amount_total" class="oe_inline" widget="monetary" options="{'currency_field': 'company_currency'}"/></strong> - <div><field name="sale_number" class="oe_inline"/> Orders</div> + <div class="o_form_field o_stat_info"> + <span class="o_stat_value"><field name="sale_amount_total" widget="monetary" options="{'currency_field': 'company_currency'}"/></span> + <span class="o_stat_text"> Orders</span> </div> </button> - </xpath> + </div> </data> </field> </record> diff --git a/addons/sales_team/sales_team_dashboard.xml b/addons/sales_team/sales_team_dashboard.xml index fa14cd91284..33e5a7062c5 100644 --- a/addons/sales_team/sales_team_dashboard.xml +++ b/addons/sales_team/sales_team_dashboard.xml @@ -47,7 +47,7 @@ </div> </div> - <div t-if="widget.view.is_action_enabled('edit')" class="o_kanban_card_manage_settings row"> + <div t-if="widget.editable" class="o_kanban_card_manage_settings row"> <div class="col-xs-8"> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </div> diff --git a/addons/sales_team/static/src/js/sales_team.js b/addons/sales_team/static/src/js/sales_team.js index 40f976a465a..0000fbdc7be 100644 --- a/addons/sales_team/static/src/js/sales_team.js +++ b/addons/sales_team/static/src/js/sales_team.js @@ -2,7 +2,7 @@ odoo.define('sales_team.update_kanban', function (require) { "use strict"; var KanbanView = require('web_kanban.KanbanView'); -var kanban_common = require('web_kanban.common'); +var KanbanRecord = require('web_kanban.Record'); var data = require('web.data'); var session = require('web.session'); @@ -40,9 +40,9 @@ KanbanView.include({ }, }); -kanban_common.KanbanRecord.include({ +KanbanRecord.include({ on_card_clicked: function() { - if (this.view.dataset.model === 'crm.team') { + if (this.model === 'crm.team') { this.$('.oe_kanban_crm_salesteams_list a').first().click(); } else { this._super.apply(this, arguments); diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml index a22e11d5945..f7c2bd06efd 100644 --- a/addons/stock/product_view.xml +++ b/addons/stock/product_view.xml @@ -222,18 +222,18 @@ <field name="model">product.product</field> <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> + <div name="button_box" position="inside"> <button class="oe_stat_button" name="%(product_open_quants)d" icon="fa-building-o" type="action" attrs="{'invisible':[('type', '=', 'service')]}" groups="stock.group_locations"> <field name="qty_available2" widget="statinfo"/> </button> - <button class="oe_inline oe_stat_button" string="Moves" name= "%(act_product_stock_move_open)d" type="action" attrs="{'invisible':[('type', '=', 'service')]}" groups="stock.group_stock_user" icon="fa-arrows-v"/> - <button class="oe_inline oe_stat_button" name="%(product_open_orderpoint)d" type="action" + <button class="oe_stat_button" string="Moves" name= "%(act_product_stock_move_open)d" type="action" attrs="{'invisible':[('type', '=', 'service')]}" groups="stock.group_stock_user" icon="fa-arrows-v"/> + <button class="oe_stat_button" name="%(product_open_orderpoint)d" type="action" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-refresh" string="Reordering Rules"/> - <button class="oe_inline oe_stat_button" name="action_view_routes" string="Routes" type="object" icon="fa-cogs" attrs="{'invisible':[('type', '=', 'service')]}" /> - </xpath> + <button class="oe_stat_button" name="action_view_routes" string="Routes" type="object" icon="fa-cogs" attrs="{'invisible':[('type', '=', 'service')]}" /> + </div> </field> </record> @@ -242,18 +242,18 @@ <field name="model">product.template</field> <field name="inherit_id" ref="product.product_template_only_form_view"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> + <div name="button_box" position="inside"> <button class="oe_stat_button" name="action_open_quants" icon="fa-building-o" type="object" attrs="{'invisible':[('type', '=', 'service')]}" groups="stock.group_locations"> <field name="qty_available2" widget="statinfo"/> </button> - <button class="oe_inline oe_stat_button" string="Moves" name= "action_view_stock_moves" type="object" attrs="{'invisible':[('type', '=', 'service')]}" groups="stock.group_stock_user" icon="fa-arrows-v"/> - <button class="oe_inline oe_stat_button" name="action_view_orderpoints" type="object" + <button class="oe_stat_button" string="Moves" name= "action_view_stock_moves" type="object" attrs="{'invisible':[('type', '=', 'service')]}" groups="stock.group_stock_user" icon="fa-arrows-v"/> + <button class="oe_stat_button" name="action_view_orderpoints" type="object" attrs="{'invisible':[('type', 'in', ('service','consu'))]}" icon="fa-refresh" string="Reordering Rules"/> - <button class="oe_inline oe_stat_button" name="action_view_routes" string="Routes" type="object" icon="fa-cogs" attrs="{'invisible':[('type', '=', 'service')]}" /> - </xpath> + <button class="oe_stat_button" name="action_view_routes" string="Routes" type="object" icon="fa-cogs" attrs="{'invisible':[('type', '=', 'service')]}" /> + </div> </field> </record> </data> diff --git a/addons/stock/stock_dashboard.xml b/addons/stock/stock_dashboard.xml index ddf35c7beb9..002a7fa3f87 100644 --- a/addons/stock/stock_dashboard.xml +++ b/addons/stock/stock_dashboard.xml @@ -100,7 +100,7 @@ </div> </div> - <div t-if="widget.view.is_action_enabled('edit')" class="o_kanban_card_manage_settings row"> + <div t-if="widget.editable" class="o_kanban_card_manage_settings row"> <div class="col-xs-8"> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </div> diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index e0659d35b82..331c3e0c9fe 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -195,7 +195,7 @@ <field name="model">stock.production.lot</field> <field name="arch" type="xml"> <form string="Serial Number"> - <div class="oe_button_box oe_right"> + <div class="oe_button_box" name="button_box"> <button name="action_traceability" icon="fa-arrow-up" class="oe_stat_button" string="Traceability" type="object" attrs="{'invisible': [('quant_ids','=',[])]}"/> </div> <div class="oe_title"> @@ -316,7 +316,7 @@ <field name="model">stock.location</field> <field name="arch" type="xml"> <form string="Stock Location"> - <div class="oe_right oe_button_box" name="buttons"> + <div class="oe_button_box" name="button_box"> <button string="Current Stock" class="oe_stat_button" icon="fa-building-o" name="%(location_open_quants)d" type="action" @@ -532,7 +532,7 @@ <field name="arch" type="xml"> <form string="Warehouse"> <sheet> - <div class="oe_right oe_button_box"> + <div class="oe_button_box" name="button_box"> <button name="view_all_routes_for_wh" string="Routes" icon="fa-refresh" @@ -703,7 +703,7 @@ <field name="state" widget="statusbar" statusbar_visible="draft,confirmed,partially_available,assigned,done" statusbar_colors='{"shipping_except":"red","invoice_except":"red","waiting_date":"blue"}'/> </header> <sheet> - <div class="oe_right oe_button_box" name="button_box"> + <div class="oe_button_box" name="button_box"> </div> <h1> <field name="name" class="oe_inline" attrs="{'invisible': [('name','=','/')]}" readonly="1"/> @@ -1564,7 +1564,7 @@ <field name="arch" type="xml"> <form string="Reordering Rules"> <sheet> - <div class="oe_right oe_button_box"> + <div class="oe_button_box" name="button_box"> <button name="action_view_proc_to_process" type="object" string="Procurement Orders to Process"/> </div> <group> @@ -1631,9 +1631,9 @@ <field name="route_ids" widget="many2many_tags"/> </group> </xpath> - <xpath expr="//div[@name='button_box']" position="inside"> + <div name="button_box" position="inside"> <button name="do_view_pickings" class="oe_stat_button" icon="fa-bars" string="Group's Pickings" type="object"/> - </xpath> + </div> <xpath expr="//field[@name='rule_id']" position="replace"> <field name="rule_id" domain="['|', ('location_id', '=', False), ('location_id', '=', location_id)]"/> <field name="partner_dest_id"/> @@ -1759,8 +1759,8 @@ <field eval="10" name="priority"/> <field name="arch" type="xml"> <form string="Quants" create="false" edit="false"> - <div class="oe_right oe_button_box"> - <button name="action_view_quant_history" type="object" string="Quant History"/> + <div class="oe_button_box" name="button_box"> + <button name="action_view_quant_history" type="object" string="Quant History"/> </div> <group> <field name="product_id"/> @@ -1861,11 +1861,12 @@ <field name="arch" type="xml"> <form string="Package"> <sheet> - <div class="oe_right oe_button_box"> - <button class="oe_inline" name="unpack" string="Unpack" type="object"/> - <button class="oe_inline oe_stat_button" name="%(action_picking_tree_all)d" + <div class="oe_button_box" name="button_box"> + <button name="unpack" string="Unpack" type="object"/> + <button class="oe_stat_button" name="%(action_picking_tree_all)d" string="Package Transfers" type="action" widget="statinfo" icon="fa-dropbox"/> + <button name="get_content_package" type="object" string="View Contained Packages content" icon="gtk-execute" attrs="{'invisible': [('children_ids','=',[])]}"/> </div> <div class="oe_title"> <label for="name" string="Package Reference" class="oe_edit_only"/> @@ -1895,9 +1896,6 @@ </tree> </field> - <div class="oe_right oe_button_box" style="margin-top: 10px"> - <button name="get_content_package" type="object" string="View Contained Packages content" icon="gtk-execute" attrs="{'invisible': [('children_ids','=',[])]}"/> - </div> <separator string="Contained Packages"/> <field name="children_ids"> <tree string="Contained Packages"> @@ -2050,9 +2048,9 @@ <field name="model">procurement.group</field> <field name="inherit_id" ref="procurement.procurement_group_form_view"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='button_box']" position="inside"> + <div name="button_box" position="inside"> <button name="%(do_view_pickings)d" string="Pickings" type="action"/> - </xpath> + </div> </field> </record> diff --git a/addons/stock_picking_wave/stock_picking_wave_view.xml b/addons/stock_picking_wave/stock_picking_wave_view.xml index b0902e51190..e79bf4b4c8f 100644 --- a/addons/stock_picking_wave/stock_picking_wave_view.xml +++ b/addons/stock_picking_wave/stock_picking_wave_view.xml @@ -11,7 +11,7 @@ <field name="state" widget="statusbar" statusbar_visible="in_progress,done"/> </header> <sheet> - <div class="oe_right oe_button_box"> + <div class="oe_button_box" name="button_box"> <button name="print_picking" string="Print all pickings" type="object"/> </div> <div class="oe_title"> diff --git a/addons/survey/views/survey_views.xml b/addons/survey/views/survey_views.xml index 284de9fc4a9..caa04d18d6f 100644 --- a/addons/survey/views/survey_views.xml +++ b/addons/survey/views/survey_views.xml @@ -35,8 +35,7 @@ <field name="state" widget="statusbar"/> </header> <sheet> - <div class="oe_right oe_button_box" name="button_box"> - </div> + <div class="oe_button_box" name="button_box"/> <group col="2"> <group> <field name="survey_id"/> @@ -184,11 +183,11 @@ <field name="public_url" /> <templates> <div t-name="kanban-box" t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click"> - <div class="oe_dropdown_toggle oe_dropdown_kanban" t-if="widget.view.is_action_enabled('edit')"> + <div class="oe_dropdown_toggle oe_dropdown_kanban" t-if="widget.editable"> <i class="fa fa-bars fa-lg"/> <ul class="oe_dropdown_menu"> <li><a type="edit">Edit Survey</a></li> - <li t-if="widget.view.is_action_enabled('delete')"><a type="delete">Delete</a></li> + <li t-if="widget.deletable"><a type="delete">Delete</a></li> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> </div> diff --git a/addons/web/static/src/js/framework/core.js b/addons/web/static/src/js/framework/core.js index 7bd4800ec98..30138102663 100644 --- a/addons/web/static/src/js/framework/core.js +++ b/addons/web/static/src/js/framework/core.js @@ -186,6 +186,11 @@ return { list_widget_registry: new Registry(), search_widgets_registry: new Registry(), search_filters_registry: new Registry(), + + // necessary to make the kanban view compatible between + // community and enterprise edition + one2many_view_registry: new Registry(), + }; diff --git a/addons/web/static/src/js/views/form_widgets.js b/addons/web/static/src/js/views/form_widgets.js index ee04f8376b4..760ea703d35 100644 --- a/addons/web/static/src/js/views/form_widgets.js +++ b/addons/web/static/src/js/views/form_widgets.js @@ -285,7 +285,7 @@ var Priority = FieldChar.extend({ }, render_value: function() { this.priorities = this.prepare_priority(); - this.$el.html(QWeb.render("Priority", {'widget': this})); + this.$el.html(QWeb.render("FormPriority", {'widget': this})); if (!this.get('readonly')){ this.$el.find('li').on('click', this.set_priority.bind(this)); } @@ -1584,6 +1584,7 @@ var StatInfo = common.AbstractField.extend({ } } this.$el.html(QWeb.render("StatInfo", options)); + this.$el.addClass('o_stat_info'); }, }); diff --git a/addons/web/static/src/js/widgets/pager.js b/addons/web/static/src/js/widgets/pager.js new file mode 100644 index 00000000000..c195aaf530d --- /dev/null +++ b/addons/web/static/src/js/widgets/pager.js @@ -0,0 +1,111 @@ +odoo.define('web.Pager', function (require) { +"use strict"; + +var Widget = require('web.Widget'); + +var direction = { + previous: -1, + next: 1, +}; + +var Pager = Widget.extend({ + template: "Pager", + + events: { + 'click .o-pager-previous': 'previous', + 'click .o-pager-next': 'next', + }, + + // pager goes from 1 to size (included). + // current value is current_min (if limit === 1) + // or the interval [current_min, current_min + limit[ if limit > 1 + init: function (parent, size, current_min, limit, options) { + this.state = { + size: size, + current_min: current_min, + limit: limit, + }; + Object.defineProperty(this.state, 'current_max', { + get: function() { + return Math.min(this.current_min + this.limit - 1, this.size); + } + }); + this.options = options || {}; + this._super(parent); + }, + + start: function () { + this.$content = this.$('.o-pager-value'); + this._render(); + return this._super(); + }, + + set_state: function (options) { + _.extend(this.state, options); + this._render(); + }, + + previous: function(e) { + this._change_selection(direction.previous); + }, + + next: function() { + this._change_selection(direction.next); + }, + + disable: function() { + this.$('button').prop("disabled", true); + }, + + enable: function() { + this.$('button').prop("disabled", false); + }, + + _render: function () { + var state; + var size = this.state.size; + var current_min = this.state.current_min; + var current_max = this.state.current_max; + var single_page = 1 === current_min && current_max === size; + + if (size === 0 || (single_page && this.options.single_page_hidden)) { + this.$el.hide(); + } else { + this.$el.show(); + + if (single_page) { + this.disable(); + } else { + this.enable(); + } + + if (this.state.limit === 1) { + state = "" + current_min + " / " + size; + } else { + state = "" + current_min + "-" + current_max + " / " + size; + } + this.$content.html(state); + } + }, + + _change_selection: function (direction) { + var size = this.state.size; + var current_min = this.state.current_min; + var limit = this.state.limit; + current_min = (current_min + limit*direction); + if (current_min > size) { + current_min = 1; + } else if ((current_min < 1) && (limit === 1)) { + current_min = size; + } else if ((current_min < 1) && (limit > 1)) { + current_min = size - ((size % limit) || limit) + 1; + } + this.state.current_min = current_min; + this.trigger('pager_changed', _.clone(this.state)); + this._render(); + }, +}); + +return Pager; + +});
\ No newline at end of file diff --git a/addons/web/static/src/less/enterprise_compatibility.less b/addons/web/static/src/less/enterprise_compatibility.less new file mode 100644 index 00000000000..1cfd5120c34 --- /dev/null +++ b/addons/web/static/src/less/enterprise_compatibility.less @@ -0,0 +1,195 @@ +// ------------------------------------------------------------------ +// Odoo +// ------------------------------------------------------------------ + +// This file is necessary to make the community edition of the Odoo web +// client compatible with the enterprise edition. + +// The problem is that we have two web clients in parallel, and every +// addons should behave properly with both. + +@odoo-brand-optional-disabled: #6ED0BC; // Specifically used for status bar status arrows +@odoo-brand-primary: #337ab7; +@odoo-brand-optional: @odoo-brand-primary; +@odoo-brand-secondary: #8f8f8f; +@odoo-form-group-cols: 12; +@odoo-horizontal-padding: 16px; +@odoo-list-grey: #e2e2e0; // FIXME: listview thead background color +@odoo-main-color-muted: #a8a8a8; +@odoo-main-text-color: #666666; +@odoo-webclient-background-color: @odoo-brand-secondary; +@odoo-view-background-color: red; +@odoo-shadow-color: #303030; +@brand-primary: @odoo-brand-primary; + +.oe-view-manager { + background-color: #f0eeee !important; +} + +.o_kanban_view { + background-color: #f0eeee; + .o_kanban_header { + color: black !important; + } + + .o_kanban_config i, .o_kanban_quick_add i { + color: #555 !important; + &:hover { + background-color: darken(#f0eeee, 10%) !important; + } + } + + .o_column_folded { + background-color: #f0eeee !important; + border: none !important; + span.o_column_title, span.o_column_unfold { + // color: white !important; + } + } + + .o_column_quick_create { + background-color: #f0eeee !important; + .o_kanban_add_column, .o_kanban_title { + color: black !important; + } + + } +} + +.oe_list { + background-color: white !important; +} + +.btn-icon { + .btn-default; +} + +.openerp .oe_avatar { + float: left; +} + +.openerp .oe_button_box { + float: right; +} + +@grid-gutter-width: 0; + +.oe-control-panel .row { + margin-left: 15px; + margin-right: 15px; +} + +.oe_form_field_one2many { + .oe-view-manager { + background-color: white; + + .o_kanban_view { + background-color: white; + + .o_kanban_record { + box-shadow: none; + } + } + } +} + +// Buttons containing only an icon +.o_icon_button { + background-color: transparent; + border: 0; + padding: 0; + outline: none; +} + +// Stars +.o_priority { + display: inline-block; + padding: 0; + margin: 0; + > a { + display: inline-block; + } + .o_star_on, .o_star_off { + color: @odoo-main-color-muted !important; + &:hover { + text-decoration: none; + } + } + .o_star_on { + color: gold !important; + } +} + +.o_text_overflow { + display: block; + max-width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: top; // To update display context changed by overflow:hidden +} + +.oe-o2m-control-panel { + background-color: white; +} + +.oe_formview { + .oe_button_box { + .oe_stat_button { + + .stat_button_icon { + display: inline-block; + } + + // padding: @odoo-statbutton-vpadding @odoo-statbutton-spacing @odoo-statbutton-vpadding 0px; + text-align: left; + + .o_button_icon { + // margin-left: @odoo-statbutton-spacing; // To create the button padding left (firefox bug) + display: inline-block; + vertical-align: middle; + &:before { + font-size: x-large; + vertical-align: middle; + } + + width: 30%; + } + + // .o_form_field_percent_pie { + // margin-left: @odoo-statbutton-spacing; // To create the button padding left (firefox bug) + // } + + .o_stat_info { // contains the value and text + display: inline-block; + vertical-align: middle; + + max-width: 70%; + // padding-right: @odoo-statbutton-spacing; + line-height: 1.2; + + .o_stat_value { + .o_text_overflow; + font-weight: 700; + color: @odoo-brand-primary; + } + .o_stat_text { + .o_text_overflow; + font-size: small; + + .o_stat_value { + display: inline; + } + } + } + + + // Some buttons only display text and directly use the WidgetButton template instead of StatInfo + > span { + .o_stat_info; // layout + font-size: small; + white-space: normal; // text on several lines if needed + } + } + } +} diff --git a/addons/web/static/src/less/utils.less b/addons/web/static/src/less/utils.less new file mode 100644 index 00000000000..8b8d02898e6 --- /dev/null +++ b/addons/web/static/src/less/utils.less @@ -0,0 +1,189 @@ +// ------------------------------------------------------------------ +// Margin +// ------------------------------------------------------------------ +.utils-margin-loop(@index) when (@index < 256) { + .o-mt@{index} {margin-top: @index px;} + .o-mb@{index} {margin-bottom: @index px;} + .o-mr@{index} {margin-right: @index px;} + .o-ml@{index} {margin-left: @index px;} + + .utils-margin-loop(@index * 2); +} +.utils-margin-loop(2); + +// ------------------------------------------------------------------ +// Padding +// ------------------------------------------------------------------ +.o-webclient-padding(@top: 0px, @right: @odoo-horizontal-padding, @bottom: 0px, @left: @odoo-horizontal-padding) { + padding-top: @top; + padding-right: @right; + padding-bottom: @bottom; + padding-left: @left; +} + +// ------------------------------------------------------------------ +// Caret +// ------------------------------------------------------------------ +.utils-caret-boilerplate() { + content: ""; + display: inline-block; + width: 0; + height: 0; + vertical-align: middle; +} +.o-caret-down(@caret-width: @caret-width-base) { + .utils-caret-boilerplate; + border-bottom: 0; + border-left: @caret-width solid transparent; + border-right: @caret-width solid transparent; + border-top: @caret-width solid; + -moz-transform: scale(0.9999); // Smooth the caret on firefox +} +.o-caret-up(@caret-width: @caret-width-base) { + .utils-caret-boilerplate; + border-bottom: @caret-width solid; + border-left: @caret-width solid transparent; + border-right: @caret-width solid transparent; + border-top: 0; + -moz-transform: scale(0.9999); // Smooth the caret on firefox +} +.o-caret-left(@caret-width: @caret-width-base) { + .utils-caret-boilerplate; + border-bottom: @caret-width solid transparent; + border-left: 0; + border-right: @caret-width solid; + border-top: @caret-width solid transparent; + -moz-transform: scale(0.9999); // Smooth the caret on firefox +} +.o-caret-right(@caret-width: @caret-width-base) { + .utils-caret-boilerplate; + border-bottom: @caret-width solid transparent; + border-left: @caret-width solid; + border-right: 0; + border-top: @caret-width solid transparent; + -moz-transform: scale(0.9999); // Smooth the caret on firefox +} + +// ------------------------------------------------------------------ +// Vendor prefixes +// ------------------------------------------------------------------ +// Flex +.o-flex-display() { + display: -ms-flexbox; + display: -moz-box; + display: -webkit-box; + display: -webkit-flex; + + display: flex; +} +.o-inline-flex-display() { + display: -ms-inline-flexbox; + display: -moz-inline-box; + display: -webkit-inline-box; + display: -webkit-inline-flex; + + display: inline-flex; +} +.o-flex-flow(@direction, @wrap) { + -ms-flex-flow: @direction @wrap; + -moz-flex-flow: @direction @wrap; + -webkit-flex-flow: @direction @wrap; + + flex-flow: @direction @wrap; +} +.o-flex(@grow, @schrink, @basis) { + -ms-flex: @grow @schrink @basis; + -moz-flex: @grow @schrink @basis; + -webkit-box-flex: @grow; + -webkit-flex: @grow @schrink @basis; + + flex: @grow @schrink @basis; +} +.o-justify-content(@justify) { + -moz-justify-content: @justify; + -webkit-justify-content: @justify; + + justify-content: @justify; +} +.o-align-content(@align) { + -webkit-align-content: @align; + + align-content: @align; +} +.o-align-items(@align) { + -webkit-align-items: @align; + + align-items: @align; +} +.o-align-self(@align) { + -webkit-align-self: @align; + + align-self: @align; +} + +// Background-size +// https://css-tricks.com/perfect-full-page-background-image/ +.o-background-size(@size) { + -webkit-background-size: @size; + -moz-background-size: @size; + -o-background-size: @size; + background-size: @size; +} + +// ------------------------------------------------------------------ +// Dropdowns +// ------------------------------------------------------------------ +.o-selected-li() { + li { + position: relative; + > a { + padding: 3px 25px; + color: @odoo-main-text-color; + &:hover { + color: darken(@odoo-main-text-color, 10%); + } + } + &.selected { + > a { + color: darken(@odoo-main-text-color, 10%); + font-weight: bold; + &:before { + font-family: FontAwesome; + position: absolute; + left: 6px; + content: "\f00c"; + } + } + } + } +} + +// ------------------------------------------------------------------ +// Select Appearance +// ------------------------------------------------------------------ +.o-appearance(@value) { + -webkit-appearance: @value; + -moz-appearance: @value; + appearance: @value; +} + +// ------------------------------------------------------------------ +// Transform +// ------------------------------------------------------------------ +.o-transform-origin(@x: 50%, @y: 50%, @z: 0) { + -ms-transform-origin: @x @y @z; + -webkit-transform-origin: @x @y @z; + -moz-transform-origin: @x @y @z; + transform-origin: @x @y @z; +} + +// ------------------------------------------------------------------ +// Position absolute +// ------------------------------------------------------------------ +.o-position-absolute(@top: auto, @right: auto, @bottom: auto, @left: auto) { + position: absolute; + top: @top; + left: @left; + bottom: @bottom; + right: @right; +}
\ No newline at end of file diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 00406837a47..52a7c8130a6 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1104,7 +1104,7 @@ </ul> </div> </t> -<t t-name="Priority"> +<t t-name="FormPriority"> <ul style="list-style: none; padding-left: 2px; display: inline-block;"> <t t-foreach="widget.priorities" t-as="rec" > <li t-att-data-value="rec.click_value" style="display: inline-block;"> @@ -1116,6 +1116,14 @@ </t> </ul> </t> +<t t-name="Priority"> + <div class="o_priority"> + <t t-foreach="widget.priorities" t-as="rec" > + <a t-if="!widget.get('readonly')" href="#" t-att-data-value="rec.click_value" t-att-title="rec.name" t-att-class="widget.get('value') gte rec.value and 'fa fa-star o_star_on' or 'fa fa-star o_star_off'"/> + <span t-if="widget.get('readonly')" t-att-data-value="rec.click_value" t-att-title="rec.name" t-att-class="widget.get('value') gte rec.value and 'fa fa-star o_star_on' or 'fa fa-star o_star_off'"/> + </t> + </div> +</t> <t t-name="FieldEmail"> <span class="oe_form_field oe_form_field_email" t-att-style="widget.node.attrs.style"> <a t-if="widget.get('effective_readonly')" href="#" class="oe_form_uri" target="_blank"/> @@ -1938,14 +1946,25 @@ <a href="javascript:void(0)"><t t-esc="text"/></a> </t> <t t-name="StatInfo"> - <strong><t t-esc="value"/></strong><br/><t t-esc="text"/> + <span class="o_stat_value"><t t-esc="value"/></span> + <span class="o_stat_text"><t t-esc="text"/></span> </t> + <button t-name="toggle_button" type="button" t-att-title="widget.string" style="box-shadow: none; white-space:nowrap;"> <img t-attf-src="#{prefix}/web/static/src/img/icons/#{widget.icon}.png" t-att-alt="widget.string"/> </button> + +<div t-name="Pager"> + <span class="o-pager-value"></span> + <span class="btn-group btn-group-sm"> + <button type="button" class="fa fa-chevron-left btn btn-icon o-pager-previous" accesskey="O"/> + <button type="button" class="fa fa-chevron-right btn btn-icon o-pager-next" accesskey="P"/> + </span> +</div> + <button t-name="FieldToggleBoolean" type="button" t-att-title="widget.string" class="oe_toggle_button"> <img t-attf-src="#{_s}/web/static/src/img/icons/#{widget.icon}" t-att-alt="widget.string"/> diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 3b935733230..b99979f2b04 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -73,6 +73,8 @@ <script type="text/javascript" src="/web/static/lib/select2/select2.js"></script> <link rel="stylesheet" href='/website/static/src/less/import_bootstrap.less'/> + <link rel="stylesheet" href="/web/static/src/less/enterprise_compatibility.less"/> + <link rel="stylesheet" href="/web/static/src/less/utils.less"/> <script src="/web/static/lib/bootstrap/js/affix.js"></script> <script src="/web/static/lib/bootstrap/js/alert.js"></script> <script src="/web/static/lib/bootstrap/js/button.js"></script> @@ -138,6 +140,7 @@ <script type="text/javascript" src="/web/static/src/js/widgets/notification.js"></script> <script type="text/javascript" src="/web/static/src/js/widgets/sidebar.js"></script> <script type="text/javascript" src="/web/static/src/js/widgets/progress_bar.js"></script> + <script type="text/javascript" src="/web/static/src/js/widgets/pager.js"></script> <script type="text/javascript" src="/web/static/src/js/widgets/systray_menu.js"></script> <script type="text/javascript" src="/web/static/src/js/widgets/user_menu.js"></script> <script type="text/javascript" src="/web/static/src/js/framework/dialog.js"></script> diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index 840597606dd..494fe5b10c6 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -577,11 +577,11 @@ background: #ffffff; } -.openerp .oe_popup_form .oe_kanban_buttons .oe_highlight { +.openerp .o_popup_form .oe_kanban_buttons .oe_highlight { color: #404040; background: none; } -.openerp .oe_popup_form .oe_kanban_buttons button.oe_highlight { +.openerp .o_popup_form .oe_kanban_buttons button.oe_highlight { background-color: #efefef; background-image: -webkit-gradient(linear, left top, left bottom, from(#efefef), to(#d8d8d8)); background-image: -webkit-linear-gradient(top, #efefef, #d8d8d8); @@ -593,7 +593,7 @@ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset; -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset; } -.openerp .oe_popup_form .oe_kanban_buttons button.oe_highlight:active { +.openerp .o_popup_form .oe_kanban_buttons button.oe_highlight:active { background-color: #e3e3e3; background-image: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6)); background-image: -webkit-linear-gradient(top, #e3e3e3, #f6f6f6); @@ -605,7 +605,7 @@ -webkit-box-shadow: none; -box-shadow: none; } -.openerp .oe_popup_form .oe_kanban_buttons button.oe_highlight:hover { +.openerp .o_popup_form .oe_kanban_buttons button.oe_highlight:hover { background-color: #f6f6f6; background-image: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3)); background-image: -webkit-linear-gradient(top, #f6f6f6, #e3e3e3); diff --git a/addons/web_kanban/static/src/css/kanban.sass b/addons/web_kanban/static/src/css/kanban.sass index b229c8c9d41..5b7d187d9a1 100644 --- a/addons/web_kanban/static/src/css/kanban.sass +++ b/addons/web_kanban/static/src/css/kanban.sass @@ -514,7 +514,7 @@ padding: 0px background: #ffffff .openerp - .oe_popup_form + .o_popup_form .oe_kanban_buttons .oe_highlight color: #404040 diff --git a/addons/web_kanban/static/src/js/compatibility.js b/addons/web_kanban/static/src/js/compatibility.js index 256086b9ef1..a8ecd06f8d6 100644 --- a/addons/web_kanban/static/src/js/compatibility.js +++ b/addons/web_kanban/static/src/js/compatibility.js @@ -1,15 +1,17 @@ odoo.define('web_kanban.compatibility', function (require) { "use strict"; -var common = require('web_kanban.common'); +var kanban_widgets = require('web_kanban.widgets'); +var KanbanRecord = require('web_kanban.Record'); +var KanbanColumn = require('web_kanban.Column'); var KanbanView = require('web_kanban.KanbanView'); return; openerp = window.openerp || {}; openerp.web_kanban = openerp.web_kanban || {}; -openerp.web_kanban.AbstractField = common.AbstractField; -openerp.web_kanban.KanbanGroup = common.KanbanGroup; -openerp.web_kanban.KanbanRecord = common.KanbanRecord; +openerp.web_kanban.AbstractField = kanban_widgets.AbstractField; +openerp.web_kanban.KanbanGroup = KanbanColumn; +openerp.web_kanban.KanbanRecord = KanbanRecord; openerp.web_kanban.KanbanView = KanbanView; }); diff --git a/addons/web_kanban/static/src/js/kanban_column.js b/addons/web_kanban/static/src/js/kanban_column.js new file mode 100644 index 00000000000..85333ffbe3f --- /dev/null +++ b/addons/web_kanban/static/src/js/kanban_column.js @@ -0,0 +1,245 @@ +odoo.define('web_kanban.Column', function (require) { +"use strict"; + +var core = require('web.core'); +var Dialog = require('web.Dialog'); +var form_common = require('web.form_common'); +var Widget = require('web.Widget'); +var quick_create = require('web_kanban.quick_create'); +var KanbanRecord = require('web_kanban.Record'); + +var _t = core._t; +var QWeb = core.qweb; +var RecordQuickCreate = quick_create.RecordQuickCreate; + +var KanbanColumn = Widget.extend({ + template: "KanbanView.Group", + + events: { + 'click .o_kanban_toggle_fold': function (e) { + e.preventDefault(); + e.stopPropagation(); + this.folded = true; + this.update_column(); + }, + 'click .o_column_edit': 'edit_column', + 'click .o_column_delete': 'delete_column', + 'click .o_kanban_quick_add': 'add_quick_create', + 'click .o_kanban_load_more': 'load_more', + }, + + custom_events: { + 'kanban_record_delete': 'delete_record', + 'cancel_quick_create': 'cancel_quick_create', + 'quick_create_add_record': 'quick_create_add_record', + }, + + init: function(parent, group_data, options, record_options) { + this._super(parent); + this.data_records = group_data.records; + this.dataset = group_data.dataset; + this.records = []; + this.title = group_data.title; + this.id = group_data.id; + this.folded = group_data.attributes.folded; + this.fields = options.fields; + this.values = group_data.values; + + this.quick_create = options.quick_create; + this.grouped_by_m2o = options.grouped_by_m2o; + this.editable = options.editable; + this.deletable = options.deletable; + this.records_editable = options.records_editable; + this.records_deletable = options.records_deletable; + this.relation = options.relation; + this.offset = 0; + this.remaining = this.dataset.size() - this.data_records.length; + + this.record_options = _.extend(_.clone(record_options), { + group_info: this.values, + }); + }, + + start: function() { + var self = this; + this.$header = this.$('.o_kanban_header'); + + for (var i = 0; i < this.data_records.length; i++) { + this.add_record(this.data_records[i], {no_update: true}); + } + this.$header.tooltip(); + + this.update_column(); + this.$el.sortable({ + connectWith: '.o_kanban_group', + revert: 150, + delay: 100, + items: '> .o_kanban_record', + helper: 'clone', + cursor: 'move', + over: function () { + self.folded = false; + self.update_column(); + }, + update: function (event, ui) { + var record = ui.item.data('record'); + var index = self.records.indexOf(record); + var test2 = $.contains(self.$el[0], record.$el[0]); + if (index >= 0 && test2) { + // resequencing records + record.$el.removeAttr('style'); // jqueryui sortable add display:block inline + self.trigger_up('kanban_column_resequence'); + } else if (index >= 0 && !test2) { + // removing record from this column + self.records.splice(self.records.indexOf(record), 1); + self.dataset.remove_ids([record.id]); + } else { + // adding record to this column + self.records.push(record); + record.setParent(self); + self.trigger_up('kanban_column_add_record', {record: record}); + } + self.update_column(); + } + }); + this.$el.click(function (event) { + if (self.$el.hasClass('o_column_folded')) { + event.preventDefault(); + self.folded = false; + self.update_column(); + } + }); + }, + + is_empty: function () { + return !this.records.length; + }, + + add_record: function (data, options) { + var record = new KanbanRecord(this, data, this.record_options); + this.records.push(record); + this.dataset.ids = _.uniq(this.dataset.ids.concat(record.id)); + if (options.position === 'before') { + record.insertAfter(this.quick_create_widget ? this.quick_create_widget.$el : this.$header); + } else { + var $load_more = this.$('.o_kanban_load_more'); + if ($load_more.length) { + record.insertBefore($load_more); + } else { + record.appendTo(this.$el); + } + } + if (!options.no_update) { + this.update_column(); + } + }, + + get_ids: function () { + var ids = []; + this.$('.o_kanban_record').each(function (index, r) { + ids.push($(r).data('record').id); + }); + return ids; + }, + + update_column: function () { + var title = this.folded ? this.title + ' (' + this.records.length + ')' : this.title; + this.$header.find('.o_column_title').text(title); + this.$header.find('.o-kanban-count').text(this.records.length); + + this.$el.toggleClass('o_column_folded', this.folded); + var tooltip; + if (this.remaining) { + tooltip = this.records.length + '/' + this.dataset.size() + _t(' records'); + } else { + tooltip = this.records.length + _t(' records'); + } + this.$header.tooltip().attr('data-original-title', tooltip); + if (!this.remaining) { + this.$('.o_kanban_load_more').remove(); + } else { + this.$('.o_kanban_load_more').html(QWeb.render('KanbanView.LoadMore', {widget:this})) + } + }, + + delete_column: function (event) { + event.preventDefault(); + var buttons = [ + { + text: _t("Ok"), + classes: 'btn-primary', + close: true, + click: this.trigger_up.bind(this, 'kanban_column_delete') + }, + {text: _t("Cancel"), close: true} + ]; + new Dialog(this, { + size: 'medium', + buttons: buttons, + $content: $('<div>', { + text: _t("Are you sure that you want to remove this column ?") + }), + }).open(); + }, + + edit_column: function (event) { + event.preventDefault(); + var dialog = new form_common.FormViewDialog(this, { + res_model: this.relation, + res_id: this.id, + title: _t("Edit Column"), + }).open(); + + dialog.on('record_saved', this, function () { + this.trigger_up('kanban_reload'); + }); + }, + + delete_record: function (event) { + event.stopped = false; + var self = this; + event.data.after = function cleanup () { + var index = self.records.indexOf(event.data.record); + self.records.splice(index, 1); + self.update_column(); + }; + }, + + add_quick_create: function () { + if (this.quick_create_widget) { + return; + } + var self = this; + var width = this.records.length ? this.records[0].$el.innerWidth() : this.$el.width() - 8; + this.quick_create_widget = new RecordQuickCreate(this, width); + this.quick_create_widget.insertAfter(this.$header); + this.quick_create_widget.$el.focusout(function () { + setTimeout(function() { + var hasFocus = !! (self.quick_create_widget.$(':focus').length > 0); + if (! hasFocus && self.quick_create_widget) { + self.cancel_quick_create(); + } + }, 10); + }); + + }, + + cancel_quick_create: function () { + this.quick_create_widget.destroy(); + this.quick_create_widget = undefined; + }, + + quick_create_add_record: function (event) { + this.trigger_up('column_add_record', event.data); + }, + + load_more: function (event) { + event.preventDefault(); + this.trigger_up('kanban_load_more'); + }, + +}); + +return KanbanColumn; + +}); diff --git a/addons/web_kanban/static/src/js/kanban_common.js b/addons/web_kanban/static/src/js/kanban_common.js deleted file mode 100644 index b63758aaa28..00000000000 --- a/addons/web_kanban/static/src/js/kanban_common.js +++ /dev/null @@ -1,928 +0,0 @@ -odoo.define('web_kanban.common', function (require) { -"use strict"; - -var core = require('web.core'); -var data = require('web.data'); -var form_common = require('web.form_common'); -var formats = require('web.formats'); -var framework = require('web.framework'); -var ProgressBar = require('web.ProgressBar'); -var pyeval = require('web.pyeval'); -var Registry = require('web.Registry'); -var session = require('web.session'); -var time = require('web.time'); -var utils = require('web.utils'); -var web_client = require('web.web_client'); -var Widget = require('web.Widget'); - -var QWeb = core.qweb; -var _t = core._t; -/** - * Interface to be implemented by kanban fields. - * - */ -var FieldInterface = { - /** - Constructor. - - parent: The widget's parent. - - field: A dictionary giving details about the field, including the current field's value in the - raw_value field. - - $node: The field <field> tag as it appears in the view, encapsulated in a jQuery object. - */ - init: function(parent, field, $node) {}, -}; - -/** - * Abstract class for classes implementing FieldInterface. - * - * Properties: - * - value: useful property to hold the value of the field. By default, the constructor - * sets value property. - * - */ -var AbstractField = Widget.extend(FieldInterface, { - /** - Constructor that saves the field and $node parameters and sets the "value" property. - */ - init: function(parent, field, $node) { - this._super(parent); - this.field = field; - this.$node = $node; - this.options = pyeval.py_eval(this.$node.attr("options") || '{}'); - this.set("value", field.raw_value); - }, -}); - -var Priority = AbstractField.extend({ - init: function(parent, field, $node) { - this._super.apply(this, arguments); - this.name = $node.attr('name'); - this.parent = parent; - }, - prepare_priority: function() { - var self = this; - var selection = this.field.selection || []; - var init_value = selection && selection[0][0] || 0; - var data = _.map(selection.slice(1), function(element, index) { - var value = { - 'value': element[0], - 'name': element[1], - 'click_value': element[0], - }; - if (index === 0 && self.get('value') == element[0]) { - value.click_value = init_value; - } - return value; - }); - return data; - }, - renderElement: function() { - this.record_id = this.parent.id; - this.priorities = this.prepare_priority(); - var readonly = this.field && this.field.readonly; - if (readonly){ - this.set('readonly', true); - } - this.$el = $(QWeb.render("Priority", {'widget': this})); - if (!readonly){ - this.$el.find('li').click(this.do_action.bind(this)); - } - }, - do_action: function(e) { - var self = this; - var li = $(e.target).closest( "li" ); - if (li.length) { - var value = {}; - value[self.name] = String(li.data('value')); - return self.parent.view.dataset._model.call('write', [[self.record_id], value, self.parent.view.dataset.get_context()]).done(self.reload_record.bind(self.parent)); - } - }, - reload_record: function() { - this.do_reload(); - }, -}); - -var KanbanSelection = AbstractField.extend({ - init: function(parent, field, $node) { - this._super.apply(this, arguments); - this.name = $node.attr('name'); - this.parent = parent; - }, - prepare_dropdown_selection: function() { - var self = this; - var data = []; - _.map(this.field.selection || [], function(res) { - var value = { - 'name': res[0], - 'tooltip': res[1], - 'state_name': res[1], - }; - var leg_opt = self.options && self.options.states_legend || null; - if (leg_opt && leg_opt[res[0]] && self.parent.group.values && self.parent.group.values[leg_opt[res[0]]]) { - value['state_name'] = self.parent.group.values[leg_opt[res[0]]]; - value['tooltip'] = self.parent.group.values[leg_opt[res[0]]]; - } - if (res[0] == 'normal') { value['state_class'] = 'oe_kanban_status'; } - else if (res[0] == 'done') { value['state_class'] = 'oe_kanban_status oe_kanban_status_green'; } - else { value['state_class'] = 'oe_kanban_status oe_kanban_status_red'; } - data.push(value); - }); - return data; - }, - renderElement: function() { - var self = this; - this.record_id = self.parent.id; - this.states = self.prepare_dropdown_selection(); - this.$el = $(QWeb.render("KanbanSelection", {'widget': self})); - this.$el.find('li').click(self.do_action.bind(self)); - }, - do_action: function(e) { - var self = this; - var li = $(e.target).closest( "li" ); - if (li.length) { - var value = {}; - value[self.name] = String(li.data('value')); - return self.parent.view.dataset._model.call('write', [[self.record_id], value, self.parent.view.dataset.get_context()]).done(self.reload_record.bind(self.parent)); - } - }, - reload_record: function() { - this.do_reload(); - }, -}); - - -/** - * Quick creation view. - * - * Triggers a single event "added" with a single parameter "name", which is the - * name entered by the user - * - * @class - * @type {*} - */ -var QuickCreate = Widget.extend({ - template: 'KanbanView.quick_create', - - /** - * close_btn: If true, the widget will display a "Close" button able to trigger - * a "close" event. - */ - init: function(parent, dataset, context, buttons) { - this._super(parent); - this._dataset = dataset; - this._buttons = buttons || false; - this._context = context || {}; - }, - start: function () { - var self = this; - self.$input = this.$el.find('input'); - self.$input.keyup(function(event){ - if(event.keyCode == 13){ - self.quick_add(); - } - }); - $(".oe_kanban_quick_create").focusout(function (e) { - var val = self.$el.find('input').val(); - if (/^\s*$/.test(val)) { self.trigger('close'); } - e.stopImmediatePropagation(); - }); - $(".oe_kanban_quick_create_add", this.$el).click(function () { - self.quick_add(); - self.focus(); - }); - $(".oe_kanban_quick_create_close", this.$el).click(function (ev) { - ev.preventDefault(); - self.trigger('close'); - }); - self.$input.keyup(function(e) { - if (e.keyCode == 27 && self._buttons) { - self.trigger('close'); - } - }); - }, - focus: function() { - this.$el.find('input').focus(); - }, - /** - * Handles user event from nested quick creation view - */ - quick_add: function () { - var self = this; - var val = this.$input.val(); - if (/^\s*$/.test(val)) { this.$el.remove(); return; } - this._dataset.call( - 'name_create', [val, new data.CompoundContext( - this._dataset.get_context(), this._context)]) - .then(function(record) { - self.$input.val(""); - self.trigger('added', record[0]); - }, function(error, event) { - event.preventDefault(); - return self.slow_create(); - }); - }, - slow_create: function() { - var self = this; - var popup = new form_common.SelectCreatePopup(this); - popup.select_element( - self._dataset.model, - { - title: _t("Create: ") + (this.string || this.name), - initial_view: "form", - disable_multiple_selection: true - }, - [], - {"default_name": self.$input.val()} - ); - popup.on("elements_selected", self, function(element_ids) { - self.$input.val(""); - self.trigger('added', element_ids[0]); - }); - } -}); - -var KanbanRecord = Widget.extend({ - template: 'KanbanView.record', - - events: { - 'click .o_kanban_manage_toggle_button': 'toggle_manage_pane', - }, - - init: function (parent, record) { - this._super(parent); - this.group = parent; - this.view = parent.view; - this.id = null; - this.set_record(record); - if (!this.view.state.records[this.id]) { - this.view.state.records[this.id] = { - folded: false - }; - } - this.state = this.view.state.records[this.id]; - this.fields = {}; - }, - set_record: function(record) { - var self = this; - this.id = record.id; - this.values = {}; - _.each(record, function(v, k) { - self.values[k] = { - value: v - }; - }); - this.record = this.transform_record(record); - }, - start: function() { - var self = this; - this._super(); - this.init_content(); - }, - init_content: function() { - var self = this; - self.sub_widgets = []; - this.$("[data-field_id]").each(function() { - self.add_widget($(this)); - }); - this.$el.data('widget', this); - this.bind_events(); - }, - transform_record: function(record) { - var self = this, - new_record = {}; - _.each(record, function(value, name) { - var r = _.clone(self.view.fields_view.fields[name] || {}); - if ((r.type === 'date' || r.type === 'datetime') && value) { - r.raw_value = time.auto_str_to_date(value); - } else { - r.raw_value = value; - } - r.value = formats.format_value(value, r); - new_record[name] = r; - }); - return new_record; - }, - renderElement: function() { - this.qweb_context = { - record: this.record, - widget: this, - read_only_mode: this.view.options.read_only_mode, - }; - for (var p in this) { - if (_.str.startsWith(p, 'kanban_')) { - this.qweb_context[p] = _.bind(this[p], this); - } - } - var $el = QWeb.render(this.template, { - 'widget': this, - 'content': this.view.qweb.render('kanban-box', this.qweb_context) - }); - this.replaceElement($el); - this.replace_fields(); - }, - replace_fields: function() { - var self = this; - this.$("field").each(function() { - var $field = $(this); - var $nfield = $("<span></span"); - var id = _.uniqueId("kanbanfield"); - self.fields[id] = $field; - $nfield.attr("data-field_id", id); - $field.replaceWith($nfield); - }); - }, - add_widget: function($node) { - var $orig = this.fields[$node.data("field_id")]; - var field = this.record[$orig.attr("name")]; - var type = field.type; - type = $orig.attr("widget") ? $orig.attr("widget") : type; - var obj = fields_registry.get(type); - var widget = new obj(this, field, $orig); - this.sub_widgets.push(widget); - widget.replace($node); - }, - bind_events: function() { - var self = this; - this.setup_color_picker(); - this.$el.find('[title]').each(function(){ - $(this).tooltip({ - delay: { show: 500, hide: 0}, - title: function() { - var template = $(this).attr('tooltip'); - if (!self.view.qweb.has_template(template)) { - return false; - } - return self.view.qweb.render(template, self.qweb_context); - }, - }); - }); - - // If no draghandle is found, make the whole card as draghandle (provided one can edit) - if (!this.$el.find('.oe_kanban_draghandle').length) { - this.$el.children(':first') - .toggleClass('oe_kanban_draghandle', this.view.is_action_enabled('edit')); - } - - this.$el.find('.oe_kanban_action').click(function(ev) { - ev.preventDefault(); - var $action = $(this), - type = $action.data('type') || 'button', - method = 'do_action_' + (type === 'action' ? 'object' : type); - if ((type === 'edit' || type === 'delete') && ! self.view.is_action_enabled(type)) { - self.view.open_record(self.id, true); - } else if (_.str.startsWith(type, 'switch_')) { - self.view.do_switch_view(type.substr(7)); - } else if (typeof self[method] === 'function') { - self[method]($action); - } else { - self.do_warn("Kanban: no action for type : " + type); - } - }); - - if (this.$el.find('.oe_kanban_global_click,.oe_kanban_global_click_edit').length) { - this.$el.on('click', function(ev) { - if (!ev.isTrigger && !$._data(ev.target, 'events')) { - var trigger = true; - var elem = ev.target; - var ischild = true; - var children = []; - while (elem) { - var events = $._data(elem, 'events'); - if (elem == ev.currentTarget) { - ischild = false; - } - if (ischild) { - children.push(elem); - if (events && events.click) { - // do not trigger global click if one child has a click event registered - trigger = false; - } - } - if (trigger && events && events.click) { - _.each(events.click, function(click_event) { - if (click_event.selector) { - // For each parent of original target, check if a - // delegated click is bound to any previously found children - _.each(children, function(child) { - if ($(child).is(click_event.selector)) { - trigger = false; - } - }); - } - }); - } - elem = elem.parentElement; - } - if (trigger) { - self.on_card_clicked(ev); - } - } - }); - } - }, - /* actions when user click on the block with a specific class - * open on normal view : oe_kanban_global_click - * open on form/edit view : oe_kanban_global_click_edit - */ - on_card_clicked: function(ev) { - if (this.$el.find('.oe_kanban_global_click').size() > 0 && this.$el.find('.oe_kanban_global_click').data('routing')) { - framework.redirect(this.$el.find('.oe_kanban_global_click').data('routing') + "/" + this.id); - } - else if (this.$el.find('.oe_kanban_global_click_edit').size()>0) - this.do_action_edit(); - else - this.do_action_open(); - }, - setup_color_picker: function() { - var self = this; - var $el = this.$el.find('ul.oe_kanban_colorpicker'); - if ($el.length) { - $el.html(QWeb.render('KanbanColorPicker', { - widget: this - })); - $el.on('click', 'a', function(ev) { - ev.preventDefault(); - var color_field = $(this).parents('.oe_kanban_colorpicker').first().data('field') || 'color'; - var data = {}; - data[color_field] = $(this).data('color'); - self.view.dataset.write(self.id, data, {}).done(function() { - self.record[color_field] = $(this).data('color'); - self.do_reload(); - }); - }); - } - }, - toggle_manage_pane: function(){ - this.$('.o_kanban_card_content').toggleClass('o_visible o_invisible'); - this.$('.o_kanban_card_manage_pane').toggleClass('o_visible o_invisible'); - this.$('.o_kanban_manage_button_section').toggleClass(this.kanban_color((this.values['color'] && this.values['color'].value) || 0)); - }, - - do_action_delete: function($action) { - var self = this; - function do_it() { - return $.when(self.view.dataset.unlink([self.id])).done(function() { - self.group.remove_record(self.id); - self.destroy(); - }); - } - if (this.view.options.confirm_on_delete) { - if (confirm(_t("Are you sure you want to delete this record ?"))) { - return do_it(); - } - } else - return do_it(); - }, - do_action_edit: function($action) { - this.view.open_record(this.id, true); - }, - do_action_open: function($action) { - this.view.open_record(this.id); - }, - do_action_object: function ($action) { - var button_attrs = $action.data(); - var node_context = button_attrs.context || {}; - var context = new data.CompoundContext(node_context); - context.set_eval_context({ - active_id: this.id, - active_ids: [this.id], - active_model: this.view.dataset.model - }); - var action_data = _.extend({}, button_attrs, {context: context}); - this.view.do_execute_action(action_data, this.view.dataset, this.id, this.do_reload); - }, - do_action_url: function($action) { - return framework.redirect($action.attr("href")); - }, - do_reload: function() { - var self = this; - this.view.dataset.read_ids([this.id], this.view.fields_keys.concat(['__last_update'])).done(function(records) { - _.each(self.sub_widgets, function(el) { - el.destroy(); - }); - self.sub_widgets = []; - if (records.length) { - self.set_record(records[0]); - self.renderElement(); - self.init_content(); - self.group.compute_cards_auto_height(); - self.view.postprocess_m2m_tags(); - } else { - self.destroy(); - } - }); - }, - kanban_getcolor: function(variable) { - var index = 0; - switch (typeof(variable)) { - case 'string': - for (var i=0, ii=variable.length; i<ii; i++) { - index += variable.charCodeAt(i); - } - break; - case 'number': - index = Math.round(variable); - break; - default: - return ''; - } - var color = (index % this.view.number_of_color_schemes); - return color; - }, - kanban_color: function(variable) { - var color = this.kanban_getcolor(variable); - return color === '' ? '' : 'oe_kanban_color_' + color; - }, - kanban_image: function(model, field, id, cache, options) { - options = options || {}; - var url; - if (this.record[field] && this.record[field].value && !utils.is_bin_size(this.record[field].value)) { - url = 'data:image/png;base64,' + this.record[field].value; - } else if (this.record[field] && ! this.record[field].value) { - url = "/web/static/src/img/placeholder.png"; - } else { - id = JSON.stringify(id); - if (options.preview_image) - field = options.preview_image; - url = session.url('/web/binary/image', {model: model, field: field, id: id}); - if (cache !== undefined) { - // Set the cache duration in seconds. - url += '&cache=' + parseInt(cache, 10); - } - } - return url; - }, - kanban_text_ellipsis: function(s, size) { - size = size || 160; - if (!s) { - return ''; - } else if (s.length <= size) { - return s; - } else { - return s.substr(0, size) + '...'; - } - }, - kanban_compute_domain: function(domain) { - return data.compute_domain(domain, this.values); - } -}); - - -var KanbanGroup = Widget.extend({ - template: 'KanbanView.group_header', - init: function (parent, records, group, dataset) { - var self = this; - this._super(parent); - this.$has_been_started = $.Deferred(); - this.view = parent; - this.group = group; - this.dataset = dataset; - this.dataset_offset = 0; - this.value = this.title = this.values = null; - if (this.group) { - this.values = group.values; - this.value = group.get('value'); - this.title = group.get('value'); - if (this.value instanceof Array) { - this.title = this.value[1]; - this.value = this.value[0]; - } - var field = this.view.group_by_field; - if (!_.isEmpty(field)) { - try { - this.title = formats.format_value(group.get('value'), field, false); - } catch(e) {} - } - } - - if (this.title === false) { - this.title = _t('Undefined'); - this.undefined_title = true; - } - var key = this.view.group_by + '-' + this.value; - if (!this.view.state.groups[key]) { - this.view.state.groups[key] = { - folded: group ? group.get('folded') : false - }; - } - this.state = this.view.state.groups[key]; - this.$records = null; - - this.records = []; - this.$has_been_started.done(function() { - self.do_add_records(records); - }); - }, - start: function() { - var self = this, - def = this._super(); - if (! self.view.group_by) { - self.$el.addClass("oe_kanban_no_group"); - var QuickCreateClass = self.view.get_quick_create_class(); - self.quick = (new QuickCreateClass(this, self.dataset, {}, false)) - .on('added', self, self.proxy('quick_created')); - self.quick.replace($(".oe_kanban_no_group_qc_placeholder")); - } - this.$records = $(QWeb.render('KanbanView.group_records_container', { widget : this})); - this.$records.insertBefore(this.view.$el.find('.oe_kanban_groups_records td:last')); - - this.$el.on('click', '.oe_kanban_group_dropdown li a', function(ev) { - var fn = 'do_action_' + $(ev.target).data().action; - if (typeof(self[fn]) === 'function') { - self[fn]($(ev.target)); - } - }); - - this.$el.find('.oe_kanban_add').click(function () { - if (self.view.quick) { - self.view.quick.trigger('close'); - } - if (self.quick) { - return false; - } - self.view.$el.find('.oe_view_nocontent').hide(); - var ctx = {}; - ctx['default_' + self.view.group_by] = self.value; - self.quick = new (self.view.get_quick_create_class())(this, self.dataset, ctx, true) - .on('added', self, self.proxy('quick_created')) - .on('close', self, function() { - self.view.$el.find('.oe_view_nocontent').show(); - this.quick.destroy(); - delete self.view.quick; - delete this.quick; - }); - self.quick.appendTo($(".oe_kanban_group_list_header", self.$records)); - self.quick.focus(); - self.view.quick = self.quick; - }); - // Add bounce effect on image '+' of kanban header when click on empty space of kanban grouped column. - this.$records.on('click', '.oe_kanban_show_more', this.do_show_more); - if (this.state.folded) { - this.do_toggle_fold(); - } - this.$el.data('widget', this); - this.$records.data('widget', this); - this.$has_been_started.resolve(); - var add_btn = this.$el.find('.oe_kanban_add'); - add_btn.tooltip({delay: { show: 500, hide:1000 }}); - this.$records.find(".oe_kanban_column_cards").click(function (ev) { - if (ev.target == ev.currentTarget) { - if (!self.state.folded) { - add_btn.openerpBounce(); - } - } - }); - this.is_started = true; - this.fetch_tooltip(); - return def; - }, - /* - * Form the tooltip, based on optional group_by_tooltip on the grouping field. - * This function goes through the arch of the view, finding the declaration - * of the field used to group. If group_by_tooltip is defined, use the previously - * computed values of the group to form the tooltip. */ - fetch_tooltip: function() { - var self = this; - if (! this.group) - return; - var options = null; - var recurse = function(node) { - if (node.tag === "field" && node.attrs.name == self.view.group_by) { - options = pyeval.py_eval(node.attrs.options || '{}'); - return; - } - _.each(node.children, function(child) { - recurse(child); - }); - }; - recurse(this.view.fields_view.arch); - if (options && options.group_by_tooltip) { - this.tooltip = _.compact( - _.union( - [this.title], - _.map( - options.group_by_tooltip, - function (key, value, list) { return self.values && self.values[value] && (key + "\n" + self.values[value]) || ''; })) - ).join('\n\n'); - this.$(".oe_kanban_group_title_text").attr("title", this.tooltip || this.title || ""); - } - }, - compute_cards_auto_height: function() { - // oe_kanban_no_auto_height is an empty class used to disable this feature - if (!this.view.group_by) { - var min_height = 0; - var els = []; - _.each(this.records, function(r) { - var $e = r.$el.children(':first:not(.oe_kanban_no_auto_height)').css('min-height', 0); - if ($e.length) { - els.push($e[0]); - min_height = Math.max(min_height, $e.outerHeight()); - } - }); - $(els).css('min-height', min_height); - } - }, - destroy: function() { - this._super(); - if (this.$records) { - this.$records.remove(); - } - }, - do_show_more: function(evt) { - var self = this; - var ids = self.view.dataset.ids.slice(0); - return this.dataset.read_slice(this.view.fields_keys.concat(['__last_update']), { - 'limit': self.view.limit, - 'offset': self.dataset_offset += self.view.limit - }).then(function(records) { - self.view.dataset.ids = ids.concat(_.difference(self.dataset.ids, ids)); - self.do_add_records(records); - self.compute_cards_auto_height(); - self.view.postprocess_m2m_tags(); - return records; - }); - }, - do_add_records: function(records, prepend) { - var self = this; - var $list_header = this.$records.find('.oe_kanban_group_list_header'); - var $show_more = this.$records.find('.oe_kanban_show_more'); - var $cards = this.$records.find('.oe_kanban_column_cards'); - - _.each(records, function(record) { - var rec = new KanbanRecord(self, record); - if (!prepend) { - rec.appendTo($cards); - self.records.push(rec); - } else { - rec.prependTo($cards); - self.records.unshift(rec); - } - }); - if ($show_more.length) { - var size = this.dataset.size(); - $show_more.toggle(this.records.length < size).find('.oe_kanban_remaining').text(size - this.records.length); - } - }, - remove_record: function(id, remove_from_dataset) { - for (var i = 0; i < this.records.length; i++) { - if (this.records[i]['id'] === id) { - this.records.splice(i, 1); - i--; - } - } - }, - do_toggle_fold: function(compute_width) { - this.$el.add(this.$records).toggleClass('oe_kanban_group_folded'); - this.state.folded = this.$el.is('.oe_kanban_group_folded'); - this.$("ul.oe_kanban_group_dropdown li a[data-action=toggle_fold]").text((this.state.folded) ? _t("Unfold") : _t("Fold")); - }, - do_action_toggle_fold: function() { - this.do_toggle_fold(); - }, - do_action_edit: function() { - var self = this; - self.do_action({ - res_id: this.value, - name: _t("Edit column"), - res_model: self.view.group_by_field.relation, - views: [[false, 'form']], - type: 'ir.actions.act_window', - target: "new", - flags: { - action_buttons: true, - headless: true, - } - }); - // to do : remove this horrible hack - var am = web_client.action_manager; - var form = am.dialog_widget.views.form.controller; - form.on("on_button_cancel", am.dialog, function() { return am.dialog.$dialog_box.modal('hide'); }); - form.on('record_saved', self, function() { - am.dialog.$dialog_box.modal('hide'); - self.view.do_reload(); - }); - }, - do_action_delete: function() { - var self = this; - if (confirm(_t("Are you sure to remove this column ?"))) { - (new data.DataSet(self, self.view.group_by_field.relation)).unlink([self.value]).done(function(r) { - self.view.do_reload(); - }); - } - }, - do_save_sequences: function() { - var self = this; - if (_.indexOf(this.view.fields_keys, 'sequence') > -1) { - var new_sequence = _.pluck(this.records, 'id'); - self.view.dataset.resequence(new_sequence); - } - }, - /** - * Handles a newly created record - * - * @param {id} id of the newly created record - */ - quick_created: function (record) { - var id = record, self = this; - self.view.remove_no_result(); - self.trigger("add_record"); - this.dataset.read_ids([id], this.view.fields_keys) - .done(function (records) { - self.view.dataset.ids.push(id); - self.do_add_records(records, true); - }); - }, - highlight: function(show){ - if(show){ - this.$el.addClass('oe_kanban_column_higlight'); - this.$records.addClass('oe_kanban_column_higlight'); - }else{ - this.$el.removeClass('oe_kanban_column_higlight'); - this.$records.removeClass('oe_kanban_column_higlight'); - } - } -}); - - -/** - * Kanban widgets: ProgressBar - * options - * - editable: boolean if current_value is editable - * - current_value: get the current_value from the field that must be present in the view - * - max_value: get the max_value from the field that must be present in the view - * - title: title of the gauge, displayed on top of the gauge - * - on_change: action to call when cliking and setting a value - */ -var KanbanProgressBar = AbstractField.extend({ - events: { - 'click': function() { - if(!this.readonly && this.progressbar.readonly) { - this.toggle_progressbar(); - } - } - }, - - init: function (parent, field, node) { - this._super(parent, field, node); - - var record = this.getParent().record; - this.progressbar = new ProgressBar(this, { - readonly: true, - value: record[this.options.current_value].raw_value, - max_value: record[this.options.max_value].raw_value, - title: this.options.title, - edit_max_value: this.options.edit_max_value, - }); - - this.readonly = !this.options.editable; - this.on_change = this.options.on_change; - }, - - start: function () { - var self = this; - - var def = this.progressbar.appendTo('<div>').done(function() { - self.progressbar.$el.addClass(self.$el.attr('class')); - self.replaceElement(self.progressbar.$el); - }); - - return $.when(this._super(), def).then(function() { - if(!self.readonly) { - var parent = self.getParent(); - self.progressbar.on('change:value change:max_value', self, function(e) { - var value = this.progressbar.get(this.progressbar.edit_max_value ? 'max_value' : 'value') || 0; - if(!isNaN(value)) { - parent.view.dataset.call(this.on_change, [parent.id, value]).then(function() { - self.toggle_progressbar(); - }); - } - }); - } - }); - }, - - toggle_progressbar: function() { - this.progressbar.readonly = !this.progressbar.readonly; - var $div = $('<div/>').insertAfter(this.$el); - this.progressbar.replace($div); - this.setElement(this.progressbar.$el); - }, -}); - -var fields_registry = new Registry(); - -fields_registry - .add('priority', Priority) - .add('kanban_state_selection', KanbanSelection) - .add("progress", KanbanProgressBar); - - -return { - AbstractField: AbstractField, - registry: fields_registry, - QuickCreate: QuickCreate, - KanbanRecord: KanbanRecord, - KanbanGroup: KanbanGroup, -}; - -}); diff --git a/addons/web_kanban/static/src/js/kanban_quick_create.js b/addons/web_kanban/static/src/js/kanban_quick_create.js new file mode 100644 index 00000000000..3268a2b1acd --- /dev/null +++ b/addons/web_kanban/static/src/js/kanban_quick_create.js @@ -0,0 +1,127 @@ +odoo.define('web_kanban.quick_create', function (require) { +"use strict"; + +var Widget = require('web.Widget'); + +var RecordQuickCreate = Widget.extend({ + template: "KanbanView.QuickCreate", + + events: { + 'click .o_kanban_cancel': 'cancel', + 'click .o_kanban_add': 'add_record', + 'keypress input': function (event) { + if (event.keyCode === 13) { + this.add_record(); + } + }, + 'keydown': function (event) { + if (event.keyCode === 27) { + this.trigger_up('cancel_quick_create'); + } + }, + }, + + init: function (parent, width) { + this._super(parent); + this.width = width; + }, + + start: function () { + this.$el.css({width: this.width}); + this.$input = this.$('input'); + this.$input.focus(); + }, + + cancel: function () { + this.trigger_up('cancel_quick_create'); + }, + + add_record: function () { + var value = this.$input.val(); + this.$input.val(''); + if (/^\s*$/.test(value)) { return; } + this.trigger_up('quick_create_add_record', {value: value}); + this.$input.focus(); + }, +}); + +var ColumnQuickCreate = Widget.extend({ + template: 'KanbanView.ColumnQuickCreate', + + events: { + 'click .o_kanban_add': function (event) { + console.log('click okanbanadd'); + event.stopPropagation(); + this.add_column(); + }, + 'click .o_kanban_quick_create': function (event) { + console.log('click quickcreate'); + event.stopPropagation(); + }, + 'keypress input': function (event) { + if (event.keyCode === 13) { + this.add_column(); + } + }, + 'click .o_kanban_cancel': function (event) { + this.folded = true; + this.update(); + }, + 'keydown': function (event) { + if (event.keyCode === 27) { + this.folded = true; + this.update(); + } + }, + }, + + init: function (parent) { + this._super(parent); + this.folded = true; + }, + + start: function () { + this.$header = this.$('.o_column_header'); + this.$quick_create = this.$('.o_kanban_quick_create'); + this.$input = this.$('input'); + this.$el.click(this.proxy('toggle')); + var self = this; + this.$el.focusout(function () { + setTimeout(function() { + var hasFocus = !! (self.$(':focus').length > 0); + if (! hasFocus) { + self.folded = true; + self.update(); + } + }, 10); + }); + }, + + toggle: function () { + this.folded = !this.folded; + this.update(); + if (!this.folded) { + this.$input.focus(); + } + }, + + update: function () { + this.$header.toggle(this.folded); + this.$quick_create.toggle(!this.folded); + }, + + add_column: function () { + var name = this.$input.val(); + this.$input.val(''); + if (/^\s*$/.test(name)) { return; } + this.trigger_up('quick_create_add_column', {value: name}); + this.$input.focus(); + } +}); + +return { + RecordQuickCreate: RecordQuickCreate, + ColumnQuickCreate: ColumnQuickCreate, +}; + +}); diff --git a/addons/web_kanban/static/src/js/kanban_record.js b/addons/web_kanban/static/src/js/kanban_record.js new file mode 100644 index 00000000000..9357978d20c --- /dev/null +++ b/addons/web_kanban/static/src/js/kanban_record.js @@ -0,0 +1,275 @@ +odoo.define('web_kanban.Record', function (require) { +"use strict"; + +var core = require('web.core'); +var data = require('web.data'); +var formats = require('web.formats'); +var framework = require('web.framework'); +var session = require('web.session'); +var time = require('web.time'); +var utils = require('web.utils'); +var Widget = require('web.Widget'); +var kanban_widgets = require('web_kanban.widgets'); + +var QWeb = core.qweb; +var fields_registry = kanban_widgets.registry; + +var KanbanRecord = Widget.extend({ + template: 'KanbanView.record', + + events: { + 'click .oe_kanban_action': 'on_kanban_action_clicked', + 'click .o_kanban_manage_toggle_button': 'toggle_manage_pane', + }, + + custom_events: { + 'kanban_update_record': 'update_record', + }, + + init: function (parent, record, options) { + this._super(parent); + + this.fields = options.fields; + this.editable = options.editable; + this.deletable = options.deletable; + this.draggable = options.draggable; + this.read_only_mode = options.read_only_mode; + this.model = options.model; + this.group_info = options.group_info; + this.qweb = options.qweb; + this.sub_widgets = []; + + this.init_content(record); + }, + + init_content: function (record) { + var self = this; + this.id = record.id; + this.values = {}; + _.each(record, function(v, k) { + self.values[k] = { + value: v + }; + }); + this.record = this.transform_record(record); + var qweb_context = { + record: this.record, + widget: this, + read_only_mode: this.read_only_mode, + }; + for (var p in this) { + if (_.str.startsWith(p, 'kanban_')) { + qweb_context[p] = _.bind(this[p], this); + } + } + this.content = this.qweb.render('kanban-box', qweb_context); + }, + + add_widgets: function () { + var self = this; + this.$("field").each(function() { + var $field = $(this); + var field = self.record[$field.attr("name")]; + var type = $field.attr("widget") || field.type; + var Widget = fields_registry.get(type); + var widget = new Widget(self, field, $field); + widget.replace($field); + self.sub_widgets.push(widget); + }); + }, + + start: function() { + this.add_widgets(); + }, + + renderElement: function () { + this._super(); + this.setup_color_picker(); + this.$el.addClass('o_kanban_record'); + this.$el.data('record', this); + if (this.$el.hasClass('oe_kanban_global_click') || this.$el.hasClass('oe_kanban_global_click_edit')) { + this.$el.on('click', this.proxy('on_global_click')); + } + }, + transform_record: function(record) { + var self = this; + var new_record = {}; + _.each(record, function(value, name) { + var r = _.clone(self.fields[name] || {}); + if ((r.type === 'date' || r.type === 'datetime') && value) { + r.raw_value = time.auto_str_to_date(value); + } else { + r.raw_value = value; + } + r.value = formats.format_value(value, r); + new_record[name] = r; + }); + return new_record; + }, + + update: function (record) { + _.invoke(this.sub_widgets, 'destroy'); + this.sub_widgets = []; + this.init_content(record); + this.renderElement(); + this.add_widgets(); + }, + + kanban_image: function(model, field, id, cache, options) { + options = options || {}; + var url; + if (this.record[field] && this.record[field].value && !utils.is_bin_size(this.record[field].value)) { + url = 'data:image/png;base64,' + this.record[field].value; + } else if (this.record[field] && ! this.record[field].value) { + url = "/web/static/src/img/placeholder.png"; + } else { + id = JSON.stringify(id); + if (options.preview_image) + field = options.preview_image; + url = session.url('/web/binary/image', {model: model, field: field, id: id}); + if (cache !== undefined) { + // Set the cache duration in seconds. + url += '&cache=' + parseInt(cache, 10); + } + } + return url; + }, + + kanban_getcolor: function(variable) { + if (typeof(variable) === 'number') { + return Math.round(variable) % 10; + } + if (typeof(variable) === 'string') { + var index = 0; + for (var i=0; i<variable.length; i++) { + index += variable.charCodeAt(i); + } + return index % 10; + } + return 0; + }, + + kanban_color: function(variable) { + var color = this.kanban_getcolor(variable); + return 'oe_kanban_color_' + color; + }, + + on_global_click: function (ev) { + if ($(ev.target).parents('.o_dropdown_kanban').length) { + return; + } + if (!ev.isTrigger) { + var trigger = true; + var elem = ev.target; + var ischild = true; + var children = []; + while (elem) { + var events = $._data(elem, 'events'); + if (elem == ev.currentTarget) { + ischild = false; + } + if (ischild) { + children.push(elem); + if (events && events.click) { + // do not trigger global click if one child has a click event registered + trigger = false; + } + } + if (trigger && events && events.click) { + _.each(events.click, function(click_event) { + if (click_event.selector) { + // For each parent of original target, check if a + // delegated click is bound to any previously found children + _.each(children, function(child) { + if ($(child).is(click_event.selector)) { + trigger = false; + } + }); + } + }); + } + elem = elem.parentElement; + } + if (trigger) { + this.on_card_clicked(ev); + } + } + }, + + /* actions when user click on the block with a specific class + * open on normal view : oe_kanban_global_click + * open on form/edit view : oe_kanban_global_click_edit + */ + on_card_clicked: function() { + if (this.$el.hasClass('oe_kanban_global_click_edit') && this.$el.data('routing')) { + framework.redirect(this.$el.data('routing') + "/" + this.id); + } else if (this.$el.hasClass('oe_kanban_global_click_edit')) { + this.trigger_up('kanban_record_edit', {id: this.id}); + } else { + this.trigger_up('kanban_record_open', {id: this.id}); + } + }, + + toggle_manage_pane: function(event){ + event.preventDefault(); + this.$('.o_kanban_card_content').toggleClass('o_visible o_invisible'); + this.$('.o_kanban_card_manage_pane').toggleClass('o_visible o_invisible'); + this.$('.o_kanban_manage_button_section').toggleClass(this.kanban_color((this.values['color'] && this.values['color'].value) || 0)); + }, + + on_kanban_action_clicked: function (ev) { + ev.preventDefault(); + + var $action = $(ev.currentTarget); + var type = $action.data('type') || 'button'; + + switch (type) { + case 'edit': + this.trigger_up('kanban_record_edit', {id: this.id}); + break; + case 'open': + this.trigger_up('kanban_record_open', {id: this.id}); + break; + case 'delete': + this.trigger_up('kanban_record_delete', {record: this}); + break; + case 'action': + case 'object': + this.trigger_up('kanban_do_action', $action.data()); + break; + default: + this.do_warn("Kanban: no action for type : " + type); + } + }, + + kanban_compute_domain: function(domain) { + return data.compute_domain(domain, this.values); + }, + + update_record: function (event) { + this.trigger_up('kanban_record_update', event.data); + }, + + setup_color_picker: function() { + var self = this; + var $colorpicker = this.$('ul.oe_kanban_colorpicker'); + if (!$colorpicker.length) { + return; + } + $colorpicker.html(QWeb.render('KanbanColorPicker', { + widget: this + })); + $colorpicker.on('click', 'a', function(ev) { + ev.preventDefault(); + var color_field = $colorpicker.data('field') || 'color'; + var data = {}; + data[color_field] = $(this).data('color'); + self.trigger_up('kanban_record_update', data); + }); + }, + +}); + +return KanbanRecord; + +}); diff --git a/addons/web_kanban/static/src/js/kanban_view.js b/addons/web_kanban/static/src/js/kanban_view.js index a488267752a..5d60fc1aeb8 100644 --- a/addons/web_kanban/static/src/js/kanban_view.js +++ b/addons/web_kanban/static/src/js/kanban_view.js @@ -4,24 +4,49 @@ odoo.define('web_kanban.KanbanView', function (require) { var core = require('web.core'); var data = require('web.data'); var Model = require('web.Model'); +var Dialog = require('web.Dialog'); +var form_common = require('web.form_common'); +var Pager = require('web.Pager'); var pyeval = require('web.pyeval'); var session = require('web.session'); var utils = require('web.utils'); var View = require('web.View'); -var kanban_common = require('web_kanban.common'); + +var KanbanColumn = require('web_kanban.Column'); +var quick_create = require('web_kanban.quick_create'); +var KanbanRecord = require('web_kanban.Record'); +var kanban_widgets = require('web_kanban.widgets'); var QWeb = core.qweb; -var _t = core._t; var _lt = core._lt; -var fields_registry = kanban_common.registry; -var KanbanGroup = kanban_common.KanbanGroup; +var _t = core._t; +var ColumnQuickCreate = quick_create.ColumnQuickCreate; +var fields_registry = kanban_widgets.registry; var KanbanView = View.extend({ - template: "KanbanView", - display_name: _lt('Kanban'), - default_nr_columns: 1, + display_name: _lt("Kanban"), view_type: "kanban", - number_of_color_schemes: 10, + className: "o_kanban_view", + mobile_friendly: true, + + custom_events: { + 'kanban_record_open': 'open_record', + 'kanban_record_edit': 'edit_record', + 'kanban_record_delete': 'delete_record', + 'kanban_do_action': 'open_action', + 'kanban_reload': 'do_reload', + 'kanban_column_resequence': function (event) { + this.resequence_column(event.target); + }, + 'kanban_record_update': 'update_record', + 'kanban_column_add_record': 'add_record_to_column', + 'kanban_column_delete': 'delete_column', + 'column_add_record': 'column_add_record', + 'quick_create_add_column': 'add_new_column', + 'kanban_load_more': 'load_more', + 'kanban_call_method': 'call_method', + }, + init: function (parent, dataset, view_id, options) { this._super(parent, dataset, view_id, options); _.defaults(this.options, { @@ -31,111 +56,42 @@ var KanbanView = View.extend({ "read_only_mode": false, "confirm_on_delete": true, }); - this.fields_view = {}; - this.fields_keys = []; - this.group_by = null; - this.group_by_field = {}; - this.grouped_by_m2o = false; - this.many2manys = []; - this.m2m_context = {}; - this.state = { - groups : {}, - records : {} - }; - this.groups = []; + + // qweb setup this.qweb = new QWeb2.Engine(); this.qweb.debug = session.debug; this.qweb.default_dict = _.clone(QWeb.default_dict); - this.has_been_loaded = $.Deferred(); - this.search_domain = this.search_context = this.search_group_by = null; - this.currently_dragging = {}; + + this.model = this.dataset.model; this.limit = options.limit || 40; - this.add_group_mutex = new utils.Mutex(); - }, - view_loading: function(r) { - return this.load_kanban(r); - }, - start: function() { - var self = this; - this._super.apply(this, arguments); - this.$el.on('click', '.oe_kanban_dummy_cell', function() { - if (self.$buttons) { - self.$buttons.find('.oe_kanban_add_column').openerpBounce(); - } - }); - }, - destroy: function() { - this._super.apply(this, arguments); - $('html').off('click.kanban'); - }, - get_quick_create_class: function () { - return kanban_common.QuickCreate; + this.grouped = undefined; + this.group_by_field = undefined; + this.default_group_by = undefined; + this.grouped_by_m2o = undefined; + this.relation = undefined; + this.is_empty = undefined; + this.many2manys = []; + this.m2m_context = {}; + this.widgets = []; + this.data = undefined; + this.model = dataset.model; + this.quick_creatable = options.quick_creatable; + this.no_content_msg = options.action && (options.action.get_empty_list_help || options.action.help); + }, - load_kanban: function(data) { - this.fields_view = data; - // use default order if defined in xml description - var default_order = this.fields_view.arch.attrs.default_order, - unsorted = !this.dataset._sort.length; - if (unsorted && default_order) { - this.dataset.set_sort(default_order.split(',')); - } - this.$el.addClass(this.fields_view.arch.attrs['class']); - this.$groups = this.$el.find('.oe_kanban_groups tr'); + view_loading: function(fvg) { + this.$el.addClass(fvg.arch.attrs.class); + this.fields_view = fvg; + this.default_group_by = fvg.arch.attrs.default_group_by; + this.fields_keys = _.keys(this.fields_view.fields); - this.add_qweb_template(); - this.has_been_loaded.resolve(); - this.trigger('kanban_view_loaded', data); - return $.when(); - }, - /** - * Render the buttons according to the KanbanView.buttons template and - * add listeners on it. - * Set this.$buttons with the produced jQuery element - * @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted - * $node may be undefined, in which case the ListView inserts them into this.options.$buttons - * or into a div of its template - */ - render_buttons: function($node) { - var display = false; - if (this.options.action_buttons !== false) { - display = this.is_action_enabled('create'); - } else if (!this.view_id && !this.options.read_only_mode) { - display = this.is_action_enabled('write') || this.is_action_enabled('create'); - } - this.$buttons = $(QWeb.render("KanbanView.buttons", {'widget': this, display: display})); - this.$buttons - .on('click', 'button.oe_kanban_button_new', this.do_add_record) - .on('click', '.oe_kanban_add_column', this.do_add_group); - // Important: This should be done after do_search is finished so that - // this.grouped_by_m2o is set - this.$buttons.find('.oe_alternative').toggle(this.grouped_by_m2o); - $node = $node || this.options.$buttons; - if ($node) { - this.$buttons.appendTo($node); - } else { - this.$('.oe_kanban_buttons').replaceWith(this.$buttons); - } - }, - _is_quick_create_enabled: function() { - if (!this.options.quick_creatable || !this.is_action_enabled('create')) - return false; - if (this.fields_view.arch.attrs.quick_create !== undefined) - return JSON.parse(this.fields_view.arch.attrs.quick_create); - return !! this.group_by; - }, - is_action_enabled: function(action) { - if (action === 'create' && !this.options.creatable) - return false; - return this._super(action); - }, - /* add_qweb_template */ - add_qweb_template: function() { + // add qweb templates for (var i=0, ii=this.fields_view.arch.children.length; i < ii; i++) { var child = this.fields_view.arch.children[i]; if (child.tag === "templates") { - this.transform_qweb_template(child); + transform_qweb_template(child, fvg, this.many2manys); this.qweb.add_template(utils.json_node_to_xml(child)); break; } else if (child.tag === 'field') { @@ -146,164 +102,130 @@ var KanbanView = View.extend({ } } }, - transform_qweb_template: function(node) { - var qweb_add_if = function(node, condition) { - if (node.attrs[QWeb.prefix + '-if']) { - condition = _.str.sprintf("(%s) and (%s)", node.attrs[QWeb.prefix + '-if'], condition); - } - node.attrs[QWeb.prefix + '-if'] = condition; - }; - // Process modifiers - if (node.tag && node.attrs.modifiers) { - var modifiers = JSON.parse(node.attrs.modifiers || '{}'); - if (modifiers.invisible) { - qweb_add_if(node, _.str.sprintf("!kanban_compute_domain(%s)", JSON.stringify(modifiers.invisible))); - } - } - switch (node.tag) { - case 'field': - var ftype = this.fields_view.fields[node.attrs.name].type; - ftype = node.attrs.widget ? node.attrs.widget : ftype; - if (ftype === 'many2many') { - if (_.indexOf(this.many2manys, node.attrs.name) < 0) { - this.many2manys.push(node.attrs.name); - } - node.tag = 'div'; - node.attrs['class'] = (node.attrs['class'] || '') + ' oe_form_field oe_tags'; - } else if (fields_registry.contains(ftype)) { - // do nothing, the kanban record will handle it - } else { - node.tag = QWeb.prefix; - node.attrs[QWeb.prefix + '-esc'] = 'record.' + node.attrs['name'] + '.value'; - } - break; - case 'button': - case 'a': - var type = node.attrs.type || ''; - if (_.indexOf('action,object,edit,open,delete,url'.split(','), type) !== -1) { - _.each(node.attrs, function(v, k) { - if (_.indexOf('icon,type,name,args,string,context,states,kanban_states'.split(','), k) != -1) { - node.attrs['data-' + k] = v; - delete(node.attrs[k]); - } - }); - if (node.attrs['data-string']) { - node.attrs.title = node.attrs['data-string']; - } - if (node.attrs['data-icon']) { - node.children = [{ - tag: 'img', - attrs: { - src: session.prefix + '/web/static/src/img/icons/' + node.attrs['data-icon'] + '.png', - width: '16', - height: '16' - } - }]; - } - if (node.tag == 'a' && node.attrs['data-type'] != "url") { - node.attrs.href = '#'; - } else { - node.attrs.type = 'button'; - } - node.attrs['class'] = (node.attrs['class'] || '') + ' oe_kanban_action oe_kanban_action_' + node.tag; - } - break; - } - if (node.children) { - for (var i = 0, ii = node.children.length; i < ii; i++) { - this.transform_qweb_template(node.children[i]); - } - } + + do_search: function(domain, context, group_by) { + this.search_domain = domain; + this.search_context = context; + this.group_by_field = group_by[0] || this.default_group_by; + this.grouped = group_by.length || this.default_group_by; + + var field = this.fields_view.fields[this.group_by_field]; + this.grouped_by_m2o = field && (field.type === 'many2one'); + this.relation = this.grouped_by_m2o ? field.relation : undefined; + + return this.load_data() + .then(this.proxy('render')) + .then(this.proxy('update_pager')); }, - do_add_record: function() { - this.dataset.index = null; - this.do_switch_view('form'); + + do_show: function() { + this.do_push_state({}); + return this._super(); }, - do_add_group: function() { + + do_reload: function() { + this.do_search(this.search_domain, this.search_context, [this.group_by_field]); }, - do_search: function(domain, context, group_by) { + + load_data: function () { var self = this; - this.search_domain = domain; - this.search_context = context; - this.search_group_by = group_by; - return $.when(this.has_been_loaded).then(function() { - self.group_by = group_by.length ? group_by[0] : self.fields_view.arch.attrs.default_group_by; - self.group_by_field = self.fields_view.fields[self.group_by] || {}; - self.grouped_by_m2o = (self.group_by_field.type === 'many2one'); - self.$el.toggleClass('oe_kanban_grouped_by_m2o', self.grouped_by_m2o); - var grouping_fields = self.group_by ? [self.group_by] : undefined; - if (!_.isEmpty(grouping_fields)) { - // ensure group_by fields are read. - self.fields_keys = _.unique(self.fields_keys.concat(grouping_fields)); - } - var grouping = new Model(self.dataset.model, context, domain).query(self.fields_keys).group_by(grouping_fields); - return self.alive($.when(grouping)).then(function(groups) { - self.remove_no_result(); - if (groups) { - return self.do_process_groups(groups); - } else { - return self.do_process_dataset(); - } - }); + var deferred = this.grouped ? this.load_groups() : this.load_records(); + return deferred.then(function (data) { + self.data = data; }); }, - do_process_groups: function(groups) { - var self = this; - // Check in the arch the fields to fetch on the stage to get tooltips data. - // Fetching data is done in batch for all stages, to avoid doing multiple - // calls. The first naive implementation of group_by_tooltip made a call - // for each displayed stage and was quite limited. - // Data for the group tooltip (group_by_tooltip) and to display stage-related - // legends for kanban state management (states_legend) are fetched in - // one call. - var group_by_fields_to_read = []; - var recurse = function(node) { - if (node.tag === "field" && node.attrs && node.attrs.options) { - var options = pyeval.py_eval(node.attrs.options); - var states_fields_to_read = _.map( - options && options.states_legend || {}, - function (value, key, list) { return value; }); - var tooltip_fields_to_read = _.map( - options && options.group_by_tooltip || {}, - function (value, key, list) { return key; }); - group_by_fields_to_read = _.union( - group_by_fields_to_read, - states_fields_to_read, - tooltip_fields_to_read); - } - _.each(node.children, function(child) { - recurse(child); - }); + load_records: function (offset, dataset) { + var options = { + 'limit': this.limit, + 'offset': offset, }; - recurse(this.fields_view.arch); - var group_ids = _.without(_.map(groups, function (elem) { return elem.attributes.value[0];}), undefined); - if (this.grouped_by_m2o && group_ids.length && group_by_fields_to_read.length) { - var group_data = new data.DataSet( - this, - this.group_by_field.relation).read_ids(group_ids, _.union(['display_name'], group_by_fields_to_read)); - } - else { var group_data = $.Deferred().resolve({}); } - - this.$el.find('table:first').show(); - this.$el.removeClass('oe_kanban_ungrouped').addClass('oe_kanban_grouped'); - return $.when(group_data).then(function (results) { - _.each(results, function (group_by_data) { - var group = _.find(groups, function (elem) {return elem.attributes.value[0] == group_by_data.id;}); - if (group) { - group.values = group_by_data; - } + dataset = dataset || this.dataset; + return dataset + .read_slice(this.fields_keys.concat(['__last_update']), options) + .then(function(records) { + return { + records: records, + is_empty: !records.length, + grouped: false, + }; }); - }).done( function () {return self.add_group_mutex.exec(function() { - self.do_clear_groups(); - self.dataset.ids = []; - if (!groups.length) { - self.no_result(); - return $.when(); + }, + + load_groups: function () { + var self = this; + var group_by_field = this.group_by_field || this.default_group_by; + this.fields_keys = _.uniq(this.fields_keys.concat(group_by_field)); + + return new Model(this.model, this.search_context, this.search_domain) + .query(this.fields_keys) + .group_by([group_by_field]) + .then(function (groups) { + + // Check in the arch the fields to fetch on the stage to get tooltips data. + // Fetching data is done in batch for all stages, to avoid doing multiple + // calls. The first naive implementation of group_by_tooltip made a call + // for each displayed stage and was quite limited. + // Data for the group tooltip (group_by_tooltip) and to display stage-related + // legends for kanban state management (states_legend) are fetched in + // one call. + var group_by_fields_to_read = []; + var recurse = function(node) { + if (node.tag === "field" && node.attrs && node.attrs.options) { + var options = pyeval.py_eval(node.attrs.options); + var states_fields_to_read = _.map( + options && options.states_legend || {}, + function (value, key, list) { return value; }); + var tooltip_fields_to_read = _.map( + options && options.group_by_tooltip || {}, + function (value, key, list) { return key; }); + group_by_fields_to_read = _.union( + group_by_fields_to_read, + states_fields_to_read, + tooltip_fields_to_read); + } + _.each(node.children, function(child) { + recurse(child); + }); + }; + recurse(self.fields_view.arch); + + // fetch group data (display information) + var group_ids = _.without(_.map(groups, function (elem) { return elem.attributes.value[0];}), undefined); + if (self.grouped_by_m2o && group_ids.length) { + return new data.DataSet(self, self.relation) + .read_ids(group_ids, _.union(['display_name'], group_by_fields_to_read)) + .then(function(results) { + _.each(groups, function (group) { + var group_id = group.attributes.value[0]; + var result = _.find(results, function (data) {return group_id === data.id;}); + group.title = result ? result.display_name : _t("Undefined"); + group.values = result; + group.id = group_id; + }); + return groups; + }); + } else { + _.each(groups, function (group) { + var value = group.attributes.value; + group.title = (value instanceof Array ? value[1] : value) || _t("Undefined"); + }); + return $.when(groups); } - self.nb_records = 0; - var groups_array = []; - return $.when.apply(null, _.map(groups, function (group, index) { + }) + .then(function (groups) { + var undef_index = _.findIndex(groups, function (g) { return g.title === _t("Undefined");}); + if (undef_index >= 1) { + var undef_group = groups[undef_index]; + groups.splice(undef_index, 1); + groups.unshift(undef_group); + } + return groups; + }) + .then(function (groups) { + // load records for each group + var is_empty = true; + return $.when.apply(null, _.map(groups, function (group) { var def = $.when([]); var dataset = new data.DataSetSearch(self, self.dataset.model, new data.CompoundContext(self.dataset.get_context(), group.model.context()), group.model.domain()); @@ -313,275 +235,273 @@ var KanbanView = View.extend({ if (group.attributes.length >= 1) { def = dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }); } - return def.then(function(records) { - self.nb_records += records.length; - self.dataset.ids.push.apply(self.dataset.ids, dataset.ids); - groups_array[index] = new KanbanGroup(self, records, group, dataset); + return def.then(function (records) { + self.dataset.ids.push.apply(self.dataset.ids, dataset.ids); + group.records = records; + group.dataset = dataset; + is_empty = is_empty && !records.length; + return group; }); })).then(function () { - if(!self.nb_records) { - self.no_result(); - } - if (self.dataset.index >= self.nb_records){ - self.dataset.index = self.dataset.size() ? 0 : null; - } - return self.do_add_groups(groups_array).done(function() { - self.trigger('kanban_groups_processed'); - }); - }); - });}); - }, - do_process_dataset: function() { - var self = this; - this.$el.find('table:first').show(); - this.$el.removeClass('oe_kanban_grouped').addClass('oe_kanban_ungrouped'); - var def = $.Deferred(); - this.add_group_mutex.exec(function() { - self.do_clear_groups(); - self.dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }).done(function(records) { - var kgroup = new KanbanGroup(self, records, null, self.dataset); - if (!_.isEmpty(self.dataset.ids) && (self.dataset.index === null || self.dataset.index >= self.dataset.ids.length)) { - self.dataset.index = 0; - } else if (_.isEmpty(self.dataset.ids)){ - self.dataset.index = null; - } - self.do_add_groups([kgroup]).done(function() { - if (_.isEmpty(records)) { - self.no_result(); - } - self.trigger('kanban_dataset_processed'); - def.resolve(); - }); - }).done(null, function() { - def.reject(); + return { + groups: Array.prototype.slice.call(arguments, 0), + is_empty: is_empty, + grouped: true, + }; }); return def; }); - return def; }, - do_reload: function() { - this.do_search(this.search_domain, this.search_context, this.search_group_by); + + is_action_enabled: function(action) { + if (action === 'create' && !this.options.creatable) { + return false; + } + return this._super(action); }, - do_clear_groups: function() { - var groups = this.groups.slice(0); - this.groups = []; - _.each(groups, function(group) { - group.destroy(); - }); + _is_quick_create_enabled: function() { + if (!this.quick_creatable || !this.is_action_enabled('create')) + return false; + if (this.fields_view.arch.attrs.quick_create !== undefined) + return JSON.parse(this.fields_view.arch.attrs.quick_create); + return !!this.grouped; }, - do_add_groups: function(groups) { + /** + * Render the buttons according to the KanbanView.buttons template and + * add listeners on it. + * Set this.$buttons with the produced jQuery element + * @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted + * $node may be undefined, in which case the ListView inserts them into this.options.$buttons + */ + render_buttons: function($node) { + var display = false; + if (this.options.action_buttons !== false) { + display = this.is_action_enabled('create'); + } else if (!this.view_id && !this.options.read_only_mode) { + display = this.is_action_enabled('write') || this.is_action_enabled('create'); + } + this.$buttons = $(QWeb.render("KanbanView.buttons", {'widget': this, display: display})); + this.$buttons.on('click', 'button.o-kanban-button-new', this.add_record.bind(this)); + + $node = $node || this.options.$buttons; + this.$buttons.appendTo($node); + }, + + render_pager: function($node, options) { var self = this; - var $parent = this.$el.parent(); - this.$el.detach(); - _.each(groups, function(group) { - self.groups[group.undefined_title ? 'unshift' : 'push'](group); - }); - var $last_td = self.$el.find('.oe_kanban_groups_headers td:last'); - var groups_started = _.map(this.groups, function(group) { - if (!group.is_started) { - group.on("add_record", self, function () { - self.remove_no_result(); - }); - return group.insertBefore($last_td); - } - }); - return $.when.apply(null, groups_started).done(function () { - self.on_groups_started(); - self.$el.appendTo($parent); - _.each(self.groups, function(group) { - group.compute_cards_auto_height(); - }); + this.pager = new Pager(this, this.dataset.size(), 1, this.limit, options); + this.pager.appendTo($node); + this.pager.on('pager_changed', this, function (state) { + self.load_records(state.current_min - 1) + .then(function (data) { + self.data = data; + }) + .done(this.proxy('render')); }); + this.update_pager(); }, - on_groups_started: function() { - var self = this; - if (this.group_by || this.fields_keys.indexOf("sequence") !== -1) { - // Kanban cards drag'n'drop - var prev_widget, is_folded, record, $columns; - if (this.group_by) { - $columns = this.$el.find('.oe_kanban_column .oe_kanban_column_cards, .oe_kanban_column .oe_kanban_folded_column_cards'); + + update_pager: function() { + if (this.pager) { + if (this.grouped) { + this.pager.do_hide(); } else { - $columns = this.$el.find('.oe_kanban_column_cards'); - } - $columns.sortable({ - handle : '.oe_kanban_draghandle', - start: function(event, ui) { - self.currently_dragging.index = ui.item.parent().children('.oe_kanban_record').index(ui.item); - self.currently_dragging.group = prev_widget = ui.item.parents('.oe_kanban_column:first').data('widget'); - ui.item.find('*').on('click.prevent', function(ev) { - return false; - }); - record = ui.item.data('widget'); - record.$el.bind('mouseup',function(ev,ui){ - if (is_folded) { - record.$el.hide(); - } - record.$el.unbind('mouseup'); - }); - ui.placeholder.height(ui.item.height()); - }, - over: function(event, ui) { - var parent = $(event.target).parent(); - prev_widget.highlight(false); - is_folded = parent.hasClass('oe_kanban_group_folded'); - if (is_folded) { - var widget = parent.data('widget'); - widget.highlight(true); - prev_widget = widget; - } - }, - revert: 150, - stop: function(event, ui) { - prev_widget.highlight(false); - var old_index = self.currently_dragging.index; - var new_index = ui.item.parent().children('.oe_kanban_record').index(ui.item); - var old_group = self.currently_dragging.group; - var new_group = ui.item.parents('.oe_kanban_column:first').data('widget'); - if (!(old_group.title === new_group.title && old_group.value === new_group.value && old_index == new_index)) { - self.on_record_moved(record, old_group, old_index, new_group, new_index); - } - setTimeout(function() { - // A bit hacky but could not find a better solution for Firefox (problem not present in chrome) - // http://stackoverflow.com/questions/274843/preventing-javascript-click-event-with-scriptaculous-drag-and-drop - ui.item.find('*').off('click.prevent'); - }, 0); - }, - scroll: false - }); - // Keep connectWith out of the sortable initialization for performance sake: - // http://www.planbox.com/blog/development/coding/jquery-ui-sortable-slow-to-bind.html - $columns.sortable({ connectWith: $columns }); - - // Kanban groups drag'n'drop - var start_index; - if (this.grouped_by_m2o) { - this.$('.oe_kanban_groups_headers').sortable({ - items: '.oe_kanban_group_header', - helper: 'clone', - axis: 'x', - opacity: 0.5, - scroll: false, - start: function(event, ui) { - start_index = ui.item.index(); - self.$('.oe_kanban_record, .oe_kanban_quick_create').css({ visibility: 'hidden' }); - }, - stop: function(event, ui) { - var stop_index = ui.item.index(); - if (start_index !== stop_index) { - var $start_column = self.$('.oe_kanban_groups_records .oe_kanban_column').eq(start_index); - var $stop_column = self.$('.oe_kanban_groups_records .oe_kanban_column').eq(stop_index); - var method = (start_index > stop_index) ? 'insertBefore' : 'insertAfter'; - $start_column[method]($stop_column); - var tmp_group = self.groups.splice(start_index, 1)[0]; - self.groups.splice(stop_index, 0, tmp_group); - var new_sequence = _.pluck(self.groups, 'value'); - (new data.DataSet(self, self.group_by_field.relation)).resequence(new_sequence).done(function(r) { - if (r === false) { - console.error("Kanban: could not resequence model '%s'. Probably no 'sequence' field.", self.group_by_field.relation); - } - }); - } - self.$('.oe_kanban_record, .oe_kanban_quick_create').css({ visibility: 'visible' }); - } - }); + this.pager.set_state({size: this.dataset.size(), current_min: 1}); } + } + }, + + render: function () { + // cleanup + this.$el.css({display:'-webkit-flex'}); + this.$el.css({display:'flex'}); + this.$el.removeClass('o_kanban_ungrouped o_kanban_grouped'); + _.invoke(this.widgets, 'destroy'); + this.$el.empty(); + this.widgets = []; + if (this.column_quick_create) { + this.column_quick_create.destroy(); + this.column_quick_create = undefined; + } + + this.record_options = { + editable: this.is_action_enabled('edit'), + deletable: this.is_action_enabled('delete'), + fields: this.fields_view.fields, + qweb: this.qweb, + model: this.model, + read_only_mode: this.options.read_only_mode, + }; + + // actual rendering + var fragment = document.createDocumentFragment(); + if (this.data.grouped) { + this.$el.addClass('o_kanban_grouped'); + this.render_grouped(fragment); + } else if (this.data.is_empty) { + this.render_no_content(fragment); } else { - this.$el.find('.oe_kanban_draghandle').removeClass('oe_kanban_draghandle'); + this.$el.addClass('o_kanban_ungrouped'); + this.render_ungrouped(fragment); + } + this.$el.append(fragment); + }, + + render_no_content: function (fragment) { + var content = QWeb.render('KanbanView.nocontent', {content: this.no_content_msg}); + $(content).appendTo(fragment); + }, + + render_ungrouped: function (fragment) { + var self = this; + var options = _.clone(this.record_options); + _.each(this.data.records, function (record) { + var kanban_record = new KanbanRecord(self, record, options); + self.widgets.push(kanban_record); + kanban_record.appendTo(fragment); + }); + + // add empty invisible divs to make sure that all kanban records are left aligned + for (var i = 0, ghost_div; i < 6; i++) { + ghost_div = $("<div>").addClass("o_kanban_record o_kanban_ghost"); + ghost_div.appendTo(fragment); } this.postprocess_m2m_tags(); }, - on_record_moved : function(record, old_group, old_index, new_group, new_index) { + + get_column_options: function () { + return { + editable: this.is_action_enabled('group_edit'), + deletable: this.is_action_enabled('group_delete'), + grouped_by_m2o: this.grouped_by_m2o, + relation: this.relation, + qweb: this.qweb, + fields: this.fields_view.fields, + quick_create: this._is_quick_create_enabled(), + }; + }, + + render_grouped: function (fragment) { var self = this; - record.$el.find('[title]').tooltip('destroy'); - $(old_group.$el).add(new_group.$el).find('.oe_kanban_group_length').hide(); - if (old_group === new_group) { - new_group.records.splice(old_index, 1); - new_group.records.splice(new_index, 0, record); - new_group.do_save_sequences(); - } else { - old_group.records.splice(old_index, 1); - new_group.records.splice(new_index, 0, record); - record.group = new_group; - var data = {}; - data[this.group_by] = new_group.value; - this.dataset.write(record.id, data, {}).done(function() { - record.do_reload(); - new_group.do_save_sequences(); - if (new_group.state.folded) { - new_group.do_action_toggle_fold(); - record.prependTo(new_group.$records.find('.oe_kanban_column_cards')); - } - }).fail(function(error, evt) { - evt.preventDefault(); - alert(_t("An error has occured while moving the record to this group: ") + error.data.message); - self.do_reload(); // TODO: use draggable + sortable in order to cancel the dragging when the rcp fails - }); + var record_options = _.extend(this.record_options, { + draggable: true, + }); + + var column_options = this.get_column_options(); + + _.each(this.data.groups, function (group) { + var column = new KanbanColumn(self, group, column_options, record_options); + column.appendTo(fragment); + self.widgets.push(column); + }); + this.$el.sortable({ + axis: 'x', + items: '> .o_kanban_group', + cursor: 'move', + revert: 150, + delay: 100, + tolerance: 'pointer', + forcePlaceholderSize: true, + stop: function () { + var ids = []; + self.$('.o_kanban_group').each(function (index, u) { + ids.push($(u).data('id')); + }); + self.resequence(ids); + }, + }); + if (this.is_action_enabled('group_create')) { + this.column_quick_create = new ColumnQuickCreate(this); + this.column_quick_create.appendTo(fragment); } + this.postprocess_m2m_tags(); }, - do_show: function() { - this.do_push_state({}); - return this._super(); + add_record: function() { + this.dataset.index = null; + this.do_switch_view('form'); }, - open_record: function(id, editable) { - if (this.dataset.select_id(id)) { - this.do_switch_view('form', null, { mode: editable ? "edit" : undefined }); + + open_record: function (event, options) { + if (this.dataset.select_id(event.data.id)) { + this.do_switch_view('form', null, options); //, null, { mode: "edit" }); } else { - this.do_warn("Kanban: could not find id#" + id); + this.do_warn("Kanban: could not find id#" + event.data.id); } }, - no_result: function() { + + edit_record: function (event) { + this.open_record(event, {mode: 'edit'}); + }, + + delete_record: function (event) { var self = this; - if (this.groups.group_by - || !this.options.action - || (!this.options.action.help && !this.options.action.get_empty_list_help)) { - return; + var record = event.data.record; + function do_it() { + return $.when(self.dataset.unlink([record.id])).done(function() { + record.destroy(); + if (event.data.after) { + event.data.after(); + } + }); + } + if (this.options.confirm_on_delete) { + Dialog.confirm(this, _t("Are you sure you want to delete this record ?"), do_it); + } else { + do_it(); } - this.$el.css("position", "relative"); - $(QWeb.render('KanbanView.nocontent', { content : this.options.action.get_empty_list_help || this.options.action.help})).insertBefore(this.$('table:first')); - this.$el.find('.oe_view_nocontent').click(function() { - self.$buttons.openerpBounce(); - }); }, - remove_no_result: function() { - this.$el.css("position", ""); - this.$el.find('.oe_view_nocontent').remove(); + + open_action: function (event) { + var self = this; + var node_context = event.data.context || {}; + var context = new data.CompoundContext(node_context); + context.set_eval_context({ + active_id: event.target.id, + active_ids: [event.target.id], + active_model: this.dataset.model, + }); + this.do_execute_action(event.data, this.dataset, event.target.id).then(function () { + self.reload_record(event.target); + }); }, /* * postprocessing of fields type many2many * make the rpc request for all ids/model and insert value inside .oe_tags fields */ - postprocess_m2m_tags: function() { + postprocess_m2m_tags: function(records) { var self = this; if (!this.many2manys.length) { return; } var relations = {}; - this.groups.forEach(function(group) { - group.records.forEach(function(record) { - self.many2manys.forEach(function(name) { - var field = record.record[name]; - var $el = record.$('.oe_form_field.oe_tags[name=' + name + ']').empty(); - // fields declared in the kanban view may not be used directly - // in the template declaration, for example fields for which the - // raw value is used -> $el[0] is undefined, leading to errors - // in the following process. Preventing to add push the id here - // prevents to make unnecessary calls to name_get - if (! $el[0]) { - return; - } - if (!relations[field.relation]) { - relations[field.relation] = { ids: [], elements: {}, context: self.m2m_context[name]}; + records = records ? (records instanceof Array ? records : [records]) : + this.grouped ? Array.prototype.concat.apply([], _.pluck(this.widgets, 'records')) : + this.widgets; + + records.forEach(function(record) { + self.many2manys.forEach(function(name) { + var field = record.record[name]; + var $el = record.$('.oe_form_field.oe_tags[name=' + name + ']'); + // fields declared in the kanban view may not be used directly + // in the template declaration, for example fields for which the + // raw value is used -> $el[0] is undefined, leading to errors + // in the following process. Preventing to add push the id here + // prevents to make unnecessary calls to name_get + if (! $el[0]) { + return; + } + if (!relations[field.relation]) { + relations[field.relation] = { ids: [], elements: {}, context: self.m2m_context[name]}; + } + var rel = relations[field.relation]; + field.raw_value.forEach(function(id) { + rel.ids.push(id); + if (!rel.elements[id]) { + rel.elements[id] = []; } - var rel = relations[field.relation]; - field.raw_value.forEach(function(id) { - rel.ids.push(id); - if (!rel.elements[id]) { - rel.elements[id] = []; - } - rel.elements[id].push($el[0]); - }); + rel.elements[id].push($el[0]); }); }); }); @@ -589,68 +509,253 @@ var KanbanView = View.extend({ var dataset = new data.DataSetSearch(self, rel_name, self.dataset.get_context(rel.context)); dataset.name_get(_.uniq(rel.ids)).done(function(result) { result.forEach(function(nameget) { - $(rel.elements[nameget[0]]).append('<span class="oe_tag">' + _.str.escapeHTML(nameget[1]) + '</span>'); + // white color (0) should not be selected, 1 instead + var color = KanbanRecord.prototype.kanban_getcolor(nameget[1]) + 1; + var $tag = $('<span>') + .addClass('o_tag oe_kanban_color_' + color) + .attr('title', _.str.escapeHTML(nameget[1])); + $(rel.elements[nameget[0]]).append($tag); }); + // we use boostrap tooltips for better and faster display + self.$('span.o_tag').tooltip(); }); }); - } -}); + }, + resequence: function (ids) { + if ((ids.length <= 1) || !this.relation) { + return; + } + new data.DataSet(this, this.relation).resequence(ids).done(function (r) { + if (!r) { + console.warn('Resequence could not be complete. ' + + 'Maybe the model does not have a "sequence" field?'); + } + }); + }, -core.view_registry.add('kanban', KanbanView); + resequence_column: function (col) { + if (_.indexOf(this.fields_keys, 'sequence') > -1) { + this.dataset.resequence(col.get_ids()); + } + }, -return KanbanView; + delete_column: function (event) { + var self = this; + var column = event.target; + (new data.DataSet(this, this.relation)).unlink([column.id]).done(function() { + if (column.is_empty()) { + var index = self.widgets.indexOf(column); + self.widgets.splice(index,1); + column.destroy(); + } else { + self.do_reload(); + } + }); + }, -}); + reload_record: function (record) { + var self = this; + this.dataset.read_ids([record.id], this.fields_keys.concat(['__last_update'])).done(function(records) { + if (records.length) { + record.update(records[0]); + self.postprocess_m2m_tags(record); + } else { + record.destroy(); + } + }); + }, + add_record_to_column: function (event) { + var self = this; + var column = event.target; + var record = event.data.record; + var data = {}; + data[this.group_by_field] = event.target.id; + this.dataset.write(record.id, data, {}).done(function () { + if (!self.isDestroyed()) { + self.reload_record(record); + self.resequence_column(column); + } + }); + }, -odoo.define(function (require) { + update_record: function(event) { + var self = this; + var record = event.target; + return this.dataset.write(record.id, event.data) + .done(function () { + if (!self.isDestroyed()) { + self.reload_record(record); + } + }); + }, -var core = require('web.core'); -var data = require('web.data'); -var web_client = require('web.web_client'); -var kanban_common = require('web_kanban.common'); -var KanbanView = require('web_kanban.KanbanView'); + column_add_record: function (event) { + var self = this; + var column = event.target; + var context = {}; + context['default_' + this.group_by_field] = column.value; + var name = event.data.value; + this.dataset.name_create(name, context).then(function on_success (data) { + add_record(data[0]); + }, function on_fail (event) { + event.preventDefault(); + var popup = new form_common.SelectCreatePopup(this); + popup.select_element( + self.dataset.model, + { + title: _t("Create: "), + initial_view: "form", + disable_multiple_selection: true + }, + [], + {"default_name": name} + ); + popup.on("elements_selected", self, function(element_ids) { + add_record(element_ids[0]); + }); + }); -var KanbanGroup = kanban_common.KanbanGroup; -var _t = core._t; + function add_record(id) { + self.dataset.add_ids([id], -1); + self.dataset.read_ids([id], self.fields_keys.concat(['__last_update'])).done(function(record) { + column.add_record(record[0], {position: 'before'}); + }); + } + }, + + add_new_column: function (event) { + var self = this; + var model = new Model(this.relation, this.search_context); + model.call('create', [{name: event.data.value}], { + context: this.search_context, + }).then(function (id) { + var dataset = new data.DataSetSearch(self, self.dataset.model, self.dataset.get_context(), []); + var group_data = { + records: [], + title: event.data.value, + id: id, + attributes: {folded: false}, + dataset: dataset, + values: {}, + }; + var options = self.get_column_options(); + var record_options = _.clone(self.record_options); + var column = new KanbanColumn(self, group_data, options, record_options); + column.insertBefore(self.$('.o_column_quick_create')); + self.widgets.push(column); + }); + }, -KanbanView.include({ - do_add_group: function() { + load_more: function (event) { var self = this; - self.do_action({ - name: _t("Add column"), - res_model: self.group_by_field.relation, - views: [[false, 'form']], - type: 'ir.actions.act_window', - target: "new", - context: self.dataset.get_context(), - flags: { - action_buttons: true, + var column = event.target; + var offset = column.offset + this.limit; + return this.load_records(offset, column.dataset).then(function (result) { + _.each(result.records, function (r) { + column.add_record(r, {no_update: true}); + }); + column.offset += self.limit; + column.remaining = Math.max(column.remaining - self.limit, 0); + column.update_column(); + self.postprocess_m2m_tags(column.records); + }); + }, + + call_method: function (event) { + var data = event.data; + this.dataset.call(data.method, data.params).then(function() { + if (data.callback) { + data.callback(); } }); - var am = web_client.action_manager; - var form = am.dialog_widget.views.form.controller; - form.on("on_button_cancel", am.dialog, am.dialog.close); - form.on('record_created', self, function(r) { - (new data.DataSet(self, self.group_by_field.relation)).name_get([r]).done(function(new_record) { - am.dialog.close(); - var domain = self.dataset.domain.slice(0); - domain.push([self.group_by, '=', new_record[0][0]]); - var dataset = new data.DataSetSearch(self, self.dataset.model, self.dataset.get_context(), domain); - var datagroup = { - get: function(key) { - return this[key]; - }, - value: new_record[0], - length: 0, - }; - var new_group = new KanbanGroup(self, [], datagroup, dataset); - self.do_add_groups([new_group]).done(function() { - $(window).scrollTo(self.groups.slice(-1)[0].$el, { axis: 'x' }); + } +}); + +function qweb_add_if(node, condition) { + if (node.attrs[QWeb.prefix + '-if']) { + condition = _.str.sprintf("(%s) and (%s)", node.attrs[QWeb.prefix + '-if'], condition); + } + node.attrs[QWeb.prefix + '-if'] = condition; +} + +function transform_qweb_template (node, fvg, many2manys) { + // Process modifiers + if (node.tag && node.attrs.modifiers) { + var modifiers = JSON.parse(node.attrs.modifiers || '{}'); + if (modifiers.invisible) { + qweb_add_if(node, _.str.sprintf("!kanban_compute_domain(%s)", JSON.stringify(modifiers.invisible))); + } + } + switch (node.tag) { + case 'field': + var ftype = fvg.fields[node.attrs.name].type; + ftype = node.attrs.widget ? node.attrs.widget : ftype; + if (ftype === 'many2many') { + if (_.indexOf(many2manys, node.attrs.name) < 0) { + many2manys.push(node.attrs.name); + } + node.tag = 'div'; + node.attrs['class'] = (node.attrs['class'] || '') + ' oe_form_field oe_tags o_kanban_tags'; + } else if (fields_registry.contains(ftype)) { + // do nothing, the kanban record will handle it + } else { + node.tag = QWeb.prefix; + node.attrs[QWeb.prefix + '-esc'] = 'record.' + node.attrs.name + '.value'; + } + break; + case 'button': + case 'a': + var type = node.attrs.type || ''; + if (_.indexOf('action,object,edit,open,delete,url'.split(','), type) !== -1) { + _.each(node.attrs, function(v, k) { + if (_.indexOf('icon,type,name,args,string,context,states,kanban_states'.split(','), k) != -1) { + node.attrs['data-' + k] = v; + delete(node.attrs[k]); + } }); - }); + if (node.attrs['data-string']) { + node.attrs.title = node.attrs['data-string']; + } + if (node.attrs['data-icon']) { + node.children = [{ + tag: 'img', + attrs: { + src: session.prefix + '/web/static/src/img/icons/' + node.attrs['data-icon'] + '.png', + width: '16', + height: '16' + } + }]; + } + if (node.tag == 'a' && node.attrs['data-type'] != "url") { + node.attrs.href = '#'; + } else { + node.attrs.type = 'button'; + } + node.attrs['class'] = (node.attrs['class'] || '') + ' oe_kanban_action oe_kanban_action_' + node.tag; + } + break; + } + if (node.children) { + for (var i = 0, ii = node.children.length; i < ii; i++) { + transform_qweb_template(node.children[i], fvg, many2manys); + } + } +} + +var One2ManyKanbanView = KanbanView.extend({ + render_pager: function($node, options) { + options = _.extend(options || {}, { + single_page_hidden: true, }); + this._super($node, options); }, }); +core.view_registry.add('kanban', KanbanView); +core.one2many_view_registry.add('kanban', One2ManyKanbanView); + +return KanbanView; + }); + diff --git a/addons/web_kanban/static/src/js/kanban_widgets.js b/addons/web_kanban/static/src/js/kanban_widgets.js new file mode 100644 index 00000000000..29942075e79 --- /dev/null +++ b/addons/web_kanban/static/src/js/kanban_widgets.js @@ -0,0 +1,247 @@ +odoo.define('web_kanban.widgets', function (require) { +"use strict"; + +var core = require('web.core'); +var ProgressBar = require('web.ProgressBar'); +var pyeval = require('web.pyeval'); +var Registry = require('web.Registry'); +var session = require('web.session'); +var Widget = require('web.Widget'); + +var QWeb = core.qweb; +/** + * Interface to be implemented by kanban fields. + * + */ +var FieldInterface = { + /** + Constructor. + - parent: The widget's parent. + - field: A dictionary giving details about the field, including the current field's value in the + raw_value field. + - $node: The field <field> tag as it appears in the view, encapsulated in a jQuery object. + */ + init: function(parent, field, $node) {}, +}; + +/** + * Abstract class for classes implementing FieldInterface. + * + * Properties: + * - value: useful property to hold the value of the field. By default, the constructor + * sets value property. + * + */ +var AbstractField = Widget.extend(FieldInterface, { + /** + Constructor that saves the field and $node parameters and sets the "value" property. + */ + init: function(parent, field, $node) { + this._super(parent); + this.field = field; + this.$node = $node; + this.options = pyeval.py_eval(this.$node.attr("options") || '{}'); + this.set("value", field.raw_value); + }, +}); + +var Priority = AbstractField.extend({ + init: function(parent, field, $node) { + this._super.apply(this, arguments); + this.name = $node.attr('name'); + }, + prepare_priority: function() { + var self = this; + var selection = this.field.selection || []; + var init_value = selection && selection[0][0] || 0; + var data = _.map(selection.slice(1), function(element, index) { + var value = { + 'value': element[0], + 'name': element[1], + 'click_value': element[0], + }; + if (index === 0 && self.get('value') == element[0]) { + value.click_value = init_value; + } + return value; + }); + return data; + }, + renderElement: function() { + this.priorities = this.prepare_priority(); + var readonly = this.field && this.field.readonly; + if (readonly){ + this.set('readonly', true); + } + this.$el = $(QWeb.render("Priority", {'widget': this})); + if (!readonly){ + this.$el.on('click', 'a', this.do_action.bind(this)); + } + }, + do_action: function(event) { + event.preventDefault(); + var $a = $(event.currentTarget); + var data = {}; + data[this.name] = String($a.data('value')); + this.trigger_up('kanban_update_record', data); + }, +}); + +var KanbanSelection = AbstractField.extend({ + + init: function(parent, field, $node) { + this._super.apply(this, arguments); + this.name = $node.attr('name'); + this.parent = parent; + }, + prepare_dropdown_selection: function() { + var self = this; + var data = []; + _.map(this.field.selection || [], function(res) { + var value = { + 'name': res[0], + 'tooltip': res[1], + 'state_name': res[1], + }; + var leg_opt = self.options && self.options.states_legend || null; + if (leg_opt) { + var key = leg_opt[value.name]; + var legend = self.parent.group_info[key]; + if (legend) { + value.state_name = legend; + value.tooltip = legend; + } + } + if (res[0] == 'normal') { value.state_class = 'oe_kanban_status'; } + else if (res[0] == 'done') { value.state_class = 'oe_kanban_status oe_kanban_status_green'; } + else { value.state_class = 'oe_kanban_status oe_kanban_status_red'; } + data.push(value); + }); + return data; + }, + renderElement: function() { + var self = this; + this.states = this.prepare_dropdown_selection(); + + var state; + for(var i = 0 ; i < this.states.length ; i++) { + if(this.states[i].name === this.get('value')) { + state = this.states[i]; + } + } + + this.$el = $(QWeb.render("KanbanSelection", {'widget': this})); + this.$('a').first().find('span').removeClass().addClass(state.state_class); + this.$('ul li').show().filter(function() { + return ($(this).data('value') === state.name); + }).hide(); + + this.$('a').click(function (ev) { + ev.preventDefault(); + }); + + this.$('li').click(this.set_kanban_selection.bind(this)); + }, + set_kanban_selection: function(e) { + e.preventDefault(); + var self = this; + var $li = $(e.target).closest( "li" ); + if ($li.length) { + var value = {}; + value[self.name] = String($li.data('value')); + this.trigger_up('kanban_update_record', value, this); + } + }, +}); + +var KanbanAttachmentImage = AbstractField.extend({ + template: 'KanbanAttachmentImage', +}); + + +/** + * Kanban widgets: ProgressBar + * options + * - editable: boolean if current_value is editable + * - current_value: get the current_value from the field that must be present in the view + * - max_value: get the max_value from the field that must be present in the view + * - title: title of the gauge, displayed on top of the gauge + * - on_change: action to call when cliking and setting a value + */ +var KanbanProgressBar = AbstractField.extend({ + events: { + 'click': function() { + if(!this.readonly && this.progressbar.readonly) { + this.toggle_progressbar(); + } + } + }, + + init: function (parent, field, node) { + this._super(parent, field, node); + + var record = this.getParent().record; + this.progressbar = new ProgressBar(this, { + readonly: true, + value: record[this.options.current_value].raw_value, + max_value: record[this.options.max_value].raw_value, + title: this.options.title, + edit_max_value: this.options.edit_max_value, + }); + + this.readonly = !this.options.editable; + this.on_change = this.options.on_change; + }, + + start: function () { + var self = this; + + var def = this.progressbar.appendTo('<div>').done(function() { + self.progressbar.$el.addClass(self.$el.attr('class')); + self.replaceElement(self.progressbar.$el); + }); + + return $.when(this._super(), def).then(function() { + if(!self.readonly) { + var parent = self.getParent(); + self.progressbar.on('change:value change:max_value', self, function(e) { + var value = this.progressbar.get(this.progressbar.edit_max_value ? 'max_value' : 'value') || 0; + if(!isNaN(value)) { + var data = { + method: this.on_change, + params: [parent.id, value], + callback: self.proxy('toggle_progressbar'), + }; + self.trigger_up('kanban_call_method', data); + + // parent.view.dataset.call(this.on_change, [parent.id, value]).then(function() { + // self.toggle_progressbar(); + // }); + } + }); + } + }); + }, + + toggle_progressbar: function() { + this.progressbar.readonly = !this.progressbar.readonly; + var $div = $('<div/>').insertAfter(this.$el); + this.progressbar.replace($div); + this.setElement(this.progressbar.$el); + }, +}); + +var fields_registry = new Registry(); + +fields_registry + .add('priority', Priority) + .add('kanban_state_selection', KanbanSelection) + .add("attachment_image", KanbanAttachmentImage) + .add('progress', KanbanProgressBar); + +return { + AbstractField: AbstractField, + registry: fields_registry, +}; + +}); diff --git a/addons/web_kanban/static/src/less/kanban_dashboard.less b/addons/web_kanban/static/src/less/kanban_dashboard.less index 3315beef9eb..61df77edcd0 100644 --- a/addons/web_kanban/static/src/less/kanban_dashboard.less +++ b/addons/web_kanban/static/src/less/kanban_dashboard.less @@ -1,7 +1,7 @@ -.oe_kanban_view { +.o_kanban_view { &.o_kanban_mobile { - .oe_kanban_record { + .o_kanban_record { width: 400px; } span { @@ -14,8 +14,6 @@ &.o_kanban_dashboard { - @odoo-brand-optional: #21b799; - .o_visible { width: 100%; } @@ -43,8 +41,8 @@ margin-right: 0px !important; } + .oe_kanban_colorpicker { - min-height: 21px; li { height: 0px; @@ -52,45 +50,37 @@ } } - .oe_kanban_record { - padding: 0; - position: relative; - margin: 10px; - - // @media (max-width: @grid-float-breakpoint) { - // margin: 10px !important; - // } - } - - // if invisible, it will no take more than its original height + // If invisible, elem will no take more than its original height a, span { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; position: relative; - width: 100%; display: block; } + .o_kanban_record { + padding: 0; + position: relative; + margin: 4px 8px; + } + a { - // color: @odoo-brand-optional; + color: @odoo-brand-optional; } .o_kanban_card_header { - display: flex; - display: -webkit-flex; + .o-flex-display(); background-color: lightgrey; position: relative; .o_kanban_card_header_title { - -webkit-flex: 3 0 0; - flex: 3 0 0; - padding: 8px; + .o-flex(3, 0, 0); + padding: 8px 16px; .o_primary { - // color: @odoo-brand-optional; - font-variant: small-caps; - font-size: large; + color: @odoo-brand-optional; + font-weight: 700; } .o_secondary { @@ -98,12 +88,11 @@ } .o_kanban_manage_button_section { - -webkit-flex: 1 0 0; - flex: 1 0 0; + .o-flex(1, 0, 0); padding: 16px; .o_kanban_manage_toggle_button { - text-align: center; + text-align: right; } } } @@ -129,6 +118,7 @@ .o_kanban_card_manage_pane { display: inline-block; vertical-align: top; + padding: 0 8px; } .o_kanban_card_manage_section { @@ -145,6 +135,7 @@ } .o_kanban_card_manage_settings { + text-align: right; padding: 8px; border-color: lightgrey; diff --git a/addons/web_kanban/static/src/less/kanban_view.less b/addons/web_kanban/static/src/less/kanban_view.less new file mode 100644 index 00000000000..4d7a6221dbf --- /dev/null +++ b/addons/web_kanban/static/src/less/kanban_view.less @@ -0,0 +1,500 @@ +@o-kanban-record-margin: 4px; + +.o_kanban_view { + + .o-flex-display(); + .o-align-content(stretch); + @media (max-width: @grid-float-breakpoint) { + padding: 0px; + } + + .o_kanban_quick_create { + background-color: white; + margin: @o-kanban-record-margin; + box-shadow: 0px 0px 1px 1px lighten(@odoo-main-text-color, 10%); + padding: 3px; + input { + width: 100%; + margin: 4px 0; + } + } + + .o_kanban_record { + min-width: 150px; + background-color: white; + margin: 4px 4px 8px 4px; + box-shadow: 0px 0px 1px 1px lighten(@odoo-main-text-color, 10%); + padding: 8px; + + &.o_record_dragged { + background-color: @odoo-main-text-color; + } + + &.oe_kanban_global_click, &.oe_kanban_global_click_edit { + cursor: pointer; + } + + ul { + list-style-type: none; + padding: 0; + } + + &.o_kanban_two_column { + .o-flex-display(); + + .oe_kanban_details { + position: relative; + display: inline-block; + .o-flex(1, 1, auto); + width: 0; + } + } + + &.o_kanban_two_column_tags { + .o-flex-display(); + .o-flex-flow(row, wrap); + + .o_kanban_tags_section { + .o-flex(1, 1, 100%); + } + .o_kanban_image { + .o-flex(1, 1, 25%); + margin-right: 0; + > img { + box-shadow: none; + } + } + .oe_kanban_details { + position: relative; + .o-flex(1, 1, 75%); + ul { + margin-bottom: 0; + } + } + } + + .o_kanban_title { + font-weight: bold; + margin-bottom: 3px; + } + + .o_kanban_image { + .o-flex(0, 0, auto); + margin-right: 8px; + text-align: center; + + img { + width: 64px; + height: 64px; + box-shadow: none; + } + + } + + .o_kanban_button { + margin-top: 15px; + + > button, > a { + position: absolute; + bottom: 0; + right: 0; + } + } + + &.ui-sortable-helper { + transform: rotate(3deg); + opacity: 0.9, + } + + .o_dropdown_kanban { + float: right; + + // Open the dropdown a the left side of the dropdown toggle + > .dropdown-menu { + left: auto; + right: 0; + padding: 3px; + } + + > a { + padding: 3px; + color: @odoo-main-text-color; + } + &.open { + visibility: visible; + } + } + + .o_kanban_footer { + .o-flex-display(); + .o-justify-content(space-between); + > * { + flex: none; + } + } + &:hover { + .o_dropdown_kanban { + visibility: visible; + } + } + .o_dropdown_kanban { + @media (max-width: @grid-float-breakpoint) { + visibility: visible; + } + } + } + + &.o_kanban_ungrouped { + .o-flex-flow(row, wrap); + .o-justify-content(flex-start); + padding: @o-kanban-record-margin @odoo-horizontal-padding - @o-kanban-record-margin; + + @media (max-width: @grid-float-breakpoint) { + padding: 0; + } + + .o_kanban_record { + .o-flex(1, 1, auto); + width: 320px; + @media (max-width: @grid-float-breakpoint) { + margin: 0 0 1px 0; + width: 100vw; + box-shadow: none; + } + } + + .o_kanban_ghost { + height: 0; + min-height: 0 !important; // to prevent view writers to override this height + visibility: hidden; + margin-top: 0; + margin-bottom: 0; + padding: 0; + } + + } + + &.o_kanban_grouped { + min-height: 100%; + padding: 0 @odoo-horizontal-padding - @o-kanban-record-margin; + .o_kanban_record { + width: 300px; + + &.o_kanban_small { + width: 240px; + } + } + } + + .o_kanban_group { + .o-flex(0, 0, auto); + margin: 0 4px; + + &.o_kanban_dragged { + background-color: @odoo-main-text-color; + .o_kanban_record { + visibility: hidden; + } + } + + .o_kanban_header + .o_kanban_record { + margin-top: 0; // no margin top from the first card after kanban_header + } + + .o_kanban_header { + color: white; + padding: 0 4px; + height: 50px; + line-height: 2.2; + cursor: move; + .o-flex-display(); + .o_column_unfold { + display: none; + } + .o_column_title { + font-size: 18px; + font-weight: 300; + .o-flex(1, 1, auto); + white-space: nowrap; + padding-top: 5px; + } + + .o-kanban-icon() { + // Share the style of `.o_kanban_quick_add` and `o_kanban_config`. + height: 50px; + width: 50px; + line-height: 1.8; + text-align: center; + color: white; + padding: 8px; + font-size: 22px; + cursor: pointer; + } + + .o_kanban_quick_add i { + .o-kanban-icon; + &:hover { + background-color: @odoo-brand-optional; + } + } + .o_kanban_config i { + .o-kanban-icon; + &:hover { + background-color: darken(@gray-light, 10%); + } + } + + .o_kanban_number { + color: @odoo-main-text-color; + text-align: center; + } + } + + .o_kanban_record { + &.o_kanban_dragged { + > * { + visibility: hidden; + } + background-color: @odoo-main-text-color; + } + } + + &.o_column_folded { + position: relative; + border-right: 1px solid @odoo-main-text-color; + margin: 0; + + .o_column_title { + line-height: 1.3; + text-transform: uppercase; + transform-origin: left bottom; + transform: rotate(90deg); + top: 40px; + left: 15px; + position: absolute; + } + > .o_kanban_record, .o_kanban_quick_add, .o_kanban_config, .o_kanban_load_more { + display: none; + } + &:hover { + cursor: pointer; + background-color: darken(@gray-light, 10%); + } + .o_column_unfold { + display: block; + padding: 0 10px; + + > i { + font-size: 22px; + padding-top: 15px; + } + + } + background-color: @gray-light; + } + + .o_kanban_load_more { + text-align: center; + a { + color: white; + font-weight: 300; + } + } + } + + .oe_kanban_color_0 { + background-color: #FFFFFF; + color: #5a5a5a; + } + .oe_kanban_color_1 { + background-color: #d8d8d8; + color: #5e5e5e; + } + .oe_kanban_color_2 { + background-color: #e5d8e3; + color: #7a3737; + } + .oe_kanban_color_3 { + background-color: #ece9c2; + color: #756832; + } + .oe_kanban_color_4 { + background-color: #dee180; + color: #5d6937; + } + .oe_kanban_color_5 { + background-color: #abd4cc; + color: #1a7759; + } + .oe_kanban_color_6 { + background-color: #b8e3e8; + color: #1a5d83; + } + .oe_kanban_color_7 { + background-color: #f4ddab; + color: #5e5e5e; + } + .oe_kanban_color_8 { + background-color: #7f637d; + color: #a5a5a5; + } + .oe_kanban_color_9 { + background-color: #d8c5d5; + color: #5e5e5e; + } + .oe_kanban_color_10 { + background-color: #46C265; + color: #639B89; + } + + .oe_tags { + display: block; + } + .o_kanban_tags { + + margin-left: -8px; + + .o_tag{ + width: 50px; + height: 9px; + display: inline-block; + vertical-align: top; + } + } + .o_kanban_footer { + width: 100%; + } + .oe_kanban_avatar { + border-radius: 12px; + border: 1px solid @odoo-main-color-muted; + } + + .o_progressbar { + .o_progressbar_title { + width: 20%; + } + .o_progress { + width: 60%; + height: 15px; + } + .o_progressbar_value { + width: 20%; + text-align: right; + } + } + + .o_kanban_attachment_image { + // The image should take the hole card + margin: -8px; + > img { + width: 100%; + } + } +} + +.oe_kanban_colorpicker { + li { + float: left; + &:first-child a { + border: 1px solid #ccc; + } + + a { + width: 15px; + height: 16px; + display: inline-block; + border: 1px solid white; + } + } +} + +.o_dropdown_kanban { + visibility: hidden; +} + +.oe_kanban_avatar_smallbox { + height: 40px; + width: 40px; + box-shadow: 0 1px 4px @gray-light; + // border: 1px solid + // border-color: #e5e5e5 #dbdbdb #d2d2d2 + // @include radius(3px) + // @include box-shadow(0 1px 2px rgba(0,0,0,0.2)) +} + +.o_kanban_shadow { + background-color: @odoo-main-text-color; + miw-width: 100px; +} + +.o_column_quick_create { + .o-flex(0, 0, auto); + margin: 0; + padding: 8px; + position: relative; + background-color: @odoo-main-text-color; + cursor: pointer; + + &:hover { + background-color: darken(@gray-light, 10%); + } + .o_column_header { + .o_kanban_add_column { + padding: 0 10px; + color: white; + + > i { + font-size: 22px; + padding-top: 7px; + } + } + .o_kanban_title { + font-size: 18px; + font-weight: 300; + white-space: nowrap; + color: @gray-lighter; + text-transform: uppercase; + transform-origin: left bottom; + transform: rotate(90deg); + top: 40px; + left: 15px; + position: absolute; + } + } + .o_kanban_quick_create { + display: none; + width: 180px; + } +} + +.oe_kanban_footer_left { + float: left; +} + +.oe_kanban_bottom_right { + float: right; +} + +.oe_kanban_bottom_left { + float: left; +} + +.oe_kanban_status { + display: inline-block; + height: 12px; + width: 12px; + border-radius: 6px; + + background-color: @odoo-brand-secondary; + + &.oe_kanban_status_green { + background-color: @brand-success; + } + &.oe_kanban_status_red { + background-color: @brand-danger; + } +} + +.o_form_field_one2many .oe_view_nocontent { + display: none; +}
\ No newline at end of file diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index 0a7bbcdcffb..497b0696025 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -1,90 +1,72 @@ <template> -<t t-name="KanbanView"> - <div class="oe_kanban_view"> - <div class="oe_kanban_buttons"/> - <table class="oe_kanban_groups"> - <tr class="oe_kanban_groups_headers"> - <td class="oe_kanban_dummy_cell"><div/></td> - </tr> - <tr class="oe_kanban_groups_records"> - <td class="oe_kanban_dummy_cell"><div/></td> - </tr> - </table> - </div> -</t> + <t t-name="KanbanView.buttons"> <t t-if="display"> - <div class="oe_kanban_buttons"> - <button type="button" class="oe_kanban_button_new btn btn-primary btn-sm"> + <div> + <button type="button" class="btn btn-primary btn-sm o-kanban-button-new"> <t t-esc="widget.options.create_text || _t('Create')"/> </button> - <span t-if="widget.is_action_enabled('group_create')" class="oe_alternative" style="display: none"> - <span class="oe_fade">or</span> - <div class="oe_bounce_container"> - <a href="#" class="oe_kanban_add_column text-info">Add a new column</a> - </div> - </span> </div> </t> </t> -<t t-name="KanbanView.group_header"> - <td class="oe_kanban_group_header"> - <t t-if="widget.view.group_by"> - <t t-set="parent" t-value="(widget.getParent())"/> - <div class="oe_kanban_header oe_semantic_html_override"> - <t t-if="widget.view._is_quick_create_enabled()"> - <div><i class="oe_kanban_add fa fa-plus" title="Quick create"/></div> - </t> - <div class="oe_dropdown_toggle oe_dropdown_kanban"> - <div class="oe_kanban_group_length"> - <span class="oe_tag"> - <t t-if="widget.group.get('length') > 99"> 99+ </t><t t-if="widget.group.get('length') <= 99"> <t t-esc="widget.group.get('length')"/> </t> - </span> - </div> - <i class="fa fa-bars fa-lg"/> - <ul class="oe_dropdown_menu oe_kanban_group_dropdown"> - <li><a data-action="toggle_fold" href="#">Fold</a></li> - <t t-if="widget.view.grouped_by_m2o and widget.value"> - <li t-if="parent && parent.is_action_enabled('group_edit')"><a data-action="edit" href="#">Edit</a></li> - <li t-if="parent && parent.is_action_enabled('group_delete')"><a data-action="delete" href="#">Delete</a></li> - </t> - </ul> - </div> - <div class="oe_fold_column"> - <div t-attf-class="oe_kanban_group_title #{widget.undefined_title ? 'oe_kanban_group_title_undefined' : ''}"> - <div class="oe_kanban_group_title_text" t-att-title="widget.title"><t t-esc="widget.title"/></div> - </div> - <div class="oe_kanban_group_length oe_tag"> - <t t-if="widget.group.get('length') > 99"> 99+ </t><t t-if="widget.group.get('length') <= 99"> <t t-esc="widget.group.get('length')"/> </t> - </div> - </div> - <span t-if="widget.title" class="oe_kanban_group_title_vertical"> - <t t-esc="widget.title"/> - </span> - </div> - </t> - <t t-if="! widget.view.group_by && widget.view._is_quick_create_enabled()"> - <span class="oe_kanban_no_group_qc_placeholder"/> - </t> - </td> + +<t t-name="KanbanView.Group"> + <div class="o_kanban_group" t-att-data-id="widget.id"> + <div class="o_kanban_header" t-att-title="widget.data_records.length + ' records'"> + <span class="o_column_title"><t t-esc="widget.title"/></span> + <span class="o_column_unfold"><i class="fa fa-arrows-h"/></span> + <span class="o_kanban_config dropdown"> + <a class="dropdown-toggle" data-toggle="dropdown" href="#"><i class="fa fa-gear"/></a> + <ul class="dropdown-menu" role="menu"> + <li><a class="o_kanban_toggle_fold" href="#">Fold</a></li> + <t t-if="widget.grouped_by_m2o"> + <li t-if="widget.editable and widget.id"><a class="o_column_edit" href="#">Edit</a></li> + <li t-if="widget.deletable and widget.id"><a class="o_column_delete" href="#">Delete</a></li> + </t> + </ul> + </span> + <t t-if="widget.quick_create"> + <span class="o_kanban_quick_add"><i class="fa fa-plus"/></span> + </t> + </div> + <div t-if="widget.remaining > 0" class="o_kanban_load_more"> + <t t-call="KanbanView.LoadMore"/> + </div> + </div> +</t> + +<t t-name="KanbanView.LoadMore"> + <a href="#">Load more... (<t t-esc="widget.remaining"/> remaining)</a> </t> -<t t-name="KanbanView.group_records_container"> - <td class="oe_kanban_column"> - <div class="oe_kanban_group_list_header"/> - <div class="oe_kanban_column_cards"/> - <div class="oe_kanban_folded_column_cards"/> - <div class="oe_kanban_show_more"> - <button class="oe_button">Show more... (<span class="oe_kanban_remaining"></span> remaining)</button> + +<t t-name="KanbanView.ColumnQuickCreate"> + <div class="o_column_quick_create"> + <div class="o_column_header"> + <span class="o_kanban_add_column"><i class="fa fa-chevron-right"/></span> + <span class="o_kanban_title">Add new Column</span> </div> - </td> + <div class="o_kanban_quick_create"> + <input type="text" placeholder="Add"/> + <button class="btn btn-primary o_kanban_add">Add</button> + <button class="btn btn-default o_kanban_cancel">Cancel</button> + </div> + </div> </t> + <t t-name="KanbanView.record"> - <div class="oe_fold_column oe_kanban_record"> - <t t-raw="content"/> + <t t-raw="widget.content"/> +</t> + +<t t-name="KanbanView.QuickCreate"> + <div class="o_kanban_quick_create"> + <input type="text" placeholder="Add"/> + <button class="btn btn-primary o_kanban_add">Add</button> + <button class="btn btn-default o_kanban_cancel">Cancel</button> </div> </t> + <t t-name="KanbanColorPicker"> - <li t-foreach="widget.view.number_of_color_schemes" t-as="color"> + <li t-foreach="10" t-as="color"> <a href="#" t-att-data-color="color_index" t-attf-class="oe_kanban_color_#{color}"/> </li> </t> @@ -98,11 +80,13 @@ </t> </div> <t t-name="KanbanView.nocontent"> - <div class="oe_view_nocontent" style="position:absolute;"> - <div class="oe_view_nocontent_bg"></div> - <div class="oe_view_nocontent_content"> - <t t-raw="content"/> - </div> + <div class="oe_view_nocontent"> + <t t-raw="content"/> + </div> +</t> +<t t-name="KanbanAttachmentImage"> + <div class="o_kanban_attachment_image"> + <img t-att-src="'/web/binary/image?model=ir.attachment&field=datas&id=' + widget.field.raw_value[0]"/> </div> </t> </template> diff --git a/addons/web_kanban/views/web_kanban.xml b/addons/web_kanban/views/web_kanban.xml index b9fb5dff577..f383254636d 100644 --- a/addons/web_kanban/views/web_kanban.xml +++ b/addons/web_kanban/views/web_kanban.xml @@ -3,11 +3,14 @@ <data> <template id="assets_backend" name="web_kanban assets" inherit_id="web.assets_backend"> <xpath expr="." position="inside"> - <link rel="stylesheet" href="/web_kanban/static/src/css/kanban.css"/> <link rel="stylesheet" href="/web_kanban/static/src/less/kanban_dashboard.less"/> + <link rel="stylesheet" href="/web_kanban/static/src/less/kanban_view.less"/> + <script type="text/javascript" src="/web_kanban/static/src/js/kanban_record.js"></script> + <script type="text/javascript" src="/web_kanban/static/src/js/kanban_quick_create.js"></script> + <script type="text/javascript" src="/web_kanban/static/src/js/kanban_column.js"></script> <script type="text/javascript" src="/web_kanban/static/src/js/kanban_view.js"></script> - <script type="text/javascript" src="/web_kanban/static/src/js/kanban_common.js"></script> + <script type="text/javascript" src="/web_kanban/static/src/js/kanban_widgets.js"></script> <script type="text/javascript" src="/web_kanban/static/src/js/compatibility.js"></script> </xpath> </template> diff --git a/addons/web_kanban_gauge/static/src/js/kanban_gauge.js b/addons/web_kanban_gauge/static/src/js/kanban_gauge.js index 7ffd021af4b..0a9f3a96b44 100644 --- a/addons/web_kanban_gauge/static/src/js/kanban_gauge.js +++ b/addons/web_kanban_gauge/static/src/js/kanban_gauge.js @@ -2,10 +2,10 @@ odoo.define('web_kanban_gauge.widget', function (require) { "use strict"; var core = require('web.core'); -var kanban_common = require('web_kanban.common'); +var kanban_widgets = require('web_kanban.widgets'); -var AbstractField = kanban_common.AbstractField; -var fields_registry = kanban_common.registry; +var AbstractField = kanban_widgets.AbstractField; +var fields_registry = kanban_widgets.registry; var _t = core._t; /** diff --git a/addons/web_tests/tests/test_ui.py b/addons/web_tests/tests/test_ui.py index 8f00a6859a4..43a35a7cfc2 100644 --- a/addons/web_tests/tests/test_ui.py +++ b/addons/web_tests/tests/test_ui.py @@ -11,4 +11,4 @@ class TestUi(openerp.tests.HttpCase): def test_03_js_public(self): self.phantom_js('/',"console.log('ok')","console") def test_04_js_admin(self): - self.phantom_js('/web',"console.log('ok')","openerp.client.action_manager.inner_widget", login='admin') + self.phantom_js('/web',"console.log('ok')","odoo.__DEBUG__.services['web.web_client'].action_manager", login='admin')
\ No newline at end of file diff --git a/addons/website/static/src/css/website.backend.css b/addons/website/static/src/css/website.backend.css index 24879793303..91b3e7c01ea 100644 --- a/addons/website/static/src/css/website.backend.css +++ b/addons/website/static/src/css/website.backend.css @@ -1,7 +1,7 @@ -.oe_WidgetWebsiteButton.danger .oe_publish { - display: none; +.oe_stat_button .o_stat_info:not(.published) > .o_stat_text.oe_publish { + display: none; } -.oe_WidgetWebsiteButton.success .oe_unpublish { +.oe_stat_button .o_stat_info.published > .o_stat_text.oe_unpublish { display: none; } .oe_form_field_html_frame iframe { diff --git a/addons/website/static/src/js/website.backend.js b/addons/website/static/src/js/website.backend.js index b1920dc4cab..d1bb6c2f5b0 100644 --- a/addons/website/static/src/js/website.backend.js +++ b/addons/website/static/src/js/website.backend.js @@ -9,10 +9,13 @@ var form_widgets = require('web.form_widgets'); // required to guarantee that var WidgetWebsiteButton = form_common.AbstractField.extend({ template: 'WidgetWebsiteButton', render_value: function() { + // Hack to replace false value by unpublished because falsy form fields + // have classname o_form_field_empty and are hidden + if (!this.get_value()) { + this.set_value('unpublished'); + } this._super(); - this.$() - .toggleClass("success", this.get_value()) - .toggleClass("danger", !this.get_value()); + this.$el.toggleClass("published", this.get_value() === true); if (this.node.attrs.class) { this.$el.addClass(this.node.attrs.class); } diff --git a/addons/website/static/src/xml/website.backend.xml b/addons/website/static/src/xml/website.backend.xml index 24f8c825816..826e29e261c 100644 --- a/addons/website/static/src/xml/website.backend.xml +++ b/addons/website/static/src/xml/website.backend.xml @@ -1,12 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <templates id="template" xml:space="preserve"> - <t t-name="WidgetWebsiteButton"> - <div class="oe_WidgetWebsiteButton success"> - <strong class="oe_unpublish text-danger">Not<br/>Published</strong> - <strong class="oe_publish text-success">Published</strong> - </div> - </t> + <div t-name="WidgetWebsiteButton" class="o_stat_info published"> + <span class="o_stat_text oe_unpublish text-danger">Not Published</span> + <span class="o_stat_text oe_publish text-success">Published</span> + </div> <t t-name="FieldTextHtmlFrame"> <div class="oe_form_field oe_form_field_html_frame oe_form_embedded_html" t-att-style="widget.node.attrs.style"> diff --git a/addons/website_blog/views/website_blog_views.xml b/addons/website_blog/views/website_blog_views.xml index 8e43249eabf..c281a09ea10 100644 --- a/addons/website_blog/views/website_blog_views.xml +++ b/addons/website_blog/views/website_blog_views.xml @@ -52,8 +52,8 @@ <field name="arch" type="xml"> <form string="Blog Post"> <sheet> - <div class="oe_right oe_button_box"> - <button class="oe_inline oe_stat_button" name="open_website_url" + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" name="open_website_url" type="object" icon="fa-globe"> <field name="website_published" widget="website_button"/> </button> diff --git a/addons/website_crm_partner_assign/views/partner_grade.xml b/addons/website_crm_partner_assign/views/partner_grade.xml index 0605e8b6887..d5f10f5bb87 100644 --- a/addons/website_crm_partner_assign/views/partner_grade.xml +++ b/addons/website_crm_partner_assign/views/partner_grade.xml @@ -8,8 +8,8 @@ <field name="inherit_id" ref="crm_partner_assign.view_partner_grade_form"/> <field name="arch" type="xml"> <xpath expr="//div[@class='oe_title']" position="before"> - <div class="oe_right oe_button_box"> - <button class="oe_inline oe_stat_button" name="open_website_url" + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" name="open_website_url" type="object" icon="fa-globe"> <field name="website_published" widget="website_button"/> </button> diff --git a/addons/website_crm_score/static/src/js/display_filters.js b/addons/website_crm_score/static/src/js/display_filters.js index 859ec6fd218..fc0eea59dda 100644 --- a/addons/website_crm_score/static/src/js/display_filters.js +++ b/addons/website_crm_score/static/src/js/display_filters.js @@ -1,10 +1,10 @@ odoo.define('website_crm_score.filters', function (require) { "use strict"; -var common = require('web_kanban.common'); +var kanban_widgets = require('web_kanban.widgets'); -var Filters = common.AbstractField.extend({ +var Filters = kanban_widgets.AbstractField.extend({ /** bnf grammar of a filter: <filter> ::= <expr> @@ -95,6 +95,6 @@ var Filters = common.AbstractField.extend({ } }); -common.registry.add('filters', Filters); +kanban_widgets.registry.add('filters', Filters); }); diff --git a/addons/website_crm_score/views/marketing.xml b/addons/website_crm_score/views/marketing.xml index 2d5d873664c..0a34c2d8dca 100644 --- a/addons/website_crm_score/views/marketing.xml +++ b/addons/website_crm_score/views/marketing.xml @@ -65,8 +65,8 @@ <field name="arch" type="xml"> <form string="Scores"> <sheet> - <div class="oe_right oe_button_box" name="buttons"> - <button class="oe_inline oe_stat_button" type="action" + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" type="action" name="%(score_leads)d" icon="fa-tags" > <field string="Leads" name="leads_count" widget="statinfo"/> diff --git a/addons/website_crm_score/views/sales.xml b/addons/website_crm_score/views/sales.xml index 565c71501b9..08045f9e38e 100644 --- a/addons/website_crm_score/views/sales.xml +++ b/addons/website_crm_score/views/sales.xml @@ -41,14 +41,13 @@ <field name="model">crm.lead</field> <field name="inherit_id" ref="crm.crm_case_form_view_leads" /> <field name="arch" type="xml"> - <!-- <xpath expr="//button[@class='oe_inline oe_stat_button']" position="after"> --> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" type="action" + <div name="button_box" position="inside"> + <button class="oe_stat_button" type="action" name="%(website_crm_score_pageviews)d" icon="fa-tags" context="{'search_default_lead_id': active_id, 'default_lead_id': active_id}"> <field string="Page views" name="pageviews_count" widget="statinfo"/> </button> - </xpath> + </div> <field name="priority" position="after"> <field name="score" /> </field> @@ -146,14 +145,14 @@ <field name="inherit_id" ref="sales_team.crm_team_view_form"/> <field name="arch" type="xml"> <xpath expr="//div[@class='oe_title']" position="before"> - <div class="oe_right oe_button_box" name="buttons"> - <button class="oe_inline oe_stat_button" type="action" + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" type="action" name="%(crm_score_views_action_graph)d" icon="fa-tags" context="{'search_default_team_id': active_id, 'search_default_unassigned': 1 }" > <field string="Unassigned leads" name="unassigned_leads" widget="statinfo"/> </button> - <button class="oe_inline oe_stat_button" type="action" + <button class="oe_stat_button" type="action" name="%(crm_score_views_action_table)d" icon="fa-tags" context="{'search_default_team_id': active_id}" > @@ -257,4 +256,4 @@ </record> </data> -</openerp>
\ No newline at end of file +</openerp> diff --git a/addons/website_event/views/website_event_backend.xml b/addons/website_event/views/website_event_backend.xml index ddfc26a0e24..10522b254a0 100644 --- a/addons/website_event/views/website_event_backend.xml +++ b/addons/website_event/views/website_event_backend.xml @@ -8,20 +8,20 @@ <field name="inherit_id" ref="event.view_event_form"/> <field name="arch" type="xml"> <!-- add state field in header --> - <xpath expr="//div[@class='oe_right oe_button_box']" position="inside"> + <div name="button_box" position="inside"> <field name="website_url" invisible="1"/> - <button class="oe_inline oe_stat_button" name="open_website_url" + <button class="oe_stat_button" name="open_website_url" type="object" icon="fa-globe"> <field name="website_published" widget="website_button"/> </button> - </xpath> + </div> <xpath expr="//field[@name='organizer_id']" position="after"> <field name="twitter_hashtag"/> </xpath> - <xpath expr="//div[@class='oe_right oe_button_box']/button" position="before"> + <xpath expr="//div[@name='button_box']/button" position="before"> <button name="action_open_badge_editor" type="object" - class="oe_stat_button oe_inline" + class="oe_stat_button" icon="fa-newspaper-o" string="Badge" help="Edit Event Badge"> diff --git a/addons/website_event_track/views/event_backend.xml b/addons/website_event_track/views/event_backend.xml index 7eb6a103d7d..4ae03d65506 100644 --- a/addons/website_event_track/views/event_backend.xml +++ b/addons/website_event_track/views/event_backend.xml @@ -18,8 +18,8 @@ <i class="fa fa-bars fa-lg"/> <ul class="oe_dropdown_menu"> <li><a t-att-href="record.website_url.value" type="url">View Track</a></li> - <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit Track</a></li></t> - <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> + <t t-if="widget.editable"><li><a type="edit">Edit Track</a></li></t> + <t t-if="widget.deletable"><li><a type="delete">Delete</a></li></t> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> </div> @@ -90,9 +90,9 @@ <field name="state" widget="statusbar" clickable="True"/> </header> <sheet string="Track"> - <div class="oe_right oe_button_box"> + <div class="oe_button_box" name="button_box"> <field name="website_url" invisible="1"/> - <button class="oe_inline oe_stat_button" name="open_website_url" + <button class="oe_stat_button" name="open_website_url" type="object" icon="fa-globe"> <field name="website_published" widget="website_button"/> </button> @@ -310,20 +310,20 @@ Tracks define the agenda of your event. These can be a talk, a round table, a me <field name="inherit_id" ref="event.view_event_form"/> <field name="model">event.event</field> <field name="arch" type="xml"> - <xpath expr="//div[@class='oe_right oe_button_box']" position="inside"> + <div name="button_box" position="inside"> <button name="%(action_event_track_from_event)d" type="action" - class="oe_stat_button oe_inline" + class="oe_stat_button" icon="fa-inbox"> <field name="count_tracks" widget="statinfo"/> </button> <button name="%(action_event_sponsor_from_event)d" type="action" - class="oe_stat_button oe_inline" + class="oe_stat_button" icon="fa-users"> - <field name="count_sponsor" widget="statinfo" class="oe_inline" string="Sponsorships"/> + <field name="count_sponsor" widget="statinfo" string="Sponsorships"/> </button> - </xpath> + </div> <xpath expr="//div[@class='oe_title']" position="inside"> <div name="event_checkboxes"> <field name="show_menu"/> @@ -334,8 +334,6 @@ Tracks define the agenda of your event. These can be a talk, a round table, a me <label for="show_track_proposal"/> </div> </xpath> - <xpath expr="//div[@class='oe_right oe_button_box']" position="inside"> - </xpath> </field> </record> diff --git a/addons/website_forum/views/res_users.xml b/addons/website_forum/views/res_users.xml index f0de0cfe855..5844e53cb36 100644 --- a/addons/website_forum/views/res_users.xml +++ b/addons/website_forum/views/res_users.xml @@ -11,12 +11,12 @@ <field name="signature" position="after"> <field name="karma" string="Forum Karma"/> </field> - <xpath expr="//div[contains(@class, 'oe_right oe_button_box')]" position="inside"> - <button class="oe_inline oe_stat_button" name="open_website_url" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="open_website_url" type="object" icon="fa-globe"> <field name="website_published" widget="website_button"/> </button> - </xpath> + </div> </field> </record> diff --git a/addons/website_forum_doc/views/doc.xml b/addons/website_forum_doc/views/doc.xml index 9a32603fcab..f45243445b8 100644 --- a/addons/website_forum_doc/views/doc.xml +++ b/addons/website_forum_doc/views/doc.xml @@ -41,8 +41,8 @@ <div class="oe_dropdown_toggle oe_dropdown_kanban" groups="base.group_user"> <i class="fa fa-bars fa-lg"/> <ul class="oe_dropdown_menu"> - <li t-if="widget.view.is_action_enabled('edit')"><a type="edit">Edit Post</a></li> - <li t-if="widget.view.is_action_enabled('delete')"><a type="delete">Delete</a></li> + <li t-if="widget.editable"><a type="edit">Edit Post</a></li> + <li t-if="widget.deletable"><a type="delete">Delete</a></li> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> </div> diff --git a/addons/website_hr/views/website_hr_view.xml b/addons/website_hr/views/website_hr_view.xml index f4592f80392..72e3f8d8ca8 100644 --- a/addons/website_hr/views/website_hr_view.xml +++ b/addons/website_hr/views/website_hr_view.xml @@ -7,12 +7,12 @@ <field name="model">hr.employee</field> <field name="inherit_id" ref="hr.view_employee_form"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='button_box']" position="inside"> - <button class="oe_inline oe_stat_button" name="open_website_url" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="open_website_url" type="object" icon="fa-globe"> <field name="website_published" widget="website_button"/> </button> - </xpath> + </div> </field> </record> diff --git a/addons/website_hr_recruitment/views/hr_job_views.xml b/addons/website_hr_recruitment/views/hr_job_views.xml index 6edfb88b1fd..6e426caa3a5 100644 --- a/addons/website_hr_recruitment/views/hr_job_views.xml +++ b/addons/website_hr_recruitment/views/hr_job_views.xml @@ -6,12 +6,12 @@ <field name="model">hr.job</field> <field name="inherit_id" ref="hr_recruitment.hr_job_survey"/> <field name="arch" type="xml"> - <xpath expr="//div[@class='oe_button_box']" position="inside"> - <button class="oe_inline oe_stat_button" name="open_website_url" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="open_website_url" type="object" icon="fa-globe"> <field name="website_published" widget="website_button"/> </button> - </xpath> + </div> </field> </record> </data> diff --git a/addons/website_links/views/website_links.xml b/addons/website_links/views/website_links.xml index 8ee1b694b11..8616019de41 100644 --- a/addons/website_links/views/website_links.xml +++ b/addons/website_links/views/website_links.xml @@ -27,7 +27,7 @@ <field name="arch" type="xml"> <form string="Website Link"> <sheet> - <div class="oe_button_box pull-right"> + <div class="oe_button_box" name="button_box"> <button type="object" icon="fa-sign-out" name="action_visit_page" string="Visit Page" class="oe_stat_button"/> diff --git a/addons/website_livechat/views/website_livechat_view.xml b/addons/website_livechat/views/website_livechat_view.xml index 524cc7d4a49..ae1402b7b38 100644 --- a/addons/website_livechat/views/website_livechat_view.xml +++ b/addons/website_livechat/views/website_livechat_view.xml @@ -7,12 +7,12 @@ <field name="model">im_livechat.channel</field> <field name="inherit_id" ref="im_livechat.im_livechat_channel_view_form"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" name="open_website_url" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="open_website_url" type="object" icon="fa-globe"> <field name="website_published" widget="website_button"/> </button> - </xpath> + </div> </field> </record> diff --git a/addons/website_partner/views/res_partner_view.xml b/addons/website_partner/views/res_partner_view.xml index 10f0dd0e9de..343733faa7c 100644 --- a/addons/website_partner/views/res_partner_view.xml +++ b/addons/website_partner/views/res_partner_view.xml @@ -10,15 +10,15 @@ <field eval="18" name="priority"/> <field name="arch" type="xml"> <data> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" name="open_website_url" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="open_website_url" type="object" icon="fa-globe"> <field name="website_published" widget="website_button"/> </button> - </xpath> + </div> </data> </field> </record> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/addons/website_sale/views/views.xml b/addons/website_sale/views/views.xml index dd401c4e8c9..9a0ac67e033 100644 --- a/addons/website_sale/views/views.xml +++ b/addons/website_sale/views/views.xml @@ -29,12 +29,12 @@ <field name="website_sequence"/> </group> </group> - <xpath expr="//div[@name='buttons']" position="inside"> - <button class="oe_inline oe_stat_button" name="open_website_url" + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="open_website_url" type="object" icon="fa-globe"> <field name="website_published" widget="website_button"/> </button> - </xpath> + </div> </field> </record> diff --git a/addons/website_slides/view/website_slides_backend.xml b/addons/website_slides/view/website_slides_backend.xml index 24aad41e2bb..680c090547d 100644 --- a/addons/website_slides/view/website_slides_backend.xml +++ b/addons/website_slides/view/website_slides_backend.xml @@ -40,13 +40,13 @@ <field name="arch" type="xml"> <form string="Slide"> <sheet> - <field name="image" widget="image" class="oe_left oe_avatar" options='{"preview_image": "image_thumb"}'/> - <div class="oe_right oe_button_box" name="buttons"> - <button class="oe_inline oe_stat_button" name="open_website_url" + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" name="open_website_url" type="object" icon="fa-globe"> <field name="website_published" widget="website_button"/> </button> </div> + <field name="image" widget="image" class="oe_left oe_avatar" options='{"preview_image": "image_thumb"}'/> <div class="oe_title oe_left"> <div class="oe_edit_only"> <label for="name" string="Name"/> @@ -194,6 +194,12 @@ <field name="arch" type="xml"> <form string="Channels"> <sheet> + <div class="oe_button_box" name="button_box"> + <button class="oe_stat_button" name="open_website_url" + type="object" icon="fa-globe"> + <field name="website_published" widget="website_button"/> + </button> + </div> <div class="oe_title oe_left"> <div class="oe_edit_only"> <label for="name" string="Name"/> @@ -202,12 +208,6 @@ <field name="name" default_focus="1" placeholder="Name"/> </h1> </div> - <div class="oe_right oe_button_box" name="buttons"> - <button class="oe_inline oe_stat_button" name="open_website_url" - type="object" icon="fa-globe"> - <field name="website_published" widget="website_button"/> - </button> - </div> <field name="category_ids" widget="many2many_tags" placeholder="Categories" context="{'default_channel_id': active_id}" domain="[('channel_id','=', active_id)]"/> <notebook colspan="4"> diff --git a/addons/website_version/views/marketing_view.xml b/addons/website_version/views/marketing_view.xml index 78130f3dc62..b9ab949aae4 100644 --- a/addons/website_version/views/marketing_view.xml +++ b/addons/website_version/views/marketing_view.xml @@ -74,7 +74,7 @@ </header> <sheet string="Exp"> <button icon="fa-line-chart" name="website_version.action_analytic" String="Statistics" action="action" - type="action" class="oe_stat_button pull-right" /> + type="action" class="pull-right" /> <div class="oe_title"> <field name="google_id" invisible="1"/> <label for="name" class="oe_edit_only"/> @@ -173,7 +173,7 @@ <form string="version Form"> <sheet string="Snap"> <group> - <button icon="fa-pencil" name="action_publish" string="Publish" type="object" class="oe_stat_button pull-right"/> + <button icon="fa-pencil" name="action_publish" string="Publish" type="object" class="pull-right"/> </group> <div class="oe_title"> <label for="name" class="oe_edit_only"/> diff --git a/doc/reference/views.rst b/doc/reference/views.rst index 31f37919e08..f3cc37fc97e 100644 --- a/doc/reference/views.rst +++ b/doc/reference/views.rst @@ -569,18 +569,18 @@ Button Box Many relevant actions or links can be displayed in the form. For example, in Opportunity form, the actions "Schedule a Call" and "Schedule a Meeting" take an important place in the use of the CRM. Instead of placing them in the -"More" menu, put them directly in the sheet as buttons (on the top right) to -make them more visible and more easily accessible. +"More" menu, put them directly in the sheet as buttons (on the top) to make +them more visible and more easily accessible. .. image:: forms/header3.png :class: img-responsive Technically, the buttons are placed inside a <div> to group them as a block on -the right-hand side of the sheet. +the top of the sheet. :: - <div class="oe_button_box oe_right"> + <div class="oe_button_box" name="button_box"> <button string="Schedule/Log Call" name="..." type="action"/> <button string="Schedule Meeting" name="action_makeMeeting" type="object"/> </div> diff --git a/openerp/addons/base/ir/ir_actions.xml b/openerp/addons/base/ir/ir_actions.xml index 526e2b203db..214f85a9772 100644 --- a/openerp/addons/base/ir/ir_actions.xml +++ b/openerp/addons/base/ir/ir_actions.xml @@ -259,11 +259,7 @@ <field name="arch" type="xml"> <form string="Server Action"> <sheet> - <div class="oe_title"> - <label for="name" class="oe_edit_only"/> - <h1><field name="name"/></h1> - </div> - <div class="oe_right oe_button_box" name="buttons"> + <div class="oe_button_box" name="button_box"> <field name="menu_ir_values_id" invisible="1"/> <button name="create_action" string="Add in the 'More' menu" type="object" attrs="{'invisible':[('menu_ir_values_id','!=',False)]}" @@ -272,6 +268,10 @@ attrs="{'invisible':[('menu_ir_values_id','=',False)]}" help="Remove the contextual action related to this server action"/> </div> + <div class="oe_title"> + <label for="name" class="oe_edit_only"/> + <h1><field name="name"/></h1> + </div> <group> <group> <field name="type" invisible="1"/> diff --git a/openerp/addons/base/module/module_view.xml b/openerp/addons/base/module/module_view.xml index 99b29647fe8..00716c073d8 100644 --- a/openerp/addons/base/module/module_view.xml +++ b/openerp/addons/base/module/module_view.xml @@ -80,12 +80,12 @@ By <field name="author" class="oe_inline"/> </h3> <div> - <button name="button_immediate_install" states="uninstalled" string="Install" type="object" class="oe_highlight"/> - <button name="button_immediate_upgrade" states="installed" string="Upgrade" type="object" class="oe_highlight"/> - <button name="button_uninstall" states="installed" string="Uninstall" type="object"/> - <button name="button_uninstall_cancel" states="to remove" string="Cancel Uninstall" type="object"/> - <button name="button_upgrade_cancel" states="to upgrade" string="Cancel Upgrade" type="object"/> - <button name="button_install_cancel" states="to install" string="Cancel Install" type="object"/> + <button name="button_immediate_install" states="uninstalled" string="Install" type="object" class="btn btn-primary"/> + <button name="button_immediate_upgrade" states="installed" string="Upgrade" type="object" class="btn btn-primary"/> + <button name="button_uninstall" states="installed" string="Uninstall" type="object" class="btn btn-default"/> + <button name="button_uninstall_cancel" states="to remove" string="Cancel Uninstall" type="object" class="btn btn-default"/> + <button name="button_upgrade_cancel" states="to upgrade" string="Cancel Upgrade" type="object" class="btn btn-default"/> + <button name="button_install_cancel" states="to install" string="Cancel Install" type="object" class="btn btn-default"/> </div> </div> <div class="oe_clear"/> @@ -170,8 +170,8 @@ <t t-if="record.summary.raw_value"><field name="summary"/><br/></t> <i><field name="name" groups="base.group_no_one"/></i> </p> - <a class="btn btn-default btn-sm" type="object" name="button_immediate_install" states="uninstalled">Install</a> - <a t-if="installed" class="btn btn-default btn-sm disabled" disabled="disabled">Installed</a> + <button type="object" class="btn btn-primary pull-right btn-sm" name="button_immediate_install" states="uninstalled">Install</button> + <button type="button" t-if="installed" class="btn btn-default pull-right btn-sm disabled" disabled="disabled">Installed</button> </div> </div> </t> @@ -209,4 +209,4 @@ <menuitem id="menu_module_updates" parent="base.menu_management" sequence="20" action="modules_updates_act_cl"/> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/openerp/addons/base/res/res_company_view.xml b/openerp/addons/base/res/res_company_view.xml index 43f7f2e9c6a..e35606f162a 100644 --- a/openerp/addons/base/res/res_company_view.xml +++ b/openerp/addons/base/res/res_company_view.xml @@ -15,6 +15,9 @@ <field name="arch" type="xml"> <form string="Company"> <sheet> + <div class="oe_button_box" name="button_box"> + <button name="%(preview_report)d" string="Preview Header/Footer" type="action"/> + </div> <div> <field name="logo" widget="image" class="oe_avatar oe_left"/> </div> @@ -90,9 +93,6 @@ <field name="font" class="oe_inline" on_change="onchange_font_name(font, rml_header, rml_header2, rml_header3)" /> <button string="(reload fonts)" name="act_discover_fonts" type="object" class="oe_link" colspan="1"/> </div> - <div class="oe_right oe_button_box" name="button_box" colspan="2"> - <button name="%(preview_report)d" string="Preview Header/Footer" type="action"/> - </div> <group col="1" colspan="2"> <field name="rml_header"/> <field name="rml_header2"/> diff --git a/openerp/addons/base/res/res_partner_view.xml b/openerp/addons/base/res/res_partner_view.xml index 3f94301bfd6..e4a0df2f374 100644 --- a/openerp/addons/base/res/res_partner_view.xml +++ b/openerp/addons/base/res/res_partner_view.xml @@ -114,8 +114,8 @@ <field name="function" placeholder="e.g. Sales Director"/> <field name="user_ids" invisible="1"/> <field name="email" widget="email" required="context.get('force_email', False)" attrs="{'required': [('user_ids','!=', [])]}"/> - <field name="phone"/> - <field name="mobile"/> + <field name="phone" widget="phone"/> + <field name="mobile" widget="phone"/> </group> </form> </field> @@ -128,23 +128,25 @@ <field name="arch" type="xml"> <form string="Partners"> <sheet> - <field name="image" widget='image' class="oe_left oe_avatar" options='{"preview_image": "image_medium", "size": [90, 90]}'/> - <div class="oe_title oe_left"> + <div class="oe_button_box" name="button_box"/> + <field name="image" widget='image' class="oe_avatar" options='{"preview_image": "image_medium", "size": [90, 90]}'/> + <div class="oe_title"> <div class="oe_edit_only"> - <label for="name"/> ( - <field name="is_company" on_change="onchange_type(is_company)" class="oe_inline"/> <label for="is_company" string="Is a Company?"/>) + <label for="name"/> + <field name="is_company" on_change="onchange_type(is_company)" class="oe_inline"/><label for="is_company" string="Is a Company?"/> </div> <h1> <field name="name" default_focus="1" placeholder="Name" /> </h1> - <field name="parent_id" - placeholder="Company" - domain="[('is_company', '=', True)]" context="{'default_is_company': True, 'default_supplier': supplier, 'default_customer': customer}" - attrs="{'invisible': [('is_company','=', True),('parent_id', '=', False)]}" - on_change="onchange_address(use_parent_address, parent_id)"/> - <field name="category_id" widget="many2many_tags" placeholder="Tags..."/> + <div class="o_row"> + <field name="parent_id" + placeholder="Company" + domain="[('is_company', '=', True)]" context="{'default_is_company': True, 'default_supplier': supplier, 'default_customer': customer}" + attrs="{'invisible': [('is_company','=', True),('parent_id', '=', False)]}" + on_change="onchange_address(use_parent_address, parent_id)"/> + <field name="category_id" widget="many2many_tags" placeholder="Tags..."/> + </div> </div> - <div class="oe_right oe_button_box" name="buttons"> </div> <group> <group> @@ -155,31 +157,30 @@ </div> <label for="street" string="Address"/> - <div> + <div class="o_address_format"> <div class="oe_edit_only"> <field name="use_parent_address" class="oe_inline" on_change="onchange_address(use_parent_address, parent_id)" attrs="{'invisible': ['|', ('is_company', '=', True),('parent_id', '=', False)]}"/> <label for="use_parent_address" attrs="{'invisible': ['|', ('is_company', '=', True), ('parent_id', '=', False)]}"/> - </div> - <button name="open_parent" type="object" string="(edit company address)" class="oe_link oe_edit_only" + <button name="open_parent" type="object" string="(edit)" class="oe_link" attrs="{'invisible': ['|',('parent_id','=', False),('use_parent_address','=',False)]}"/> - <field name="street" placeholder="Street..." attrs="{'readonly': [('use_parent_address','=',True)]}"/> - <field name="street2" attrs="{'readonly': [('use_parent_address','=',True)]}"/> - <div class="address_format"> - <field name="city" placeholder="City" style="width: 40%%" attrs="{'readonly': [('use_parent_address','=',True)]}"/> - <field name="state_id" class="oe_no_button" placeholder="State" style="width: 37%%" options='{"no_open": True}' on_change="onchange_state(state_id)" attrs="{'readonly': [('use_parent_address','=',True)]}"/> - <field name="zip" placeholder="ZIP" style="width: 20%%" attrs="{'readonly': [('use_parent_address','=',True)]}"/> </div> - <field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": True, "no_create": True}' attrs="{'readonly': [('use_parent_address','=',True)]}"/> + + <field name="street" placeholder="Street..." class="o_address_street" attrs="{'readonly': [('use_parent_address','=',True)]}"/> + <field name="street2" placeholder="Street 2..." class="o_address_street" attrs="{'readonly': [('use_parent_address','=',True)]}"/> + <field name="city" placeholder="City" class="o_address_city" attrs="{'readonly': [('use_parent_address','=',True)]}"/> + <field name="state_id" class="o_address_state" placeholder="State" options='{"no_open": True}' on_change="onchange_state(state_id)" attrs="{'readonly': [('use_parent_address','=',True)]}"/> + <field name="zip" placeholder="ZIP" class="o_address_zip" attrs="{'readonly': [('use_parent_address','=',True)]}"/> + <field name="country_id" placeholder="Country" class="o_address_country" options='{"no_open": True, "no_create": True}' attrs="{'readonly': [('use_parent_address','=',True)]}"/> </div> <field name="website" widget="url" placeholder="e.g. www.odoo.com"/> </group> <group> <field name="function" placeholder="e.g. Sales Director" attrs="{'invisible': [('is_company','=', True)]}"/> - <field name="phone" placeholder="e.g. +32.81.81.37.00"/> - <field name="mobile"/> + <field name="phone" widget="phone" placeholder="e.g. +32.81.81.37.00"/> + <field name="mobile" widget="phone"/> <field name="fax"/> <field name="user_ids" invisible="1"/> <field name="email" widget="email" attrs="{'required': [('user_ids','!=', [])]}"/> @@ -229,19 +230,15 @@ </a> <div class="oe_module_desc"> <div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_color_border"> - <div class="col-md-12"> - <div> - <h4><a type="open"><field name="name"/></a></h4> - <i><div t-if="record.function.raw_value"> - <field name="function"/></div></i> - <div><a t-if="record.email.raw_value" title="Mail" t-att-href="'mailto:'+record.email.value"> - <field name="email"/> - </a></div> - <div t-if="record.phone.raw_value">Phone: <field name="phone"/></div> - <div t-if="record.mobile.raw_value">Mobile: <field name="mobile"/></div> - <div t-if="record.fax.raw_value">Fax: <field name="fax"/></div> - </div> - </div> + <h4><a type="open"><field name="name"/></a></h4> + <i><div t-if="record.function.raw_value"> + <field name="function"/></div></i> + <div><a t-if="record.email.raw_value" title="Mail" t-att-href="'mailto:'+record.email.value"> + <field name="email"/> + </a></div> + <div t-if="record.phone.raw_value">Phone: <field name="phone"/></div> + <div t-if="record.mobile.raw_value">Mobile: <field name="mobile"/></div> + <div t-if="record.fax.raw_value">Fax: <field name="fax"/></div> </div> </div> </div> @@ -260,8 +257,8 @@ <group> <field name="function" placeholder="e.g. Sales Director"/> <field name="email"/> - <field name="phone"/> - <field name="mobile"/> + <field name="phone" widget="phone"/> + <field name="mobile" widget="phone"/> <field name="title"/> </group> <div> @@ -273,15 +270,13 @@ <field class="oe_inline" name="type"/> </div> <label for="street" string="Address" attrs="{'invisible': [('use_parent_address','=', True)]}"/> - <div attrs="{'invisible': [('use_parent_address','=', True)]}" name="div_address"> - <field name="street" placeholder="Street..."/> - <field name="street2"/> - <div class="address_format"> - <field name="city" placeholder="City" style="width: 40%%"/> - <field name="state_id" class="oe_no_button" placeholder="State" style="width: 37%%" options='{"no_open": True}' on_change="onchange_state(state_id)"/> - <field name="zip" placeholder="ZIP" style="width: 20%%"/> - </div> - <field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": True, "no_create": True}'/> + <div class="o_address_format" attrs="{'invisible': [('use_parent_address','=', True)]}" name="div_address"> + <field name="street" placeholder="Street..." class="o_address_street"/> + <field name="street2" placeholder="Street 2..." class="o_address_street"/> + <field name="city" placeholder="City" class="o_address_city"/> + <field name="state_id" class="o_address_state" placeholder="State" options='{"no_open": True}' on_change="onchange_state(state_id)"/> + <field name="zip" placeholder="ZIP" class="o_address_zip"/> + <field name="country_id" placeholder="Country" class="o_address_country" options='{"no_open": True, "no_create": True}'/> </div> </group> <field name="supplier" invisible="True"/> @@ -343,8 +338,8 @@ <field name="category_id" string="Tag" filter_domain="[('category_id','ilike', self)]"/> <field name="user_id"/> <field name="parent_id" domain="[('is_company','=',1)]" operator="child_of"/> - <group expand="0" string="Group By"> - <filter string="Salesperson" icon="terp-personal" domain="[]" context="{'group_by' : 'user_id'}" /> + <group expand="0" name="group_by" string="Group By"> + <filter name="salesperson" string="Salesperson" icon="terp-personal" domain="[]" context="{'group_by' : 'user_id'}" /> <filter string="Company" context="{'group_by': 'parent_id'}"/> <filter string="Country" context="{'group_by': 'country_id'}"/> </group> @@ -357,7 +352,7 @@ <field name="name">res.partner.kanban</field> <field name="model">res.partner</field> <field name="arch" type="xml"> - <kanban> + <kanban class="o_res_partner_kanban"> <field name="color"/> <field name="display_name"/> <field name="title"/> @@ -377,24 +372,23 @@ <field name="image"/> <templates> <t t-name="kanban-box"> - <div class="oe_kanban_vignette oe_semantic_html_override"> - <a type="open"> + <div class="o_kanban_two_column_tags oe_kanban_global_click o_res_partner_kanban"> + <div class="o_kanban_tags_section oe_kanban_partner_categories"/> + <div class="o_kanban_image"> <t t-if="record.image.raw_value"> - <img t-att-src="kanban_image('res.partner', 'image_small', record.id.value)" class="oe_kanban_image"/> + <img t-att-src="kanban_image('res.partner', 'image_small', record.id.value)"/> </t> <t t-if="!record.image.raw_value"> <t t-if="record.is_company.raw_value === true"> - <img t-att-src='_s + "/base/static/src/img/company_image.png"' class="oe_kanban_image"/> + <img t-att-src='_s + "/base/static/src/img/company_image.png"'/> </t> <t t-if="record.is_company.raw_value === false"> - <img t-att-src='_s + "/base/static/src/img/avatar.png"' class="oe_kanban_image"/> + <img t-att-src='_s + "/base/static/src/img/avatar.png"'/> </t> </t> - </a> + </div> <div class="oe_kanban_details"> - <h4 class="oe_partner_heading"><a type="open"><field name="display_name"/></a></h4> - <div class="oe_kanban_partner_categories"/> - <div class="oe_kanban_partner_links"/> + <strong class="oe_partner_heading"><field name="display_name"/></strong> <ul> <li t-if="record.parent_id.raw_value and !record.function.raw_value"><field name="parent_id"/></li> <li t-if="!record.parent_id.raw_value and record.function.raw_value"><field name="function"/></li> @@ -402,8 +396,9 @@ <li t-if="record.city.raw_value and !record.country_id.raw_value"><field name="city"/></li> <li t-if="!record.city.raw_value and record.country_id.raw_value"><field name="country_id"/></li> <li t-if="record.city.raw_value and record.country_id.raw_value"><field name="city"/>, <field name="country_id"/></li> - <li t-if="record.email.raw_value"><a t-attf-href="mailto:#{record.email.raw_value}"><field name="email"/></a></li> + <li t-if="record.email.raw_value" class="o_text_overflow"><field name="email"/></li> </ul> + <div class="oe_kanban_partner_links"/> </div> </div> @@ -594,4 +589,4 @@ <menuitem action="action_partner_category_form" id="menu_partner_category_form" name="Categories" sequence="1" parent="menu_config_address_book" groups="base.group_no_one"/> </data> -</openerp> +</openerp>
\ No newline at end of file diff --git a/openerp/addons/base/res/res_users_view.xml b/openerp/addons/base/res/res_users_view.xml index 4690927fea7..8d54be9c996 100644 --- a/openerp/addons/base/res/res_users_view.xml +++ b/openerp/addons/base/res/res_users_view.xml @@ -165,7 +165,7 @@ <form string="Users"> <sheet> <field name="id" invisible="1"/> - <div class="oe_right oe_button_box"> + <div class="oe_button_box" name="button_box"> <button string="Change Password" type="action" name="%(change_password_wizard_action)d" class="oe_link" help="Change the user password."/> </div> diff --git a/openerp/addons/base/static/src/css/modules.css b/openerp/addons/base/static/src/css/modules.css index 9d31e8cf7b1..3df2e344dc9 100644 --- a/openerp/addons/base/static/src/css/modules.css +++ b/openerp/addons/base/static/src/css/modules.css @@ -1,19 +1,26 @@ .oe_module_vignette { padding: 4px; + display: flex; } -.oe_module_icon, .oe_module_desc { - display: inline-block; - vertical-align: top; -} + .oe_module_icon { width: 80px; height: 80px; + flex-shrink: 0; + flex-grow: 0; } .oe_module_desc { - width: 210px; font-size: 13px; padding: 0px 5px; + flex-grow: 1; + position: relative; +} + +.oe_module_desc > button { + position: absolute; + bottom: 0; + right: 0; } .oe_module_desc h4 { @@ -33,7 +40,3 @@ margin: 3px 0 5px; } -.oe_module_desc .oe_button { - min-width: 70px; -} - |
