diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/board/static/src | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/board/static/src')
| -rw-r--r-- | addons/board/static/src/img/layout_1-1-1.png | bin | 0 -> 306 bytes | |||
| -rw-r--r-- | addons/board/static/src/img/layout_1-1.png | bin | 0 -> 313 bytes | |||
| -rw-r--r-- | addons/board/static/src/img/layout_1-2.png | bin | 0 -> 313 bytes | |||
| -rw-r--r-- | addons/board/static/src/img/layout_1.png | bin | 0 -> 292 bytes | |||
| -rw-r--r-- | addons/board/static/src/img/layout_2-1.png | bin | 0 -> 304 bytes | |||
| -rw-r--r-- | addons/board/static/src/img/view_todo_arrow.png | bin | 0 -> 3389 bytes | |||
| -rw-r--r-- | addons/board/static/src/js/action_manager_board_action.js | 33 | ||||
| -rw-r--r-- | addons/board/static/src/js/add_to_board_menu.js | 152 | ||||
| -rw-r--r-- | addons/board/static/src/js/board_view.js | 465 | ||||
| -rw-r--r-- | addons/board/static/src/scss/dashboard.scss | 178 | ||||
| -rw-r--r-- | addons/board/static/src/xml/board.xml | 114 |
11 files changed, 942 insertions, 0 deletions
diff --git a/addons/board/static/src/img/layout_1-1-1.png b/addons/board/static/src/img/layout_1-1-1.png Binary files differnew file mode 100644 index 00000000..5eda2823 --- /dev/null +++ b/addons/board/static/src/img/layout_1-1-1.png diff --git a/addons/board/static/src/img/layout_1-1.png b/addons/board/static/src/img/layout_1-1.png Binary files differnew file mode 100644 index 00000000..e72aa3a0 --- /dev/null +++ b/addons/board/static/src/img/layout_1-1.png diff --git a/addons/board/static/src/img/layout_1-2.png b/addons/board/static/src/img/layout_1-2.png Binary files differnew file mode 100644 index 00000000..4b14d7ae --- /dev/null +++ b/addons/board/static/src/img/layout_1-2.png diff --git a/addons/board/static/src/img/layout_1.png b/addons/board/static/src/img/layout_1.png Binary files differnew file mode 100644 index 00000000..69a0e308 --- /dev/null +++ b/addons/board/static/src/img/layout_1.png diff --git a/addons/board/static/src/img/layout_2-1.png b/addons/board/static/src/img/layout_2-1.png Binary files differnew file mode 100644 index 00000000..ed866add --- /dev/null +++ b/addons/board/static/src/img/layout_2-1.png diff --git a/addons/board/static/src/img/view_todo_arrow.png b/addons/board/static/src/img/view_todo_arrow.png Binary files differnew file mode 100644 index 00000000..8633430e --- /dev/null +++ b/addons/board/static/src/img/view_todo_arrow.png diff --git a/addons/board/static/src/js/action_manager_board_action.js b/addons/board/static/src/js/action_manager_board_action.js new file mode 100644 index 00000000..05414572 --- /dev/null +++ b/addons/board/static/src/js/action_manager_board_action.js @@ -0,0 +1,33 @@ +odoo.define('board.ActionManager', function (require) { +"use strict"; + +/** + * The purpose of this file is to patch the ActionManager to properly generate + * the flags for the 'ir.actions.act_window' of model 'board.board'. + */ + +var ActionManager = require('web.ActionManager'); + +ActionManager.include({ + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + * @private + */ + _executeWindowAction: function (action) { + if (action.res_model === 'board.board' && action.view_mode === 'form') { + action.target = 'inline'; + _.extend(action.flags, { + hasActionMenus: false, + hasSearchView: false, + headless: true, + }); + } + return this._super.apply(this, arguments); + }, +}); + +}); diff --git a/addons/board/static/src/js/add_to_board_menu.js b/addons/board/static/src/js/add_to_board_menu.js new file mode 100644 index 00000000..42a4a707 --- /dev/null +++ b/addons/board/static/src/js/add_to_board_menu.js @@ -0,0 +1,152 @@ +odoo.define('board.AddToBoardMenu', function (require) { + "use strict"; + + const Context = require('web.Context'); + const Domain = require('web.Domain'); + const DropdownMenuItem = require('web.DropdownMenuItem'); + const FavoriteMenu = require('web.FavoriteMenu'); + const { sprintf } = require('web.utils'); + const { useAutofocus } = require('web.custom_hooks'); + + const { useState } = owl.hooks; + + /** + * 'Add to board' menu + * + * Component consisiting of a toggle button, a text input and an 'Add' button. + * The first button is simply used to toggle the component and will determine + * whether the other elements should be rendered. + * The input will be given the name (or title) of the view that will be added. + * Finally, the last button will send the name as well as some of the action + * properties to the server to add the current view (and its context) to the + * user's dashboard. + * This component is only available in actions of type 'ir.actions.act_window'. + * @extends DropdownMenuItem + */ + class AddToBoardMenu extends DropdownMenuItem { + constructor() { + super(...arguments); + + this.interactive = true; + this.state = useState({ + name: this.env.action.name || "", + open: false, + }); + + useAutofocus(); + } + + //--------------------------------------------------------------------- + // Private + //--------------------------------------------------------------------- + + /** + * This is the main function for actually saving the dashboard. This method + * is supposed to call the route /board/add_to_dashboard with proper + * information. + * @private + */ + async _addToBoard() { + const searchQuery = this.env.searchModel.get('query'); + const context = new Context(this.env.action.context); + context.add(searchQuery.context); + context.add({ + group_by: searchQuery.groupBy, + orderedBy: searchQuery.orderedBy, + }); + if (searchQuery.timeRanges && searchQuery.timeRanges.hasOwnProperty('fieldName')) { + context.add({ + comparison: searchQuery.timeRanges, + }); + } + let controllerQueryParams; + this.env.searchModel.trigger('get-controller-query-params', params => { + controllerQueryParams = params || {}; + }); + controllerQueryParams.context = controllerQueryParams.context || {}; + const queryContext = controllerQueryParams.context; + delete controllerQueryParams.context; + context.add(Object.assign(controllerQueryParams, queryContext)); + + const domainArray = new Domain(this.env.action.domain || []); + const domain = Domain.prototype.normalizeArray(domainArray.toArray().concat(searchQuery.domain)); + + const evalutatedContext = context.eval(); + for (const key in evalutatedContext) { + if (evalutatedContext.hasOwnProperty(key) && /^search_default_/.test(key)) { + delete evalutatedContext[key]; + } + } + evalutatedContext.dashboard_merge_domains_contexts = false; + + Object.assign(this.state, { + name: $(".o_input").val() || "", + open: false, + }); + + const result = await this.rpc({ + route: '/board/add_to_dashboard', + params: { + action_id: this.env.action.id || false, + context_to_save: evalutatedContext, + domain: domain, + view_mode: this.env.view.type, + name: this.state.name, + }, + }); + if (result) { + this.env.services.notification.notify({ + title: sprintf(this.env._t("'%s' added to dashboard"), this.state.name), + message: this.env._t("Please refresh your browser for the changes to take effect."), + type: 'warning', + }); + } else { + this.env.services.notification.notify({ + message: this.env._t("Could not add filter to dashboard"), + type: 'danger', + }); + } + } + + //--------------------------------------------------------------------- + // Handlers + //--------------------------------------------------------------------- + + /** + * @private + * @param {KeyboardEvent} ev + */ + _onInputKeydown(ev) { + switch (ev.key) { + case 'Enter': + ev.preventDefault(); + this._addToBoard(); + break; + case 'Escape': + // Gives the focus back to the component. + ev.preventDefault(); + ev.target.blur(); + break; + } + } + + //--------------------------------------------------------------------- + // Static + //--------------------------------------------------------------------- + + /** + * @param {Object} env + * @returns {boolean} + */ + static shouldBeDisplayed(env) { + return env.action.type === 'ir.actions.act_window'; + } + } + + AddToBoardMenu.props = {}; + AddToBoardMenu.template = 'AddToBoardMenu'; + + FavoriteMenu.registry.add('add-to-board-menu', AddToBoardMenu, 10); + + return AddToBoardMenu; +}); diff --git a/addons/board/static/src/js/board_view.js b/addons/board/static/src/js/board_view.js new file mode 100644 index 00000000..87b5034f --- /dev/null +++ b/addons/board/static/src/js/board_view.js @@ -0,0 +1,465 @@ +odoo.define('board.BoardView', function (require) { +"use strict"; + +var Context = require('web.Context'); +var config = require('web.config'); +var core = require('web.core'); +var dataManager = require('web.data_manager'); +var Dialog = require('web.Dialog'); +var Domain = require('web.Domain'); +var FormController = require('web.FormController'); +var FormRenderer = require('web.FormRenderer'); +var FormView = require('web.FormView'); +var pyUtils = require('web.py_utils'); +var session = require('web.session'); +var viewRegistry = require('web.view_registry'); + +var _t = core._t; +var _lt = core._lt; +var QWeb = core.qweb; + +var BoardController = FormController.extend({ + custom_events: _.extend({}, FormController.prototype.custom_events, { + change_layout: '_onChangeLayout', + enable_dashboard: '_onEnableDashboard', + save_dashboard: '_saveDashboard', + switch_view: '_onSwitchView', + }), + + /** + * @override + */ + init: function (parent, model, renderer, params) { + this._super.apply(this, arguments); + this.customViewID = params.customViewID; + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @override + */ + getTitle: function () { + return _t("My Dashboard"); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Actually save a dashboard + * + * @returns {Promise} + */ + _saveDashboard: function () { + var board = this.renderer.getBoard(); + var arch = QWeb.render('DashBoard.xml', _.extend({}, board)); + return this._rpc({ + route: '/web/view/edit_custom', + params: { + custom_id: this.customViewID, + arch: arch, + } + }).then(dataManager.invalidate.bind(dataManager)); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {OdooEvent} event + */ + _onChangeLayout: function (event) { + var self = this; + var dialog = new Dialog(this, { + title: _t("Edit Layout"), + $content: QWeb.render('DashBoard.layouts', _.clone(event.data)) + }); + dialog.opened().then(function () { + dialog.$('li').click(function () { + var layout = $(this).attr('data-layout'); + self.renderer.changeLayout(layout); + self._saveDashboard(); + dialog.close(); + }); + }); + dialog.open(); + }, + /** + * We need to intercept switch_view event coming from sub views, because we + * don't actually want to switch view in dashboard, we want to do a + * do_action (which will open the record in a different breadcrumb). + * + * @private + * @param {OdooEvent} event + */ + _onSwitchView: function (event) { + event.stopPropagation(); + this.do_action({ + type: 'ir.actions.act_window', + res_model: event.data.model, + views: [[event.data.formViewID || false, 'form']], + res_id: event.data.res_id, + }); + }, +}); + +var BoardRenderer = FormRenderer.extend({ + custom_events: _.extend({}, FormRenderer.prototype.custom_events, { + update_filters: '_onUpdateFilters', + switch_view: '_onSwitchView', + }), + events: _.extend({}, FormRenderer.prototype.events, { + 'click .oe_dashboard_column .oe_fold': '_onFoldClick', + 'click .oe_dashboard_link_change_layout': '_onChangeLayout', + 'click .oe_dashboard_column .oe_close': '_onCloseAction', + }), + + /** + * @override + */ + init: function (parent, state, params) { + this._super.apply(this, arguments); + this.noContentHelp = params.noContentHelp; + this.actionsDescr = {}; + this._boardSubcontrollers = []; // for board: controllers of subviews + this._boardFormViewIDs = {}; // for board: mapping subview controller to form view id + }, + /** + * Call `on_attach_callback` for each subview + * + * @override + */ + on_attach_callback: function () { + _.each(this._boardSubcontrollers, function (controller) { + if ('on_attach_callback' in controller) { + controller.on_attach_callback(); + } + }); + }, + /** + * Call `on_detach_callback` for each subview + * + * @override + */ + on_detach_callback: function () { + _.each(this._boardSubcontrollers, function (controller) { + if ('on_detach_callback' in controller) { + controller.on_detach_callback(); + } + }); + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @param {string} layout + */ + changeLayout: function (layout) { + var $dashboard = this.$('.oe_dashboard'); + var current_layout = $dashboard.attr('data-layout'); + if (current_layout !== layout) { + var clayout = current_layout.split('-').length, + nlayout = layout.split('-').length, + column_diff = clayout - nlayout; + if (column_diff > 0) { + var $last_column = $(); + $dashboard.find('.oe_dashboard_column').each(function (k, v) { + if (k >= nlayout) { + $(v).find('.oe_action').appendTo($last_column); + } else { + $last_column = $(v); + } + }); + } + $dashboard.toggleClass('oe_dashboard_layout_' + current_layout + ' oe_dashboard_layout_' + layout); + $dashboard.attr('data-layout', layout); + } + }, + /** + * Returns a representation of the current dashboard + * + * @returns {Object} + */ + getBoard: function () { + var self = this; + var board = { + form_title : this.arch.attrs.string, + style : this.$('.oe_dashboard').attr('data-layout'), + columns : [], + }; + this.$('.oe_dashboard_column').each(function () { + var actions = []; + $(this).find('.oe_action').each(function () { + var actionID = $(this).attr('data-id'); + var newAttrs = _.clone(self.actionsDescr[actionID]); + + /* prepare attributes as they should be saved */ + if (newAttrs.modifiers) { + newAttrs.modifiers = JSON.stringify(newAttrs.modifiers); + } + actions.push(newAttrs); + }); + board.columns.push(actions); + }); + return board; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + * @param {Object} params + * @param {jQueryElement} params.$node + * @param {integer} params.actionID + * @param {Object} params.context + * @param {any[]} params.domain + * @param {string} params.viewType + * @returns {Promise} + */ + _createController: function (params) { + var self = this; + return this._rpc({ + route: '/web/action/load', + params: {action_id: params.actionID} + }) + .then(function (action) { + if (!action) { + // the action does not exist anymore + return Promise.resolve(); + } + var evalContext = new Context(params.context).eval(); + if (evalContext.group_by && evalContext.group_by.length === 0) { + delete evalContext.group_by; + } + // tz and lang are saved in the custom view + // override the language to take the current one + var rawContext = new Context(action.context, evalContext, {lang: session.user_context.lang}); + var context = pyUtils.eval('context', rawContext, evalContext); + var domain = params.domain || pyUtils.eval('domain', action.domain || '[]', action.context); + + action.context = context; + action.domain = domain; + + // When creating a view, `action.views` is expected to be an array of dicts, while + // '/web/action/load' returns an array of arrays. + action._views = action.views; + action.views = $.map(action.views, function (view) { return {viewID: view[0], type: view[1]}}); + + var viewType = params.viewType || action._views[0][1]; + var view = _.find(action._views, function (descr) { + return descr[1] === viewType; + }) || [false, viewType]; + return self.loadViews(action.res_model, context, [view]) + .then(function (viewsInfo) { + var viewInfo = viewsInfo[viewType]; + var View = viewRegistry.get(viewType); + + const searchQuery = { + context: context, + domain: domain, + groupBy: typeof context.group_by === 'string' && context.group_by ? + [context.group_by] : + context.group_by || [], + orderedBy: context.orderedBy || [], + }; + + if (View.prototype.searchMenuTypes.includes('comparison')) { + searchQuery.timeRanges = context.comparison || {}; + } + + var view = new View(viewInfo, { + action: action, + hasSelectors: false, + modelName: action.res_model, + searchQuery, + withControlPanel: false, + withSearchPanel: false, + }); + return view.getController(self).then(function (controller) { + self._boardFormViewIDs[controller.handle] = _.first( + _.find(action._views, function (descr) { + return descr[1] === 'form'; + }) + ); + self._boardSubcontrollers.push(controller); + return controller.appendTo(params.$node); + }); + }); + }); + }, + /** + * @private + * @param {Object} node + * @returns {jQueryElement} + */ + _renderTagBoard: function (node) { + var self = this; + // we add the o_dashboard class to the renderer's $el. This means that + // this function has a side effect. This is ok because we assume that + // once we have a '<board>' tag, we are in a special dashboard mode. + this.$el.addClass('o_dashboard'); + + var hasAction = _.detect(node.children, function (column) { + return _.detect(column.children,function (element){ + return element.tag === "action"? element: false; + }); + }); + if (!hasAction) { + return $(QWeb.render('DashBoard.NoContent')); + } + + // We should start with three columns available + node = $.extend(true, {}, node); + + // no idea why master works without this, but whatever + if (!('layout' in node.attrs)) { + node.attrs.layout = node.attrs.style; + } + for (var i = node.children.length; i < 3; i++) { + node.children.push({ + tag: 'column', + attrs: {}, + children: [] + }); + } + + // register actions, alongside a generated unique ID + _.each(node.children, function (column, column_index) { + _.each(column.children, function (action, action_index) { + action.attrs.id = 'action_' + column_index + '_' + action_index; + self.actionsDescr[action.attrs.id] = action.attrs; + }); + }); + + var $html = $('<div>').append($(QWeb.render('DashBoard', {node: node, isMobile: config.device.isMobile}))); + this._boardSubcontrollers = []; // dashboard controllers are reset on re-render + + // render each view + _.each(this.actionsDescr, function (action) { + self.defs.push(self._createController({ + $node: $html.find('.oe_action[data-id=' + action.id + '] .oe_content'), + actionID: _.str.toNumber(action.name), + context: action.context, + domain: Domain.prototype.stringToArray(action.domain, {}), + viewType: action.view_mode, + })); + }); + $html.find('.oe_dashboard_column').sortable({ + connectWith: '.oe_dashboard_column', + handle: '.oe_header', + scroll: false + }).bind('sortstop', function () { + self.trigger_up('save_dashboard'); + }); + + return $html; + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + */ + _onChangeLayout: function () { + var currentLayout = this.$('.oe_dashboard').attr('data-layout'); + this.trigger_up('change_layout', {currentLayout: currentLayout}); + }, + /** + * @private + * @param {MouseEvent} event + */ + _onCloseAction: function (event) { + var self = this; + var $container = $(event.currentTarget).parents('.oe_action:first'); + Dialog.confirm(this, (_t("Are you sure you want to remove this item?")), { + confirm_callback: function () { + $container.remove(); + self.trigger_up('save_dashboard'); + }, + }); + }, + /** + * @private + * @param {MouseEvent} event + */ + _onFoldClick: function (event) { + var $e = $(event.currentTarget); + var $action = $e.closest('.oe_action'); + var id = $action.data('id'); + var actionAttrs = this.actionsDescr[id]; + + if ($e.is('.oe_minimize')) { + actionAttrs.fold = '1'; + } else { + delete(actionAttrs.fold); + } + $e.toggleClass('oe_minimize oe_maximize'); + $action.find('.oe_content').toggle(); + this.trigger_up('save_dashboard'); + }, + /** + * Let FormController know which form view it should display based on the + * window action of the sub controller that is switching view + * + * @private + * @param {OdooEvent} event + */ + _onSwitchView: function (event) { + event.data.formViewID = this._boardFormViewIDs[event.target.handle]; + }, + /** + * Stops the propagation of 'update_filters' events triggered by the + * controllers instantiated by the dashboard to prevent them from + * interfering with the ActionManager. + * + * @private + * @param {OdooEvent} event + */ + _onUpdateFilters: function (event) { + event.stopPropagation(); + }, +}); + +var BoardView = FormView.extend({ + config: _.extend({}, FormView.prototype.config, { + Controller: BoardController, + Renderer: BoardRenderer, + }), + display_name: _lt('Board'), + + /** + * @override + */ + init: function (viewInfo) { + this._super.apply(this, arguments); + this.controllerParams.customViewID = viewInfo.custom_view_id; + }, +}); + +return BoardView; + +}); + + +odoo.define('board.viewRegistry', function (require) { +"use strict"; + +var BoardView = require('board.BoardView'); + +var viewRegistry = require('web.view_registry'); + +viewRegistry.add('board', BoardView); + +}); diff --git a/addons/board/static/src/scss/dashboard.scss b/addons/board/static/src/scss/dashboard.scss new file mode 100644 index 00000000..b11aa5ff --- /dev/null +++ b/addons/board/static/src/scss/dashboard.scss @@ -0,0 +1,178 @@ +.o_dashboard { + + // Dashboard layout + .oe_dashboard_layout_1 .oe_dashboard_column { + &.index_0 { + width: 100%; + } + &.index_1, &.index_2 { + display: none; + } + } + .oe_dashboard_layout_1-1 .oe_dashboard_column { + width: 50%; + &.index_2 { + display: none; + } + } + .oe_dashboard_layout_1-1-1 .oe_dashboard_column { + width: 33%; + } + .oe_dashboard_layout_2-1 .oe_dashboard_column { + &.index_0 { + width: 70%; + } + &.index_1 { + width: 30%; + } + &.index_2 { + display: none; + } + } + .oe_dashboard_layout_1-2 .oe_dashboard_column { + &.index_0 { + width: 30%; + } + &.index_1 { + width: 70%; + } + &.index_2 { + display: none; + } + } + .oe_dashboard_column { + vertical-align: top; + } + + // Layout selector + .oe_dashboard_links { + text-align: right; + margin: 0 4px 6px 0; + } + + // Dashboard content + .oe_dashboard { + width: 100%; + .oe_action { + margin: 0 8px 8px 0; + background-color: white; + border: 1px solid; + border-color: #e5e5e5 #dbdbdb #d2d2d2; + margin: 0 8px 8px 0; + .oe_header { + font-size: 16px; + vertical-align: middle; + margin: 0; + padding: 12px; + &:hover { + cursor: move; + } + .oe_icon { + float: right; + cursor: pointer; + color: #b3b3b3; + &:hover { + color: #666; + text-decoration: none; + } + } + .oe_close:after { + content: "×"; + margin-left: 4px; + } + .oe_minimize:after { + content: "-"; + margin-left: 4px; + } + .oe_maximize:after { + content: "+"; + margin-left: 4px; + } + .oe_header_text { + width: auto; + visibility: hidden; + display: inline-block; + cursor: text; + } + span { + cursor: pointer; + } + } + .oe_header_empty { + padding-top: 0; + padding-bottom: 2px; + } + .oe_button_create { + margin-left: 4px; + padding: 0 4px 0 4px; + height: 16px !important; + } + .oe_content { + padding: 0 12px 12px 12px; + &.oe_folded { + display: none; + } + .o_view_nocontent { + display: none; // we don't have a create button on the dashboard, so no need to display that + } + } + + // Override border of many2manytags defined for form_views + .o_kanban_view .o_kanban_record .oe_kanban_list_many2many .o_field_many2manytags { + border: none; + } + + // Override height of graph. min-height doesn't do the trick + .o_graph_controller canvas { + height: 300px; + } + + // Override height for calendar view to be displayed properly + .o_calendar_view { + height: 100vh; + } + + // Override height for map view to be displayed properly + .o_map_view { + .o_map_container, .o_pin_list_container { + height: calc(100vh - #{$o-navbar-height}); + } + } + } + } +} + +// Layout selector modal +.oe_dashboard_layout_selector { + ul { + white-space: nowrap; + } + li { + margin: 0; + padding: 0; + list-style-type: none; + float: left; + } + .oe_dashboard_selected_layout { + margin-left: -30px; + vertical-align: bottom; + margin-bottom: 10px; + } +} + +// Favorites menu in control panel +.o_add_to_dashboard { + display: none; // hidden by default + max-width: 250px; + width: auto; +} + +@include media-breakpoint-down(sm) { + .o_dashboard .oe_dashboard { + table-layout: fixed; + + .oe_action .oe_header .oe_header_text { + display: none; + } + } +} diff --git a/addons/board/static/src/xml/board.xml b/addons/board/static/src/xml/board.xml new file mode 100644 index 00000000..127c9b3a --- /dev/null +++ b/addons/board/static/src/xml/board.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="utf-8"?> +<template> +<t t-name="DashBoard"> + <t t-if="isMobile"> + <t t-set="node.attrs.layout" t-value="1"/> + </t> + <t t-if="!isMobile"> + <div class="oe_dashboard_links"> + <button type="button" class="button oe_dashboard_link_change_layout btn btn-secondary" + title="Change Layout.."> + <img src="/board/static/src/img/layout_1-1-1.png" width="16" height="16" alt=""/> + <span> Change Layout </span> + </button> + </div> + </t> + <table t-att-data-layout="node.attrs.layout" t-attf-class="oe_dashboard oe_dashboard_layout_#{node.attrs.layout}" cellspacing="0" cellpadding="0" border="0"> + <tr> + <td t-foreach="node.children" t-as="column" t-if="column.tag == 'column'" + t-att-id="'column_' + column_index" t-attf-class="oe_dashboard_column index_#{column_index}"> + + <t t-foreach="column.children" t-as="action" t-if="action.tag == 'action'" t-call="DashBoard.action"/> + </td> + </tr> + </table> +</t> +<t t-name="DashBoard.action"> + <div t-att-data-id="action.attrs.id" class="oe_action"> + <h2 t-attf-class="oe_header #{action.attrs.string ? '' : 'oe_header_empty'}"> + <span class="oe_header_txt"> <t t-esc="action.attrs.string"/> </span> + <input class = "oe_header_text" type="text"/> + <t t-if="!action.attrs.string">&nbsp;</t> + <span class='oe_icon oe_close'></span> + <span class='oe_icon oe_minimize oe_fold' t-if="!action.attrs.fold"></span> + <span class='oe_icon oe_maximize oe_fold' t-if="action.attrs.fold"></span> + </h2> + <div t-att-class="'oe_content' + (action.attrs.fold ? ' oe_folded' : '')"/> + </div> +</t> +<t t-name="DashBoard.layouts"> + <div class="oe_dashboard_layout_selector"> + <p> + <strong>Choose dashboard layout</strong> + </p> + <ul> + <li t-foreach="'1 1-1 1-1-1 1-2 2-1'.split(' ')" t-as="layout" t-att-data-layout="layout"> + <img t-attf-src="/board/static/src/img/layout_#{layout}.png" alt=""/> + <i t-if="layout == currentLayout" class="oe_dashboard_selected_layout fa fa-check fa-lg text-success" aria-label='Layout' role="img" title="Layout"/> + </li> + </ul> + </div> +</t> +<t t-name="DashBoard.NoContent"> + <div class="o_view_nocontent"> + <div class="o_nocontent_help"> + <p class="o_view_nocontent_neutral_face"> + Your personal dashboard is empty + </p><p> + To add your first report into this dashboard, go to any + menu, switch to list or graph view, and click <i>"Add to + Dashboard"</i> in the extended search options. + </p><p> + You can filter and group data before inserting into the + dashboard using the search options. + </p> + </div> + </div> +</t> +<t t-name="DashBoard.xml"> + <form t-att-string="form_title"> + <board t-att-style="style"> + <column t-foreach="columns" t-as="column"> + <action t-foreach="column" t-as="action" t-att="action"/> + </column> + </board> + </form> +</t> +<div t-name="HomeWidget" class="oe_dashboard_home_widget"/> +<t t-name="HomeWidget.content"> + <h3><t t-esc="widget.title"/></h3> + <iframe width="100%" frameborder="0" t-att-src="url"/> +</t> + +<t t-name="AddToBoardMenu" owl="1"> + <li class="o_menu_item o_add_to_board" role="menuitem"> + <button type="button" class="dropdown-item" + t-ref="fallback-focus" + t-on-click="state.open = !state.open" + > + <t>Add to my dashboard</t> + </button> + <t t-if="state.open"> + <div class="dropdown-item-text"> + <input type="text" class="o_input" autofocus="" + t-model.trim="state.name" + t-on-keydown="_onInputKeydown" + /> + </div> + <div class="dropdown-item-text"> + <button type="button" class="btn btn-primary" t-on-click="_addToBoard">Add</button> + </div> + </t> + </li> +</t> + +<t t-name="SearchView.addtodashboard"> + <a href="#" class="dropdown-item o_add_to_dashboard_link o_closed_menu">Add to my Dashboard</a> + <div class="dropdown-item-text o_add_to_dashboard"> + <input class="o_input o_add_to_dashboard_input" type="text"/> + </div> + <div class="dropdown-item-text o_add_to_dashboard"> + <button type="button" class="btn btn-primary o_add_to_dashboard_button">Add</button> + </div> +</t> +</template> |
