diff --git a/odoo_dynamic_dashboard/README.rst b/odoo_dynamic_dashboard/README.rst old mode 100755 new mode 100644 index cf23cfbbb..efc8c394f --- a/odoo_dynamic_dashboard/README.rst +++ b/odoo_dynamic_dashboard/README.rst @@ -3,33 +3,32 @@ :alt: License: AGPL-3 Odoo Dynamic Dashboard -====================== +========================== * Dynamically Arrange the dashboard to get the information that are relevant to your business, department, or a specific process or need. Configuration ============= -* No additional configurations needed. +- No configuration needed + +License +======= +Affero General Public License v3.0 (AGPL v3) +(https://www.gnu.org/licenses/agpl-3.0-standalone.html) Company ------- * `Cybrosys Techno Solutions `__ -License -------- -Affero General Public License, Version 3 (AGPL-3). -(https://www.gnu.org/licenses/agpl-3.0-standalone.html) - Credits ------- -* Developers : Irfan, Afras, - (V16) Amal Prasad, - (V17) Arjun S, +* Developers: (V17) Arjun S, + (V16) Robin, Afra MP * Contact: odoo@cybrosys.com Contacts -------- * Mail Contact : odoo@cybrosys.com -* Website : https://cybrosys.com +* Website : http://www.cybrosys.com Bug Tracker ----------- @@ -39,10 +38,8 @@ Maintainer ========== .. image:: https://cybrosys.com/images/logo.png :target: https://cybrosys.com - This module is maintained by Cybrosys Technologies. - -For support and more information, please visit `Our Website `__ +For support and more information, please visit https://www.cybrosys.com Further information =================== diff --git a/odoo_dynamic_dashboard/__init__.py b/odoo_dynamic_dashboard/__init__.py index 57ce4b1c1..027e09ba9 100644 --- a/odoo_dynamic_dashboard/__init__.py +++ b/odoo_dynamic_dashboard/__init__.py @@ -19,5 +19,6 @@ # If not, see . # ############################################################################# -from . import models from . import controllers +from . import models +from . import wizard diff --git a/odoo_dynamic_dashboard/__manifest__.py b/odoo_dynamic_dashboard/__manifest__.py index 2566712ef..421ca4b06 100644 --- a/odoo_dynamic_dashboard/__manifest__.py +++ b/odoo_dynamic_dashboard/__manifest__.py @@ -21,34 +21,42 @@ ############################################################################# { 'name': "Odoo Dynamic Dashboard", - 'version': '17.0.1.0.0', - 'category': 'Extra Tools ', - 'summary': """Odoo Dynamic Dashboard, Dynamic Dashboard, Odoo Dashboard, Dynamic Dashbaord, AI Dashboard, Odoo17 Dashboard, Dashboard, Odoo17, Configurable Dashboard""", - 'description': """Create Configurable Dashboard Dynamically to get the - information that are relevant to your business, department, - or a specific process or need, Dynamic Dashboard, Dashboard , - Dashboard Odoo""", + 'version': '17.0.2.0.0', + 'category': 'Productivity', + 'summary': """Create Configurable Dashboards Easily""", + 'description': """Create Configurable Odoo Dynamic Dashboard to get the + information that are relevant to your business, department, or a specific + process or need""", 'author': 'Cybrosys Techno Solutions', 'company': 'Cybrosys Techno Solutions', 'maintainer': 'Cybrosys Techno Solutions', - 'website': 'https://www.cybrosys.com', + 'website': "https://www.cybrosys.com", 'depends': ['web'], 'data': [ 'security/ir.model.access.csv', - 'views/dashboard_view.xml', - 'views/dashboard_menu_view.xml', - 'views/dynamic_block_view.xml', + 'data/dashboard_theme_data.xml', + 'views/dashboard_views.xml', + 'views/dynamic_block_views.xml', + 'views/dashboard_menu_views.xml', + 'views/dashboard_theme_views.xml', + 'wizard/dashboard_mail_views.xml', ], 'assets': { 'web.assets_backend': [ - 'odoo_dynamic_dashboard/static/src/js/**/*.js', + 'odoo_dynamic_dashboard/static/src/css/**/*.css', 'odoo_dynamic_dashboard/static/src/scss/**/*.scss', + 'odoo_dynamic_dashboard/static/src/js/**/*.js', 'odoo_dynamic_dashboard/static/src/xml/**/*.xml', + 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css', + 'odoo_dynamic_dashboard/static/lib/js/interactjs.js', ], }, - 'images': ['static/description/banner.png'], + 'images': ['static/description/banner.jpg'], 'license': "AGPL-3", 'installable': True, 'auto_install': False, 'application': True, } + + + diff --git a/odoo_dynamic_dashboard/controllers/odoo_dynamic_dashboard.py b/odoo_dynamic_dashboard/controllers/odoo_dynamic_dashboard.py index 8a5336761..93ee85789 100644 --- a/odoo_dynamic_dashboard/controllers/odoo_dynamic_dashboard.py +++ b/odoo_dynamic_dashboard/controllers/odoo_dynamic_dashboard.py @@ -24,38 +24,11 @@ from odoo.http import request class DynamicDashboard(http.Controller): - """ - This is the class DynamicDashboard which is the subclass of the class - http.Controller - """ + """Class to search and filter values in dashboard""" - @http.route('/create/tile', type='json', auth='user') - def tile_creation(self, **kw): - """This is the method to create the tile when create on the button - ADD BLOCK""" - tile_type = kw.get('type') - action_id = kw.get('action_id') - request.env['dashboard.block'].get_dashboard_vals(action_id) - tile_id = request.env['dashboard.block'].sudo().create({ - 'name': 'New Block', - 'type': tile_type, - 'tile_color': '#1f6abb', - 'text_color': '#FFFFFF', - 'fa_icon': 'fa fa-money', - 'fa_color': '#132e45', - 'edit_mode': True, - 'client_action': int(action_id), - }) - return {'id': tile_id.id, 'name': tile_id.name, 'type': tile_type, 'icon': 'fa fa-money', - 'color': '#1f6abb', - 'tile_color': '#1f6abb', - 'text_color': '#FFFFFF', - 'icon_color': '#1f6abb'} - - @http.route('/get/values', type='json', auth='user') - def get_value(self, **kw): - """This is the method get_value which will get the records inside the - tile""" - action_id = kw.get('action_id') - datas = request.env['dashboard.block'].get_dashboard_vals(action_id) - return datas + @http.route('/custom_dashboard/search_input_chart', type='json', + auth="public", website=True) + def dashboard_search_input_chart(self, search_input): + """Function to filter search input in dashboard""" + return request.env['dashboard.block'].search([ + ('name', 'ilike', search_input)]).ids diff --git a/odoo_dynamic_dashboard/data/dashboard_theme_data.xml b/odoo_dynamic_dashboard/data/dashboard_theme_data.xml new file mode 100644 index 000000000..29c052ae3 --- /dev/null +++ b/odoo_dynamic_dashboard/data/dashboard_theme_data.xml @@ -0,0 +1,12 @@ + + + + + + Demo + #4158D0 + #C850C0 + #FFCC70 + + + \ No newline at end of file diff --git a/odoo_dynamic_dashboard/doc/RELEASE_NOTES.md b/odoo_dynamic_dashboard/doc/RELEASE_NOTES.md old mode 100755 new mode 100644 index c402d045b..b34341b2f --- a/odoo_dynamic_dashboard/doc/RELEASE_NOTES.md +++ b/odoo_dynamic_dashboard/doc/RELEASE_NOTES.md @@ -1,7 +1,13 @@ ## Module -#### 02.03.2024 +#### 18.05.2024 #### Version 17.0.1.0.0 -#### ADD +##### ADD +- Initial commit for Odoo Dynamic Dashboard -- Initial Commit for Odoo Dynamic Dashboard +## Module + +#### 20.07.2024 +#### Version 17.0.2.0.0 +##### UPDT +- New Features has been Added. \ No newline at end of file diff --git a/odoo_dynamic_dashboard/models/__init__.py b/odoo_dynamic_dashboard/models/__init__.py index e87ef0c37..33f213fd2 100644 --- a/odoo_dynamic_dashboard/models/__init__.py +++ b/odoo_dynamic_dashboard/models/__init__.py @@ -20,6 +20,6 @@ # ############################################################################# from . import dashboard_block -from . import dashboard_block_line from . import dashboard_menu +from . import dashboard_theme from . import domain_to_sql diff --git a/odoo_dynamic_dashboard/models/dashboard_block.py b/odoo_dynamic_dashboard/models/dashboard_block.py index 60595f2d9..a2f66a378 100644 --- a/odoo_dynamic_dashboard/models/dashboard_block.py +++ b/odoo_dynamic_dashboard/models/dashboard_block.py @@ -19,140 +19,164 @@ # If not, see . # ############################################################################# -from odoo import models, fields, _ -from odoo.exceptions import ValidationError -from odoo.osv import expression from ast import literal_eval +from odoo import api, fields, models +from odoo.osv import expression class DashboardBlock(models.Model): - """Creates the model Dashboard Blocks""" + """Class is used to create charts and tiles in dashboard""" _name = "dashboard.block" - _description = "Dashboard Blocks" + _description = "Dashboard Block" def get_default_action(self): - """This is the method get_default_action which will return the default - action id.""" + """Function to get values from dashboard if action_id is true return + id else return false""" action_id = self.env.ref( - 'odoo_dynamic_dashboard.dynamic_dashboard_action') + 'odoo_dynamic_dashboard.dashboard_view_action') if action_id: return action_id.id return False name = fields.Char(string="Name", help='Name of the block') - field_id = fields.Many2one('ir.model.fields', string='Measured Field', - domain="[('store', '=', True), ('model_id', '=', model_id), ('ttype', 'in', ['float','integer','monetary'])]", - help='Measured field for the block') - fa_icon = fields.Char(string="Icon", help='Icon for the block') - graph_size = fields.Selection( - selection=[("col-lg-4", "Small"), ("col-lg-6", "Medium"), - ("col-lg-12", "Large")], - string="Graph Size", default='col-lg-4', help="Size of the graph") + fa_icon = fields.Char(string="Icon", help="Add icon for tile") operation = fields.Selection( selection=[("sum", "Sum"), ("avg", "Average"), ("count", "Count")], string="Operation", - help='Tile Operation that needs to bring values for tile') + help='Tile Operation that needs to bring values for tile', + required=True) graph_type = fields.Selection( selection=[("bar", "Bar"), ("radar", "Radar"), ("pie", "Pie"), - ("line", "Line"), ("doughnut", "Doughnut")], + ("polarArea", "polarArea"), ("line", "Line"), + ("doughnut", "Doughnut")], string="Chart Type", help='Type of Chart') - measured_field = fields.Many2one("ir.model.fields", string="Measured Field", - help='Measure field for the chart') - client_action = fields.Many2one('ir.actions.client', - default=get_default_action, - string="Client Action", - help='Client Action for the dashboard ' - 'block') + measured_field_id = fields.Many2one("ir.model.fields", + string="Measured Field", + help="Select the Measured") + client_action_id = fields.Many2one('ir.actions.client', + string="Client action", + default=get_default_action, + help="Client action") type = fields.Selection( - selection=[("graph", "Chart"), ("tile", "Tile")], string="Type", - help='Type of Block ie, Chart or Tile') - x_axis = fields.Char(string="X-Axis", help="X-axis for the chart") - y_axis = fields.Char(string="Y-Axis", help="Y-axis for the chart") - group_by = fields.Many2one("ir.model.fields", store=True, - string="Group by(Y-Axis)", - help='Field value for Y-Axis', - domain="[('store', '=', True)]") + selection=[("graph", "Chart"), ("tile", "Tile")], + string="Type", help='Type of Block ie, Chart or Tile') + x_axis = fields.Char(string="X-Axis", help="Chart X-axis") + y_axis = fields.Char(string="Y-Axis", help="Chart Y-axis") + height = fields.Char(string="Height ", help="Height of the block") + width = fields.Char(string="Width", help="Width of the block") + translate_x = fields.Char(string="Translate_X", + help="x value for the style transform translate") + translate_y = fields.Char(string="Translate_Y", + help="y value for the style transform translate") + data_x = fields.Char(string="Data_X", help="Data x value for resize") + data_y = fields.Char(string="Data_Y", help="Data y value for resize") + group_by_id = fields.Many2one("ir.model.fields", store=True, + string="Group by(Y-Axis)", + help='Field value for Y-Axis') tile_color = fields.Char(string="Tile Color", help='Primary Color of Tile') text_color = fields.Char(string="Text Color", help='Text Color of Tile') + val_color = fields.Char(string="Value Color", help='Value Color of Tile') fa_color = fields.Char(string="Icon Color", help='Icon Color of Tile') - filter = fields.Char(string="Filter", help='Filter for Tile') + filter = fields.Char(string="Filter", help="Add filter") model_id = fields.Many2one('ir.model', string='Model', - help='Model for Tile') - model_name = fields.Char(related='model_id.model', readonly=True, - string="Model Name", help='Model Name of Tile') - filter_by = fields.Many2one("ir.model.fields", string=" Filter By", - help="Filter By for Tile") - filter_values = fields.Char(string="Filter Values", - help="Filter Values for tiles accordingly") - sequence = fields.Integer(string="Sequence", - help="sequence of the dashboard") - edit_mode = fields.Boolean(default=False, invisible=True, - string="Edit Mode", help="Edit mode of the tile") + help="Select the module name") + model_name = fields.Char(related='model_id.model', string="Model Name", + help="Added model_id model") + edit_mode = fields.Boolean(string="Edit Mode", + help="Enable to edit chart and tile", + default=False, invisible=True) - def get_dashboard_vals(self, action_id): - """Dashboard block values""" + @api.onchange('model_id') + def _onchange_model_id(self): + if self.operation or self.measured_field_id: + self.operation = False + self.measured_field_id = False + + def get_dashboard_vals(self, action_id, start_date=None, end_date=None): + """Fetch block values from js and create chart""" block_id = [] for rec in self.env['dashboard.block'].sudo().search( - [('client_action', '=', int(action_id))]): + [('client_action_id', '=', int(action_id))]): if rec.filter is False: rec.filter = "[]" filter_list = literal_eval(rec.filter) filter_list = [filter_item for filter_item in filter_list if not ( isinstance(filter_item, tuple) and filter_item[ 0] == 'create_date')] - vals = { - 'id': rec.id, - 'name': rec.name, - 'type': rec.type, - 'graph_type': rec.graph_type, - 'icon': rec.fa_icon, - 'cols': rec.graph_size, - 'color': rec.tile_color if rec.tile_color else '#1f6abb;', - 'text_color': rec.text_color if rec.text_color else '#FFFFFF;', - 'icon_color': rec.fa_color if rec.fa_color else '#1f6abb;', - 'tile_color': rec.tile_color if rec.tile_color else '#FFFFFF;', - 'model_name': rec.model_name, - 'measured_field': rec.measured_field.field_description if rec.measured_field else None, - 'y_field': rec.measured_field.name, - 'x_field': rec.group_by.name, - 'operation': rec.operation, - 'domain': filter_list - } + rec.filter = repr(filter_list) + vals = {'id': rec.id, 'name': rec.name, 'type': rec.type, + 'graph_type': rec.graph_type, 'icon': rec.fa_icon, + 'model_name': rec.model_name, + 'color': f'background-color: {rec.tile_color};' if rec.tile_color else '#1f6abb;', + 'text_color': f'color: {rec.text_color};' if rec.text_color else '#FFFFFF;', + 'val_color': f'color: {rec.val_color};' if rec.val_color else '#FFFFFF;', + 'icon_color': f'color: {rec.tile_color};' if rec.tile_color else '#1f6abb;', + 'height': rec.height, + 'width': rec.width, + 'translate_x': rec.translate_x, + 'translate_y': rec.translate_y, + 'data_x': rec.data_x, + 'data_y': rec.data_y, + 'domain': filter_list, + } domain = [] if rec.filter: domain = expression.AND([literal_eval(rec.filter)]) if rec.model_name: if rec.type == 'graph': - query = self.env[rec.model_name].get_query(domain, - rec.operation, - rec.measured_field, - group_by=rec.group_by) - try: - self._cr.execute(query) - except Exception as exc: - raise ValidationError( - _(f"Could'nt fetch data try another group by field for {rec.name} block")) from exc + self._cr.execute(self.env[rec.model_name].get_query(domain, + rec.operation, + rec.measured_field_id, + start_date, + end_date, + group_by=rec.group_by_id)) records = self._cr.dictfetchall() x_axis = [] for record in records: - x_axis.append(record.get(rec.group_by.name)) + if record.get('name') and type( + record.get('name')) == dict: + x_axis.append(record.get('name')[self._context.get( + 'lang') or 'en_US']) + else: + x_axis.append(record.get(rec.group_by_id.name)) y_axis = [] for record in records: y_axis.append(record.get('value')) vals.update({'x_axis': x_axis, 'y_axis': y_axis}) else: - query = self.env[rec.model_name].get_query(domain, - rec.operation, - rec.measured_field) - self._cr.execute(query) + self._cr.execute(self.env[rec.model_name].get_query(domain, + rec.operation, + rec.measured_field_id, + start_date, + end_date)) records = self._cr.dictfetchall() magnitude = 0 total = records[0].get('value') while abs(total) >= 1000: magnitude += 1 total /= 1000.0 - val = f'{total:.2f}{" KMGTP"[magnitude]}' if magnitude else f'{total:.2f}' + val = '%.2f%s' % ( + total, ['', 'K', 'M', 'G', 'T', 'P'][magnitude]) records[0]['value'] = val vals.update(records[0]) block_id.append(vals) return block_id + + def get_save_layout(self, grid_data_list): + """Function fetch edited values while edit layout of the chart or tile + and save values in a database""" + for data in grid_data_list: + block = self.browse(int(data['id'])) + if data.get('data-x'): + block.write({ + 'translate_x': f"{data['data-x']}px", + 'translate_y': f"{data['data-y']}px", + 'data_x': data['data-x'], + 'data_y': data['data-y'], + }) + if data.get('height'): + block.write({ + 'height': f"{data['height']}px", + 'width': f"{data['width']}px", + }) + return True diff --git a/odoo_dynamic_dashboard/models/dashboard_menu.py b/odoo_dynamic_dashboard/models/dashboard_menu.py index 37220bec2..dd9101cd1 100644 --- a/odoo_dynamic_dashboard/models/dashboard_menu.py +++ b/odoo_dynamic_dashboard/models/dashboard_menu.py @@ -23,77 +23,58 @@ from odoo import api, fields, models class DashboardMenu(models.Model): - """ - This is the class DashboardMenu which is the subclass of the class Model - which is here used to create the model dashboard.menu. - """ + """Class to create new dashboard menu""" _name = "dashboard.menu" _description = "Dashboard Menu" - name = fields.Char(string="Name", help="Name of the dashboard") - parent_id = fields.Many2one('ir.ui.menu', string="Menu", - help="Parent of the dashboard") + name = fields.Char(string="Name", ondelete='cascade', + help="Enter a name for the dashboard menu") + menu_id = fields.Many2one('ir.ui.menu', string="Parent Menu", + help="Parent Menu Location of New Dashboard", + ondelete='cascade') group_ids = fields.Many2many('res.groups', string='Groups', - related='parent_id.groups_id', - help="User need to be at least in one " - "of these groups to see the menu") + related='menu_id.groups_id', + help="User need to be at least in one of " + "these groups to see the menu") client_action_id = fields.Many2one('ir.actions.client', - string='Client Action', - help="Client Action Related " - "to the dashboard") - menu_id = fields.Many2one('ir.ui.menu', string="Created Menu", - help="Created menu") + string="Client Action", + help="Client action of the " + "corresponding dashboard menu") @api.model def create(self, vals): - """ - Summary: - This is the create function of the model DashboardMenu which is - triggered when creating a new record in this model. - Args: - vals: - The values when the user creating a new record. - Returns: - res: - Returns the created record at the end - """ - values = { + """Function to create new dashboard menu""" + action_id = self.env['ir.actions.client'].create({ 'name': vals['name'], - 'tag': 'owl.dynamic_dashboard', - } - action_id = self.env['ir.actions.client'].create(values) + 'tag': 'OdooDynamicDashboard', + }) vals['client_action_id'] = action_id.id - menu_id = self.env['ir.ui.menu'].create({ + self.env['ir.ui.menu'].create({ 'name': vals['name'], - 'parent_id': vals['parent_id'], - 'action': f'ir.actions.client,{action_id.id}' + 'parent_id': vals['menu_id'], + 'action': 'ir.actions.client,%d' % (action_id.id,) }) - res = super(DashboardMenu, self).create(vals) - res.menu_id = menu_id.id - return res + return super(DashboardMenu, self).create(vals) def write(self, vals): - """ - Summary: - This is the write function of the model DashboardMenu which is - triggered when changing any value in the corresponding record. - Args: - vals: - The values when the user creating a editing an record. - Returns: - Returns the updated record at the end - """ - if self.menu_id: - self.menu_id.update(vals) + """Function to save edited data in dashboard menu""" + for rec in self: + client_act_id = rec['client_action_id'].id + self.env['ir.ui.menu'].search( + [('parent_id', '=', rec['menu_id'].id), + ('action', '=', f'ir.actions.client,{client_act_id}')]).write({ + 'name': vals['name'] if 'name' in vals.keys() else rec['name'], + 'parent_id': vals['menu_id'] if 'menu_id' in vals.keys() else + rec['menu_id'], + 'action': f'ir.actions.client,{client_act_id}' + }) return super(DashboardMenu, self).write(vals) def unlink(self): - """ - Summary: - This is the unlink function of the model DashboardMenu which is - triggered when unlinking any record in this model. - Returns: - Returns the record delete. - """ - self.menu_id.unlink() + """Delete dashboard along with menu item""" + for rec in self: + self.env['ir.ui.menu'].search( + [('parent_id', '=', rec['menu_id'].id), + ('action', '=', + f'ir.actions.client,{rec["client_action_id"].id}')]).unlink() return super(DashboardMenu, self).unlink() diff --git a/odoo_dynamic_dashboard/models/dashboard_theme.py b/odoo_dynamic_dashboard/models/dashboard_theme.py new file mode 100644 index 000000000..1f1787bff --- /dev/null +++ b/odoo_dynamic_dashboard/models/dashboard_theme.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import api, fields, models + + +class DashboardTheme(models.Model): + _name = 'dashboard.theme' + _description = 'Dashboard Theme' + + name = fields.Char(string='Theme Name', help='Name of the theme') + color_x = fields.Char(string='Color X', help='Select the color_x for theme', + default='#4158D0') + color_y = fields.Char(string='Color Y', help='Select the color_y for theme', + default='#C850C0') + color_z = fields.Char(string='Color Z', help='Select the color_z for theme', + default='#FFCC70') + body = fields.Html(string='Body', help='Preview of the theme will be shown') + style = fields.Char(string='Style', + help='It store the style of the gradient') + + @api.constrains('name', 'color_x', 'color_y', 'color_z') + def save_record(self): + """ + Function for saving the datas including body and style + """ + self.body = f"
" + self.style = f"background-image: linear-gradient(50deg, {self.color_x} 0%, {self.color_y} 46%, {self.color_z} 100%);" + + def get_records(self): + """ + Function for returning all records with fields name and style + """ + records = self.search_read([], ['name', 'style']) + return records diff --git a/odoo_dynamic_dashboard/models/domain_to_sql.py b/odoo_dynamic_dashboard/models/domain_to_sql.py index 47c3c3ef1..f0de3a4df 100644 --- a/odoo_dynamic_dashboard/models/domain_to_sql.py +++ b/odoo_dynamic_dashboard/models/domain_to_sql.py @@ -22,46 +22,53 @@ from odoo import models -def get_query(self, args, operation, field, group_by=False, apply_ir_rules=False): - """Dashboard block Query Creation""" +def get_query(self, args, operation, field, start_date=None, end_date=None, + group_by=False, apply_ir_rules=False): + """ Dashboard block Query Creation """ query = self._where_calc(args) if apply_ir_rules: self._apply_ir_rules(query, 'read') if operation and field: - data = 'COALESCE(%s("%s".%s),0) AS value' % (operation.upper(), self._table, field.name) + data = 'COALESCE(%s("%s".%s),0) AS value' % ( + operation.upper(), self._table, field.name) join = '' group_by_str = '' if group_by: if group_by.ttype == 'many2one': relation_model = group_by.relation.replace('.', '_') join = ' INNER JOIN %s on "%s".id = "%s".%s' % ( - relation_model, relation_model, self._table, group_by.name) + relation_model, relation_model, self._table, group_by.name) rec_name = self.env[group_by.relation]._rec_name_fallback() - data = data + ',"%s".%s AS %s' % (relation_model, rec_name, group_by.name) + data = data + ',"%s".%s AS %s' % ( + relation_model, rec_name, group_by.name) group_by_str = ' Group by "%s".%s' % (relation_model, rec_name) else: data = data + ',"%s".%s' % (self._table, group_by.name) - group_by_str = ' Group by "%s".%s' % (self._table, str(group_by.name)) + group_by_str = ' Group by "%s".%s' % ( + self._table, str(group_by.name)) else: data = '"%s".id' % (self._table) from_clause, where_clause, where_clause_params = query.get_sql() where_str = where_clause and (" WHERE %s" % where_clause) or '' - if 'company_id' in self._fields: - if len(self.env.companies.ids) > 1: - operator = 'in' - company = str(tuple(self.env.companies.ids)) - else: - operator = '=' - company = self.env.companies.ids[0] - if where_str == '': - add = ' where' - else: - add = ' and' - multicompany_condition = '%s "%s".company_id %s %s' % (add, self._table, operator, company) + if start_date and start_date != 'null': + start_date_query = f' AND ({from_clause}."create_date" >= \'{start_date}\')' + else: + start_date_query = '' + if end_date and end_date != 'null': + end_date_query = f' AND ({from_clause}."create_date" <= \'{end_date}\')' else: - multicompany_condition = '' + end_date_query = '' + query_str = 'SELECT %s FROM ' % data + from_clause + join + where_str + start_date_query + end_date_query + group_by_str + def format_param(x): + if not isinstance(x, tuple): + return "'" + str(x) + "'" + elif isinstance(x, tuple) and len(x) == 1: + return "(" + str(x[0]) + ")" + else: + return str(x) + + exact_query = query_str % tuple(map(format_param, where_clause_params)) + return exact_query + - query_str = 'SELECT %s FROM ' % data + from_clause + join + where_str + multicompany_condition + group_by_str - where_clause_params = map(lambda x: "'" + str(x) + "'", where_clause_params) - return query_str % tuple(where_clause_params) models.BaseModel.get_query = get_query diff --git a/odoo_dynamic_dashboard/security/ir.model.access.csv b/odoo_dynamic_dashboard/security/ir.model.access.csv index 726d7d6b2..fcade0113 100644 --- a/odoo_dynamic_dashboard/security/ir.model.access.csv +++ b/odoo_dynamic_dashboard/security/ir.model.access.csv @@ -1,4 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_dashboard_block,access.dashboard.block,model_dashboard_block,base.group_user,1,1,1,1 access_dashboard_menu,access.dashboard.menu,model_dashboard_menu,base.group_user,1,1,1,1 -access_dashboard_block_line,access.dashboard.block.line,model_dashboard_block_line,base.group_user,1,1,1,1 +access_dashboard_theme,access.dashboard.theme,model_dashboard_theme,base.group_user,1,1,1,1 +access_dashboard_mail,access.dashboard.mail,model_dashboard_mail,base.group_user,1,1,1,1 diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/1.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/1.png deleted file mode 100644 index f78d925dc..000000000 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/1.png and /dev/null differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/10.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/10.png deleted file mode 100644 index d4a8f6dde..000000000 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/10.png and /dev/null differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/11.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/11.png deleted file mode 100644 index 23fedfae9..000000000 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/11.png and /dev/null differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/2.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/2.png deleted file mode 100644 index e9fc46be6..000000000 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/2.png and /dev/null differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/3.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/3.png deleted file mode 100644 index dfc002cb9..000000000 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/3.png and /dev/null differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/4.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/4.png deleted file mode 100644 index 3781835ff..000000000 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/4.png and /dev/null differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/5.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/5.png deleted file mode 100644 index 37419b56b..000000000 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/5.png and /dev/null differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/6.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/6.png deleted file mode 100644 index 2893f15ed..000000000 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/6.png and /dev/null differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/7.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/7.png deleted file mode 100644 index b25df5281..000000000 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/7.png and /dev/null differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/8.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/8.png deleted file mode 100644 index e7699fcee..000000000 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/8.png and /dev/null differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/9.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/9.png deleted file mode 100644 index 0ed177599..000000000 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/9.png and /dev/null differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/Tiles.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/Tiles.png new file mode 100644 index 000000000..840ca3952 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/Tiles.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/additems.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/additems.png new file mode 100644 index 000000000..4d14bbaa3 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/additems.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/blocks.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/blocks.png new file mode 100644 index 000000000..4faac43c8 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/blocks.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/charts.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/charts.png new file mode 100644 index 000000000..12cec0141 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/charts.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/darkmode.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/darkmode.png new file mode 100644 index 000000000..07152ceef Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/darkmode.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/dashboard_create.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/dashboard_create.png new file mode 100644 index 000000000..f57a3343f Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/dashboard_create.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/delete.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/delete.png new file mode 100644 index 000000000..315c6fc9e Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/delete.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/editlayout.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/editlayout.png new file mode 100644 index 000000000..bf566acf4 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/editlayout.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/email.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/email.png new file mode 100644 index 000000000..00f2f0d96 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/email.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/email_report.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/email_report.png new file mode 100644 index 000000000..e8a7442d6 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/email_report.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/hero.gif b/odoo_dynamic_dashboard/static/description/assets/screenshots/hero.gif index f939a4599..630d56644 100644 Binary files a/odoo_dynamic_dashboard/static/description/assets/screenshots/hero.gif and b/odoo_dynamic_dashboard/static/description/assets/screenshots/hero.gif differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/mainpage.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/mainpage.png new file mode 100644 index 000000000..19508c10a Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/mainpage.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/newbar.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/newbar.png new file mode 100644 index 000000000..f645291ad Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/newbar.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/newtile.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/newtile.png new file mode 100644 index 000000000..e1b51dc80 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/newtile.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/pdf.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/pdf.png new file mode 100644 index 000000000..5fce57032 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/pdf.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/pdf_and_mail.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/pdf_and_mail.png new file mode 100644 index 000000000..5944d04ea Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/pdf_and_mail.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/redirect.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/redirect.png new file mode 100644 index 000000000..481e80450 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/redirect.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/sales_dashboard.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/sales_dashboard.png new file mode 100644 index 000000000..1cc77c546 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/sales_dashboard.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/savetypes.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/savetypes.png new file mode 100644 index 000000000..8ab132751 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/savetypes.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/themes.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/themes.png new file mode 100644 index 000000000..47905fb23 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/themes.png differ diff --git a/odoo_dynamic_dashboard/static/description/assets/screenshots/themeselect.png b/odoo_dynamic_dashboard/static/description/assets/screenshots/themeselect.png new file mode 100644 index 000000000..86d3437c3 Binary files /dev/null and b/odoo_dynamic_dashboard/static/description/assets/screenshots/themeselect.png differ diff --git a/odoo_dynamic_dashboard/static/description/index.html b/odoo_dynamic_dashboard/static/description/index.html index abffe7005..f3c7456c5 100644 --- a/odoo_dynamic_dashboard/static/description/index.html +++ b/odoo_dynamic_dashboard/static/description/index.html @@ -7,12 +7,9 @@ Document - - + + - -
- -
-
-

- Odoo Dynamic Dashboard -

-

- Advanced Dashboard Module For Odoo17

-
-
-

- Dynamically Arrange the dashboard to get the information that - are relevant to your business, department, or a specific process - or need. -

-
+ +
+ +
+
+

+ Odoo Dynamic Dashboard +

+

+ This Module Helps To Create Configurable Dashboards Easily.

+
+
+

+ Create Configurable Odoo Dynamic Dashboard to get the + information that are relevant to your business, department, or a specific + process or need. +

+
-
- -
+
+ +
- -
-
- -
- - - -
-
-

- KEY HIGHLIGHTS -

-
-
-
-
- -
-
-

- Dynamic Dashboard for configuring new chart & - tiles

+
+ +
+ + + +
+
+

+ KEY HIGHLIGHTS +

+
+
+
+
+ +
+
+

+ Easily Create Dynamic Charts And Tiles

+
-
-
-
-
- -
-
-

- Adding background and font color for tile, - option for selecting FontAwesome icon

+
+
+
+ +
+
+

+ Create A Dynamic Dashboard Menu In Any Model

+
-
-
-
-
- -
-
-

- Chart types: Bar, Line, Radar, Pie, Doughnut, - option to select the chart sizes -

+
+
+
+ +
+
+

+ Charts Can Export Into Image, PDF, XLSX And CSV +

+
-
-
-
-
- -
-
-

- Lets you create dynamic menu's easily

+
+
+
+ +
+
+

+ Edit And Configure Charts And Tiles

+
-
-
- - - -
-
+
+ + + +
+
+ +
+

+ Overview + + +

+

+ Odoo Dynamic Dashboard

+
-
-

- Overview - - -

-

- Odoo Dynamic Dashboard

+
+
+ This module helps to create configurable dashboards easily. + Odoo Dynamic Dashboard module helps to Arrange the dashboard to + get the information that are relevant to your business, + department, or a specific process or need. +
+
+
+ + +
-
-
- This module helps to create configurable dashboards easily. - The Odoo Dynamic Dashboard module allows you to arrange the - dashboard to display information relevant to your business, - department, or specific processes and needs. -
+ + +
+
+ +
+
-
-
- - -
- - - -
-
- -
- -
-
- -
-
- -
-

- Screenshots - - -

-

- Odoo Dynamic Dashboard

-
+
-
-

- Odoo Dynamic Dashboard View -

-
- -
-
+
+
-
-

- Dashboard Tiles View -

-

- The tiled view of the records in Odoo is also - clickable; when you click on a record, you will be - directed to the records tree view. -

-
- +
+

+ Screenshots + + +

+

+ Odoo Dynamic Dashboard

-
-
-

- Dashboard Charts View -

-

- The chart view of the records in Odoo is provided in - the dashboard based on the filtering.

-
- +
+

+ Odoo Dynamic Dashboard View +

+
+ +
-
-
-

- Creating New Tile Block -

-

- When you click the 'Add Block' button on the - dashboard, a new empty tile block will be added. You - can configure this tile block using the button in - the top right corner. You can also use this button - to edit existing tile blocks.

-
- +
+

+ Dashboard Tiles View +

+

+ The tiled view of the records in Odoo is also clickable; when you click on a record, you will be directed to the records tree view. +

+
+ +
-

You will - then be redirected to the form view to configure the - tile block. Here, you can add a name to the tile and - specify the model from which records should be - displayed. You can choose an operation—such as sum, - count, or average—to be performed on the data shown - in the field on the dashboard. Additionally, you can - add a measurement field to determine the value - displayed in the tile. There is also an option to - set a filter, which will dictate the data shown in - the tile. Finally, in the tile information section, - you can set the tile type, tile icon, icon color, - tile color, and text color. -

-
- + +
+

+ Dashboard Charts View +

+

+ The chart view of the records in Odoo is provided in the dashboard based on the filtering.

+
+ +
+

+ Clicking on various icons generates different types of chart data, which can be exported as image, PDF, CSV, and XLSX formats.

+
+ +
+

+ By clicking cross icon you can delete the chart and also the same for tile.

+
+ +
+
+

+ Select Type Of The Item +

+
+ +
+

+ When choosing the "Tile" option, input a name for the tile, + select a model, operation type, and measured field. + Additionally, customize the tile with an icon and color.

+
+ +
+

+ When opting for "Charts," provide a name for the chart, + select a model, operation type, and measured field, along with filters. + Additionally, choose the chart type, set its size, + and specify the grouping for the Y-axis.

+
+ +
+
-
+
+

+ Edit Layout +

+

+ You can change the position of the item and resize the item. +

+
+ +
+
-
-

- Creating New Chart Block -

-

- Click the 'Add Graph' button to add a new block. - From here, you can click the configuration button in - the top right corner to make the necessary - configurations for the chart.

-
- +
+

+ By double-clicking the tile, it will redirect to the corresponding tree view. +

+
+ +
-

- From here, you will be redirected to the form view - to configure the chart. You can set the name, - operation, measurement field, and filter, similar to - the tile block. However, in the block information - section for the chart, you have additional options - to set the block type, chart type, chart size, and - the reference field to display in the charts. -

-
- + +
+

+ Change The Mode +

+

+ Dark mode +

+
+ +
-
-
-

- Blocks -

-

- By clicking on the 'Blocks' menu from the Dashboard - module, you can see the blocks created in the - dashboard. From here, you can configure, delete, or - create new dashboard blocks. -

-
- +
+

+ Dashboard Menu +

+

+ You can Add dashboard menu in any module. Add a name for the menu and select the parent + menu also +

+
+ +
+
+ +
+
+
+

+ Dashboard Block +

+

+ You can see all the tiles and charts created in the Dashboard Block menu. +

+
+ +
-
-
-

- Dynamic Dashboard -

-

- From the configuration menu, select 'Dashboards' to - view the dynamic dashboards that have been created. - Here, you can create, remove, or edit dynamic - dashboards. To create a new dynamic dashboard, click - the 'NEW' button. -

-
- +
+

+ Dashboard Theme +

+

+ Customize the Dashboard Theme via Configuration > Dashboard Theme. + Then, choose the configured theme for your dashboard. +

+
+ +
+
+ +
-

- This is the form view for configuring the dynamic - dashboard. Here, you can set the name of the - dashboard menu and specify the parent menu under - which this dashboard menu should appear. -

-
- + +
+

+ Print PDF +

+

+ Print & Mail whole dashboard in PDF format by clicking the PDF or Mail icon. +

+
+ +
+
+ +
-

- From the parent menu, you can see that the new - dynamic dashboard has been created as specified in - the configuration. -

-
- + +
+

+ Send Mail +

+

+ Clicking the Mail icon brings up a wizard, + where you can select users and then click the SEND button. +

+
+ +
+

+ Demo Mail +

+
+ +
-
-
+
-
-
+
+
-

-

+

+

-
-

- FEATURES - - -

-

- Comprehensive Features of ADVANCED DYNAMIC - DASHBOARD

-
+
+

+ FEATURES + + +

+

+ Comprehensive Features of ODOO DYNAMIC DASHBOARD

+
-
- -
-
-
-
- - - -
- Edit And Configure Charts And - Tiles -
+
+ +
+
+
+
+ + + +
+ Edit And Configure Charts And Tiles. +
+
-
-
-
-
-
- - - -
- Easily Create Dynamic Charts And - Tiles -
+
+
+
+
+ + + +
+ Easily Create Dynamic Charts And Tiles. +
+
-
-
-
-
-
- - - -
- Multiple way to arrange your dashboard -
+
+
+
+
+ + + +
+ Available Dark Mode And Light Mode. +
+
-
-
-
-
-
- - - -
- Create a Dynamic - Dashboard Menu In Any Model -
+
+
+
+
+ + + +
+ Create A Odoo Dynamic Dashboard Menu In Any Model. +
+
-
-
-
-
-
- - - -
- Charts Configuration -
+
+
+
+
+ + + +
+ Charts Can Export Into Image, PDF,XLSX And CSV. +
+
-
-
-
-
-
- - - -
- Tiles Configuration -
+
+
+
+
+ + + +
+ Drag And Resize The Chart And Tile. +
+
+
+
+
+
+
+
+
+

+ RELEASE NOTES + + +

-
-
-
+
+
+
+ +
+

Version 17.0.1 I Released + on : 18th May 2024 +

+

Initial commit for + odoo_dynamic_dashboard

+
-
-
-
-

- RELEASE NOTES - - -

-
-
-
-
- -
-

Version 17.0.1 I Released - on : 11th May 2024 -

-

Initial commit for - odoo_dynamic_dashboard

- -
-
+
+
-
-
+
+
-
- - + -
-
-
-

Related Modules

-

Explore our related modules

-
-
-
-
+ -
+
-
-
- +
+
+ +
+

+ Our Services +

-

- Our Services -

-
-
-
-
-
- +
+
+
+
+ +
+
+ Odoo + Customization
-
- Odoo - Customization
-
-
-
- +
+
+ +
+
+ Odoo + Implementation
-
- Odoo - Implementation
-
-
-
- +
+
+ +
+
+ Odoo + Support
-
- Odoo - Support
-
-
-
- +
+
+ +
+
+ Hire + Odoo + Developer
-
- Hire - Odoo - Developer
-
-
-
- +
+
+ +
+
+ Odoo + Integration
-
- Odoo - Integration
-
-
-
- +
+
+ +
+
+ Odoo + Migration
-
- Odoo - Migration
-
-
-
- +
+
+ +
+
+ Odoo + Consultancy
-
- Odoo - Consultancy
-
-
-
- +
+
+ +
+
+ Odoo + Implementation
-
- Odoo - Implementation
-
-
-
- +
+
+ +
+
+ Odoo + Licensing Consultancy
-
- Odoo - Licensing Consultancy
-
-
+
-
-
-
-

Our Industries

-
+
+
+

Our Industries

+
-
-
-
- -
+
+
+ +
- Trading -
-

- Easily procure - and - sell your products

+ Trading +
+

+ Easily procure + and + sell your products

+
-
-
-
- -
+
+ +
- POS -
-

- Easy - configuration - and convivial experience

+ POS +
+

+ Easy + configuration + and convivial experience

+
-
-
-
- -
+
+ +
- Education -
-

- A platform for - educational management

+ Education +
+

+ A platform for + educational management

+
-
-
-
- -
+
+ +
- Manufacturing -
-

- Plan, track and - schedule your operations

+ Manufacturing +
+

+ Plan, track and + schedule your operations

+
-
-
-
- -
+
+ +
- E-commerce & Website -
-

- Mobile - friendly, - awe-inspiring product pages

+ E-commerce & Website +
+

+ Mobile + friendly, + awe-inspiring product pages

+
-
-
-
- -
+
+ +
- Service Management -
-

- Keep track of - services and invoice

+ Service Management +
+

+ Keep track of + services and invoice

+
-
-
-
- -
+
+ +
- Restaurant -
-

- Run your bar or - restaurant methodically

+ Restaurant +
+

+ Run your bar or + restaurant methodically

+
-
-
-
- -
+
+ +
- Hotel Management -
-

- An - all-inclusive - hotel management application

+ Hotel Management +
+

+ An + all-inclusive + hotel management application

+
-
- + - - -
-
-
-

Support

-

Need help? Get in touch. -

-
-
-
-
-
- -
-
-

Need Help?

-

Got questions or need help? - Get - in touch.

- -

- odoo@cybrosys.com

-
-
+ + +
+
+
+

Support

+

Need help? Get in touch. +

+
-
-
-
-
- +
+
+
+ +
+
+

Need Help?

+

Got questions or need help? + Get + in touch.

+ +

+ odoo@cybrosys.com

+
+
-
-

WhatsApp

-

Say hi to us on - WhatsApp!

- +
+
+
+
+ +
+
+

WhatsApp

+

Say hi to us on + WhatsApp!

+

+91 86068 27707

+
-
-
-
-
- -
-
-

Skype

-

Say hi to us on Skype!

- +
+
+
+ +
+
+

Skype

+

Say hi to us on Skype!

+

cybroopenerp

+
-
-
- -
- -
-
- -
-
- +
-
- -
+ +
+
+ +
+ +
+ +
+
+ +
- - diff --git a/odoo_dynamic_dashboard/static/lib/js/interactjs.js b/odoo_dynamic_dashboard/static/lib/js/interactjs.js new file mode 100644 index 000000000..b57c73bdb --- /dev/null +++ b/odoo_dynamic_dashboard/static/lib/js/interactjs.js @@ -0,0 +1,4 @@ +/* interact.js 1.10.26 | https://raw.github.com/taye/interact.js/main/LICENSE */ + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).interact=e()}(this,(function(){"use strict";function t(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function e(e){for(var n=1;n2&&void 0!==arguments[2]?arguments[2]:function(t){return!0},r=arguments.length>3?arguments[3]:void 0;if(r=r||{},w.string(t)&&-1!==t.search(" ")&&(t=J(t)),w.array(t))return t.forEach((function(t){return $(t,e,n,r)})),r;if(w.object(t)&&(e=t,t=""),w.func(e)&&n(t))r[t]=r[t]||[],r[t].push(e);else if(w.array(e))for(var i=0,o=e;i1?lt(e):e[0];ot(r,t.page),function(t,e){e=e||{},I.isOperaMobile&&rt(t)?it("screen",t,e):it("client",t,e)}(r,t.client),t.timeStamp=n}function ct(t){var e=[];return w.array(t)?(e[0]=t[0],e[1]=t[1]):"touchend"===t.type?1===t.touches.length?(e[0]=t.touches[0],e[1]=t.changedTouches[0]):0===t.touches.length&&(e[0]=t.changedTouches[0],e[1]=t.changedTouches[1]):(e[0]=t.touches[0],e[1]=t.touches[1]),e}function lt(t){for(var e={pageX:0,pageY:0,clientX:0,clientY:0,screenX:0,screenY:0},n=0;n=(parseInt(y(r).getComputedStyle(r).zIndex,10)||0)&&(e=o);else e=o}else e=o}return e}(a);return r.activeDrops[h]||null}function St(t,e,n){var r=t.dropState,i={enter:null,leave:null,activate:null,deactivate:null,move:null,drop:null};return"dragstart"===n.type&&(i.activate=new xt(r,n,"dropactivate"),i.activate.target=null,i.activate.dropzone=null),"dragend"===n.type&&(i.deactivate=new xt(r,n,"dropdeactivate"),i.deactivate.target=null,i.deactivate.dropzone=null),r.rejected||(r.cur.element!==r.prev.element&&(r.prev.dropzone&&(i.leave=new xt(r,n,"dragleave"),n.dragLeave=i.leave.target=r.prev.element,n.prevDropzone=i.leave.dropzone=r.prev.dropzone),r.cur.dropzone&&(i.enter=new xt(r,n,"dragenter"),n.dragEnter=r.cur.element,n.dropzone=r.cur.dropzone)),"dragend"===n.type&&r.cur.dropzone&&(i.drop=new xt(r,n,"drop"),n.dropzone=r.cur.dropzone,n.relatedTarget=r.cur.element),"dragmove"===n.type&&r.cur.dropzone&&(i.move=new xt(r,n,"dropmove"),n.dropzone=r.cur.dropzone)),i}function _t(t,e){var n=t.dropState,r=n.activeDrops,i=n.cur,o=n.prev;e.leave&&o.dropzone.fire(e.leave),e.enter&&i.dropzone.fire(e.enter),e.move&&i.dropzone.fire(e.move),e.drop&&i.dropzone.fire(e.drop),e.deactivate&&wt(r,e.deactivate),n.prev.dropzone=i.dropzone,n.prev.element=i.element}function Pt(t,e){var n=t.interaction,r=t.iEvent,i=t.event;if("dragmove"===r.type||"dragend"===r.type){var o=n.dropState;e.dynamicDrop&&(o.activeDrops=Et(e,n.element));var a=r,s=Tt(n,a,i);o.rejected=o.rejected&&!!s&&s.dropzone===o.cur.dropzone&&s.element===o.cur.element,o.cur.dropzone=s&&s.dropzone,o.cur.element=s&&s.element,o.events=St(n,0,a)}}var Ot={id:"actions/drop",install:function(t){var e=t.actions,n=t.interactStatic,r=t.Interactable,i=t.defaults;t.usePlugin(_),r.prototype.dropzone=function(t){return function(t,e){if(w.object(e)){if(t.options.drop.enabled=!1!==e.enabled,e.listeners){var n=$(e.listeners),r=Object.keys(n).reduce((function(t,e){return t[/^(enter|leave)/.test(e)?"drag".concat(e):/^(activate|deactivate|move)/.test(e)?"drop".concat(e):e]=n[e],t}),{}),i=t.options.drop.listeners;i&&t.off(i),t.on(r),t.options.drop.listeners=r}return w.func(e.ondrop)&&t.on("drop",e.ondrop),w.func(e.ondropactivate)&&t.on("dropactivate",e.ondropactivate),w.func(e.ondropdeactivate)&&t.on("dropdeactivate",e.ondropdeactivate),w.func(e.ondragenter)&&t.on("dragenter",e.ondragenter),w.func(e.ondragleave)&&t.on("dragleave",e.ondragleave),w.func(e.ondropmove)&&t.on("dropmove",e.ondropmove),/^(pointer|center)$/.test(e.overlap)?t.options.drop.overlap=e.overlap:w.number(e.overlap)&&(t.options.drop.overlap=Math.max(Math.min(1,e.overlap),0)),"accept"in e&&(t.options.drop.accept=e.accept),"checker"in e&&(t.options.drop.checker=e.checker),t}if(w.bool(e))return t.options.drop.enabled=e,t;return t.options.drop}(this,t)},r.prototype.dropCheck=function(t,e,n,r,i,o){return function(t,e,n,r,i,o,a){var s=!1;if(!(a=a||t.getRect(o)))return!!t.options.drop.checker&&t.options.drop.checker(e,n,s,t,o,r,i);var c=t.options.drop.overlap;if("pointer"===c){var l=K(r,i,"drag"),u=ot(e);u.x+=l.x,u.y+=l.y;var p=u.x>a.left&&u.xa.top&&u.y=a.left&&h<=a.right&&v>=a.top&&v<=a.bottom}if(d&&w.number(c)){s=Math.max(0,Math.min(a.right,d.right)-Math.max(a.left,d.left))*Math.max(0,Math.min(a.bottom,d.bottom)-Math.max(a.top,d.top))/(d.width*d.height)>=c}t.options.drop.checker&&(s=t.options.drop.checker(e,n,s,t,o,r,i));return s}(this,t,e,n,r,i,o)},n.dynamicDrop=function(e){return w.bool(e)?(t.dynamicDrop=e,n):t.dynamicDrop},V(e.phaselessTypes,{dragenter:!0,dragleave:!0,dropactivate:!0,dropdeactivate:!0,dropmove:!0,drop:!0}),e.methodDict.drop="dropzone",t.dynamicDrop=!1,i.actions.drop=Ot.defaults},listeners:{"interactions:before-action-start":function(t){var e=t.interaction;"drag"===e.prepared.name&&(e.dropState={cur:{dropzone:null,element:null},prev:{dropzone:null,element:null},rejected:null,events:null,activeDrops:[]})},"interactions:after-action-start":function(t,e){var n=t.interaction,r=(t.event,t.iEvent);if("drag"===n.prepared.name){var i=n.dropState;i.activeDrops=[],i.events={},i.activeDrops=Et(e,n.element),i.events=St(n,0,r),i.events.activate&&(wt(i.activeDrops,i.events.activate),e.fire("actions/drop:start",{interaction:n,dragEvent:r}))}},"interactions:action-move":Pt,"interactions:after-action-move":function(t,e){var n=t.interaction,r=t.iEvent;if("drag"===n.prepared.name){var i=n.dropState;_t(n,i.events),e.fire("actions/drop:move",{interaction:n,dragEvent:r}),i.events={}}},"interactions:action-end":function(t,e){if("drag"===t.interaction.prepared.name){var n=t.interaction,r=t.iEvent;Pt(t,e),_t(n,n.dropState.events),e.fire("actions/drop:end",{interaction:n,dragEvent:r})}},"interactions:stop":function(t){var e=t.interaction;if("drag"===e.prepared.name){var n=e.dropState;n&&(n.activeDrops=null,n.events=null,n.cur.dropzone=null,n.cur.element=null,n.prev.dropzone=null,n.prev.element=null,n.rejected=!1)}}},getActiveDrops:Et,getDrop:Tt,getDropEvents:St,fireDropEvents:_t,filterEventType:function(t){return 0===t.search("drag")||0===t.search("drop")},defaults:{enabled:!1,accept:null,overlap:"pointer"}},kt=Ot;function Dt(t){var e=t.interaction,n=t.iEvent,r=t.phase;if("gesture"===e.prepared.name){var i=e.pointers.map((function(t){return t.pointer})),o="start"===r,a="end"===r,s=e.interactable.options.deltaSource;if(n.touches=[i[0],i[1]],o)n.distance=pt(i,s),n.box=ut(i),n.scale=1,n.ds=0,n.angle=ft(i,s),n.da=0,e.gesture.startDistance=n.distance,e.gesture.startAngle=n.angle;else if(a||e.pointers.length<2){var c=e.prevEvent;n.distance=c.distance,n.box=c.box,n.scale=c.scale,n.ds=0,n.angle=c.angle,n.da=0}else n.distance=pt(i,s),n.box=ut(i),n.scale=n.distance/e.gesture.startDistance,n.angle=ft(i,s),n.ds=n.scale-e.gesture.scale,n.da=n.angle-e.gesture.angle;e.gesture.distance=n.distance,e.gesture.angle=n.angle,w.number(n.scale)&&n.scale!==1/0&&!isNaN(n.scale)&&(e.gesture.scale=n.scale)}}var It={id:"actions/gesture",before:["actions/drag","actions/resize"],install:function(t){var e=t.actions,n=t.Interactable,r=t.defaults;n.prototype.gesturable=function(t){return w.object(t)?(this.options.gesture.enabled=!1!==t.enabled,this.setPerAction("gesture",t),this.setOnEvents("gesture",t),this):w.bool(t)?(this.options.gesture.enabled=t,this):this.options.gesture},e.map.gesture=It,e.methodDict.gesture="gesturable",r.actions.gesture=It.defaults},listeners:{"interactions:action-start":Dt,"interactions:action-move":Dt,"interactions:action-end":Dt,"interactions:new":function(t){t.interaction.gesture={angle:0,distance:0,scale:1,startAngle:0,startDistance:0}},"auto-start:check":function(t){if(!(t.interaction.pointers.length<2)){var e=t.interactable.options.gesture;if(e&&e.enabled)return t.action={name:"gesture"},!1}}},defaults:{},getCursor:function(){return""},filterEventType:function(t){return 0===t.search("gesture")}},Mt=It;function zt(t,e,n,r,i,o,a){if(!e)return!1;if(!0===e){var s=w.number(o.width)?o.width:o.right-o.left,c=w.number(o.height)?o.height:o.bottom-o.top;if(a=Math.min(a,Math.abs(("left"===t||"right"===t?s:c)/2)),s<0&&("left"===t?t="right":"right"===t&&(t="left")),c<0&&("top"===t?t="bottom":"bottom"===t&&(t="top")),"left"===t){var l=s>=0?o.left:o.right;return n.x=0?o.top:o.bottom;return n.y(s>=0?o.right:o.left)-a;if("bottom"===t)return n.y>(c>=0?o.bottom:o.top)-a}return!!w.element(r)&&(w.element(e)?e===r:F(r,e,i))}function At(t){var e=t.iEvent,n=t.interaction;if("resize"===n.prepared.name&&n.resizeAxes){var r=e;n.interactable.options.resize.square?("y"===n.resizeAxes?r.delta.x=r.delta.y:r.delta.y=r.delta.x,r.axes="xy"):(r.axes=n.resizeAxes,"x"===n.resizeAxes?r.delta.y=0:"y"===n.resizeAxes&&(r.delta.x=0))}}var Rt,Ct,jt={id:"actions/resize",before:["actions/drag"],install:function(t){var e=t.actions,n=t.browser,r=t.Interactable,i=t.defaults;jt.cursors=function(t){return t.isIe9?{x:"e-resize",y:"s-resize",xy:"se-resize",top:"n-resize",left:"w-resize",bottom:"s-resize",right:"e-resize",topleft:"se-resize",bottomright:"se-resize",topright:"ne-resize",bottomleft:"ne-resize"}:{x:"ew-resize",y:"ns-resize",xy:"nwse-resize",top:"ns-resize",left:"ew-resize",bottom:"ns-resize",right:"ew-resize",topleft:"nwse-resize",bottomright:"nwse-resize",topright:"nesw-resize",bottomleft:"nesw-resize"}}(n),jt.defaultMargin=n.supportsTouch||n.supportsPointerEvent?20:10,r.prototype.resizable=function(e){return function(t,e,n){if(w.object(e))return t.options.resize.enabled=!1!==e.enabled,t.setPerAction("resize",e),t.setOnEvents("resize",e),w.string(e.axis)&&/^x$|^y$|^xy$/.test(e.axis)?t.options.resize.axis=e.axis:null===e.axis&&(t.options.resize.axis=n.defaults.actions.resize.axis),w.bool(e.preserveAspectRatio)?t.options.resize.preserveAspectRatio=e.preserveAspectRatio:w.bool(e.square)&&(t.options.resize.square=e.square),t;if(w.bool(e))return t.options.resize.enabled=e,t;return t.options.resize}(this,e,t)},e.map.resize=jt,e.methodDict.resize="resizable",i.actions.resize=jt.defaults},listeners:{"interactions:new":function(t){t.interaction.resizeAxes="xy"},"interactions:action-start":function(t){!function(t){var e=t.iEvent,n=t.interaction;if("resize"===n.prepared.name&&n.prepared.edges){var r=e,i=n.rect;n._rects={start:V({},i),corrected:V({},i),previous:V({},i),delta:{left:0,right:0,width:0,top:0,bottom:0,height:0}},r.edges=n.prepared.edges,r.rect=n._rects.corrected,r.deltaRect=n._rects.delta}}(t),At(t)},"interactions:action-move":function(t){!function(t){var e=t.iEvent,n=t.interaction;if("resize"===n.prepared.name&&n.prepared.edges){var r=e,i=n.interactable.options.resize.invert,o="reposition"===i||"negate"===i,a=n.rect,s=n._rects,c=s.start,l=s.corrected,u=s.delta,p=s.previous;if(V(p,l),o){if(V(l,a),"reposition"===i){if(l.top>l.bottom){var f=l.top;l.top=l.bottom,l.bottom=f}if(l.left>l.right){var d=l.left;l.left=l.right,l.right=d}}}else l.top=Math.min(a.top,c.bottom),l.bottom=Math.max(a.bottom,c.top),l.left=Math.min(a.left,c.right),l.right=Math.max(a.right,c.left);for(var h in l.width=l.right-l.left,l.height=l.bottom-l.top,l)u[h]=l[h]-p[h];r.edges=n.prepared.edges,r.rect=l,r.deltaRect=u}}(t),At(t)},"interactions:action-end":function(t){var e=t.iEvent,n=t.interaction;if("resize"===n.prepared.name&&n.prepared.edges){var r=e;r.edges=n.prepared.edges,r.rect=n._rects.corrected,r.deltaRect=n._rects.delta}},"auto-start:check":function(t){var e=t.interaction,n=t.interactable,r=t.element,i=t.rect,o=t.buttons;if(i){var a=V({},e.coords.cur.page),s=n.options.resize;if(s&&s.enabled&&(!e.pointerIsDown||!/mouse|pointer/.test(e.pointerType)||0!=(o&s.mouseButtons))){if(w.object(s.edges)){var c={left:!1,right:!1,top:!1,bottom:!1};for(var l in c)c[l]=zt(l,s.edges[l],a,e._latestPointer.eventTarget,r,i,s.margin||jt.defaultMargin);c.left=c.left&&!c.right,c.top=c.top&&!c.bottom,(c.left||c.right||c.top||c.bottom)&&(t.action={name:"resize",edges:c})}else{var u="y"!==s.axis&&a.x>i.right-jt.defaultMargin,p="x"!==s.axis&&a.y>i.bottom-jt.defaultMargin;(u||p)&&(t.action={name:"resize",axes:(u?"x":"")+(p?"y":"")})}return!t.action&&void 0}}}},defaults:{square:!1,preserveAspectRatio:!1,axis:"xy",margin:NaN,edges:null,invert:"none"},cursors:null,getCursor:function(t){var e=t.edges,n=t.axis,r=t.name,i=jt.cursors,o=null;if(n)o=i[r+n];else if(e){for(var a="",s=0,c=["top","bottom","left","right"];s=1){var l={x:qt.x*c,y:qt.y*c};if(l.x||l.y){var u=Vt(o);w.window(o)?o.scrollBy(l.x,l.y):o&&(o.scrollLeft+=l.x,o.scrollTop+=l.y);var p=Vt(o),f={x:p.x-u.x,y:p.y-u.y};(f.x||f.y)&&e.fire({type:"autoscroll",target:n,interactable:e,delta:f,interaction:t,container:o})}qt.prevTime=a}qt.isScrolling&&(Lt.cancel(qt.i),qt.i=Lt.request(qt.scroll))},check:function(t,e){var n;return null==(n=t.options[e].autoScroll)?void 0:n.enabled},onInteractionMove:function(t){var e=t.interaction,n=t.pointer;if(e.interacting()&&qt.check(e.interactable,e.prepared.name))if(e.simulation)qt.x=qt.y=0;else{var r,i,o,a,s=e.interactable,c=e.element,l=e.prepared.name,u=s.options[l].autoScroll,p=Bt(u.container,s,c);if(w.window(p))a=n.clientXp.innerWidth-qt.margin,o=n.clientY>p.innerHeight-qt.margin;else{var f=Y(p);a=n.clientXf.right-qt.margin,o=n.clientY>f.bottom-qt.margin}qt.x=i?1:a?-1:0,qt.y=o?1:r?-1:0,qt.isScrolling||(qt.margin=u.margin,qt.speed=u.speed,qt.start(e))}}};function Bt(t,e,n){return(w.string(t)?W(t,e,n):t)||y(n)}function Vt(t){return w.window(t)&&(t=window.document.body),{x:t.scrollLeft,y:t.scrollTop}}var Wt={id:"auto-scroll",install:function(t){var e=t.defaults,n=t.actions;t.autoScroll=qt,qt.now=function(){return t.now()},n.phaselessTypes.autoscroll=!0,e.perAction.autoScroll=qt.defaults},listeners:{"interactions:new":function(t){t.interaction.autoScroll=null},"interactions:destroy":function(t){t.interaction.autoScroll=null,qt.stop(),qt.interaction&&(qt.interaction=null)},"interactions:stop":qt.stop,"interactions:action-move":function(t){return qt.onInteractionMove(t)}}},Gt=Wt;function Nt(t,e){var n=!1;return function(){return n||(g.console.warn(e),n=!0),t.apply(this,arguments)}}function Ut(t,e){return t.name=e.name,t.axis=e.axis,t.edges=e.edges,t}function Ht(t){return w.bool(t)?(this.options.styleCursor=t,this):null===t?(delete this.options.styleCursor,this):this.options.styleCursor}function Kt(t){return w.func(t)?(this.options.actionChecker=t,this):null===t?(delete this.options.actionChecker,this):this.options.actionChecker}var $t={id:"auto-start/interactableMethods",install:function(t){var e=t.Interactable;e.prototype.getAction=function(e,n,r,i){var o=function(t,e,n,r,i){var o=t.getRect(r),a=e.buttons||{0:1,1:4,3:8,4:16}[e.button],s={action:null,interactable:t,interaction:n,element:r,rect:o,buttons:a};return i.fire("auto-start:check",s),s.action}(this,n,r,i,t);return this.options.actionChecker?this.options.actionChecker(e,n,o,this,i,r):o},e.prototype.ignoreFrom=Nt((function(t){return this._backCompatOption("ignoreFrom",t)}),"Interactable.ignoreFrom() has been deprecated. Use Interactble.draggable({ignoreFrom: newValue})."),e.prototype.allowFrom=Nt((function(t){return this._backCompatOption("allowFrom",t)}),"Interactable.allowFrom() has been deprecated. Use Interactble.draggable({allowFrom: newValue})."),e.prototype.actionChecker=Kt,e.prototype.styleCursor=Ht}};function Jt(t,e,n,r,i){return e.testIgnoreAllow(e.options[t.name],n,r)&&e.options[t.name].enabled&&ee(e,n,t,i)?t:null}function Qt(t,e,n,r,i,o,a){for(var s=0,c=r.length;s=s)return!1;if(d.interactable===t){if((l+=h===n.name?1:0)>=o)return!1;if(d.element===e&&(u++,h===n.name&&u>=a))return!1}}}return s>0}function ne(t,e){return w.number(t)?(e.autoStart.maxInteractions=t,this):e.autoStart.maxInteractions}function re(t,e,n){var r=n.autoStart.cursorElement;r&&r!==t&&(r.style.cursor=""),t.ownerDocument.documentElement.style.cursor=e,t.style.cursor=e,n.autoStart.cursorElement=e?t:null}function ie(t,e){var n=t.interactable,r=t.element,i=t.prepared;if("mouse"===t.pointerType&&n&&n.options.styleCursor){var o="";if(i.name){var a=n.options[i.name].cursorChecker;o=w.func(a)?a(i,n,r,t._interacting):e.actions.map[i.name].getCursor(i)}re(t.element,o||"",e)}else e.autoStart.cursorElement&&re(e.autoStart.cursorElement,"",e)}var oe={id:"auto-start/base",before:["actions"],install:function(t){var e=t.interactStatic,n=t.defaults;t.usePlugin($t),n.base.actionChecker=null,n.base.styleCursor=!0,V(n.perAction,{manualStart:!1,max:1/0,maxPerElement:1,allowFrom:null,ignoreFrom:null,mouseButtons:1}),e.maxInteractions=function(e){return ne(e,t)},t.autoStart={maxInteractions:1/0,withinInteractionLimit:ee,cursorElement:null}},listeners:{"interactions:down":function(t,e){var n=t.interaction,r=t.pointer,i=t.event,o=t.eventTarget;n.interacting()||te(n,Zt(n,r,i,o,e),e)},"interactions:move":function(t,e){!function(t,e){var n=t.interaction,r=t.pointer,i=t.event,o=t.eventTarget;"mouse"!==n.pointerType||n.pointerIsDown||n.interacting()||te(n,Zt(n,r,i,o,e),e)}(t,e),function(t,e){var n=t.interaction;if(n.pointerIsDown&&!n.interacting()&&n.pointerWasMoved&&n.prepared.name){e.fire("autoStart:before-start",t);var r=n.interactable,i=n.prepared.name;i&&r&&(r.options[i].manualStart||!ee(r,n.element,n.prepared,e)?n.stop():(n.start(n.prepared,r,n.element),ie(n,e)))}}(t,e)},"interactions:stop":function(t,e){var n=t.interaction,r=n.interactable;r&&r.options.styleCursor&&re(n.element,"",e)}},maxInteractions:ne,withinInteractionLimit:ee,validateAction:Jt},ae=oe;var se={id:"auto-start/dragAxis",listeners:{"autoStart:before-start":function(t,e){var n=t.interaction,r=t.eventTarget,i=t.dx,o=t.dy;if("drag"===n.prepared.name){var a=Math.abs(i),s=Math.abs(o),c=n.interactable.options.drag,l=c.startAxis,u=a>s?"x":a0&&(e.autoStartHoldTimer=setTimeout((function(){e.start(e.prepared,e.interactable,e.element)}),n))},"interactions:move":function(t){var e=t.interaction,n=t.duplicate;e.autoStartHoldTimer&&e.pointerWasMoved&&!n&&(clearTimeout(e.autoStartHoldTimer),e.autoStartHoldTimer=null)},"autoStart:before-start":function(t){var e=t.interaction;ce(e)>0&&(e.prepared.name=null)}},getHoldDuration:ce},ue=le,pe={id:"auto-start",install:function(t){t.usePlugin(ae),t.usePlugin(ue),t.usePlugin(se)}},fe=function(t){return/^(always|never|auto)$/.test(t)?(this.options.preventDefault=t,this):w.bool(t)?(this.options.preventDefault=t?"always":"never",this):this.options.preventDefault};function de(t){var e=t.interaction,n=t.event;e.interactable&&e.interactable.checkAndPreventDefault(n)}var he={id:"core/interactablePreventDefault",install:function(t){var e=t.Interactable;e.prototype.preventDefault=fe,e.prototype.checkAndPreventDefault=function(e){return function(t,e,n){var r=t.options.preventDefault;if("never"!==r)if("always"!==r){if(e.events.supportsPassive&&/^touch(start|move)$/.test(n.type)){var i=y(n.target).document,o=e.getDocOptions(i);if(!o||!o.events||!1!==o.events.passive)return}/^(mouse|pointer|touch)*(down|start)/i.test(n.type)||w.element(n.target)&&R(n.target,"input,select,textarea,[contenteditable=true],[contenteditable=true] *")||n.preventDefault()}else n.preventDefault()}(this,t,e)},t.interactions.docEvents.push({type:"dragstart",listener:function(e){for(var n=0,r=t.interactions.list;n150)return null;var e=180*Math.atan2(t.prevEvent.velocityY,t.prevEvent.velocityX)/Math.PI;e<0&&(e+=360);var n=112.5<=e&&e<247.5,r=202.5<=e&&e<337.5;return{up:r,down:!r&&22.5<=e&&e<157.5,left:n,right:!n&&(292.5<=e||e<67.5),angle:e,speed:t.prevEvent.speed,velocity:{x:t.prevEvent.velocityX,y:t.prevEvent.velocityY}}}},{key:"preventDefault",value:function(){}},{key:"stopImmediatePropagation",value:function(){this.immediatePropagationStopped=this.propagationStopped=!0}},{key:"stopPropagation",value:function(){this.propagationStopped=!0}}]),n}(vt);Object.defineProperties(Se.prototype,{pageX:{get:function(){return this.page.x},set:function(t){this.page.x=t}},pageY:{get:function(){return this.page.y},set:function(t){this.page.y=t}},clientX:{get:function(){return this.client.x},set:function(t){this.client.x=t}},clientY:{get:function(){return this.client.y},set:function(t){this.client.y=t}},dx:{get:function(){return this.delta.x},set:function(t){this.delta.x=t}},dy:{get:function(){return this.delta.y},set:function(t){this.delta.y=t}},velocityX:{get:function(){return this.velocity.x},set:function(t){this.velocity.x=t}},velocityY:{get:function(){return this.velocity.y},set:function(t){this.velocity.y=t}}});var _e=o((function t(e,n,i,o,a){r(this,t),this.id=void 0,this.pointer=void 0,this.event=void 0,this.downTime=void 0,this.downTarget=void 0,this.id=e,this.pointer=n,this.event=i,this.downTime=o,this.downTarget=a})),Pe=function(t){return t.interactable="",t.element="",t.prepared="",t.pointerIsDown="",t.pointerWasMoved="",t._proxy="",t}({}),Oe=function(t){return t.start="",t.move="",t.end="",t.stop="",t.interacting="",t}({}),ke=0,De=function(){function t(e){var n=this,i=e.pointerType,o=e.scopeFire;r(this,t),this.interactable=null,this.element=null,this.rect=null,this._rects=void 0,this.edges=null,this._scopeFire=void 0,this.prepared={name:null,axis:null,edges:null},this.pointerType=void 0,this.pointers=[],this.downEvent=null,this.downPointer={},this._latestPointer={pointer:null,event:null,eventTarget:null},this.prevEvent=null,this.pointerIsDown=!1,this.pointerWasMoved=!1,this._interacting=!1,this._ending=!1,this._stopped=!0,this._proxy=void 0,this.simulation=null,this.doMove=Nt((function(t){this.move(t)}),"The interaction.doMove() method has been renamed to interaction.move()"),this.coords={start:{page:{x:0,y:0},client:{x:0,y:0},timeStamp:0},prev:{page:{x:0,y:0},client:{x:0,y:0},timeStamp:0},cur:{page:{x:0,y:0},client:{x:0,y:0},timeStamp:0},delta:{page:{x:0,y:0},client:{x:0,y:0},timeStamp:0},velocity:{page:{x:0,y:0},client:{x:0,y:0},timeStamp:0}},this._id=ke++,this._scopeFire=o,this.pointerType=i;var a=this;this._proxy={};var s=function(t){Object.defineProperty(n._proxy,t,{get:function(){return a[t]}})};for(var c in Pe)s(c);var l=function(t){Object.defineProperty(n._proxy,t,{value:function(){return a[t].apply(a,arguments)}})};for(var u in Oe)l(u);this._scopeFire("interactions:new",{interaction:this})}return o(t,[{key:"pointerMoveTolerance",get:function(){return 1}},{key:"pointerDown",value:function(t,e,n){var r=this.updatePointer(t,e,n,!0),i=this.pointers[r];this._scopeFire("interactions:down",{pointer:t,event:e,eventTarget:n,pointerIndex:r,pointerInfo:i,type:"down",interaction:this})}},{key:"start",value:function(t,e,n){return!(this.interacting()||!this.pointerIsDown||this.pointers.length<("gesture"===t.name?2:1)||!e.options[t.name].enabled)&&(Ut(this.prepared,t),this.interactable=e,this.element=n,this.rect=e.getRect(n),this.edges=this.prepared.edges?V({},this.prepared.edges):{left:!0,right:!0,top:!0,bottom:!0},this._stopped=!1,this._interacting=this._doPhase({interaction:this,event:this.downEvent,phase:"start"})&&!this._stopped,this._interacting)}},{key:"pointerMove",value:function(t,e,n){this.simulation||this.modification&&this.modification.endResult||this.updatePointer(t,e,n,!1);var r,i,o=this.coords.cur.page.x===this.coords.prev.page.x&&this.coords.cur.page.y===this.coords.prev.page.y&&this.coords.cur.client.x===this.coords.prev.client.x&&this.coords.cur.client.y===this.coords.prev.client.y;this.pointerIsDown&&!this.pointerWasMoved&&(r=this.coords.cur.client.x-this.coords.start.client.x,i=this.coords.cur.client.y-this.coords.start.client.y,this.pointerWasMoved=Q(r,i)>this.pointerMoveTolerance);var a,s,c,l=this.getPointerIndex(t),u={pointer:t,pointerIndex:l,pointerInfo:this.pointers[l],event:e,type:"move",eventTarget:n,dx:r,dy:i,duplicate:o,interaction:this};o||(a=this.coords.velocity,s=this.coords.delta,c=Math.max(s.timeStamp/1e3,.001),a.page.x=s.page.x/c,a.page.y=s.page.y/c,a.client.x=s.client.x/c,a.client.y=s.client.y/c,a.timeStamp=c),this._scopeFire("interactions:move",u),o||this.simulation||(this.interacting()&&(u.type=null,this.move(u)),this.pointerWasMoved&&et(this.coords.prev,this.coords.cur))}},{key:"move",value:function(t){t&&t.event||nt(this.coords.delta),(t=V({pointer:this._latestPointer.pointer,event:this._latestPointer.event,eventTarget:this._latestPointer.eventTarget,interaction:this},t||{})).phase="move",this._doPhase(t)}},{key:"pointerUp",value:function(t,e,n,r){var i=this.getPointerIndex(t);-1===i&&(i=this.updatePointer(t,e,n,!1));var o=/cancel$/i.test(e.type)?"cancel":"up";this._scopeFire("interactions:".concat(o),{pointer:t,pointerIndex:i,pointerInfo:this.pointers[i],event:e,eventTarget:n,type:o,curEventTarget:r,interaction:this}),this.simulation||this.end(e),this.removePointer(t,e)}},{key:"documentBlur",value:function(t){this.end(t),this._scopeFire("interactions:blur",{event:t,type:"blur",interaction:this})}},{key:"end",value:function(t){var e;this._ending=!0,t=t||this._latestPointer.event,this.interacting()&&(e=this._doPhase({event:t,interaction:this,phase:"end"})),this._ending=!1,!0===e&&this.stop()}},{key:"currentAction",value:function(){return this._interacting?this.prepared.name:null}},{key:"interacting",value:function(){return this._interacting}},{key:"stop",value:function(){this._scopeFire("interactions:stop",{interaction:this}),this.interactable=this.element=null,this._interacting=!1,this._stopped=!0,this.prepared.name=this.prevEvent=null}},{key:"getPointerIndex",value:function(t){var e=at(t);return"mouse"===this.pointerType||"pen"===this.pointerType?this.pointers.length-1:yt(this.pointers,(function(t){return t.id===e}))}},{key:"getPointerInfo",value:function(t){return this.pointers[this.getPointerIndex(t)]}},{key:"updatePointer",value:function(t,e,n,r){var i,o,a,s=at(t),c=this.getPointerIndex(t),l=this.pointers[c];return r=!1!==r&&(r||/(down|start)$/i.test(e.type)),l?l.pointer=t:(l=new _e(s,t,e,null,null),c=this.pointers.length,this.pointers.push(l)),st(this.coords.cur,this.pointers.map((function(t){return t.pointer})),this._now()),i=this.coords.delta,o=this.coords.prev,a=this.coords.cur,i.page.x=a.page.x-o.page.x,i.page.y=a.page.y-o.page.y,i.client.x=a.client.x-o.client.x,i.client.y=a.client.y-o.client.y,i.timeStamp=a.timeStamp-o.timeStamp,r&&(this.pointerIsDown=!0,l.downTime=this.coords.cur.timeStamp,l.downTarget=n,tt(this.downPointer,t),this.interacting()||(et(this.coords.start,this.coords.cur),et(this.coords.prev,this.coords.cur),this.downEvent=e,this.pointerWasMoved=!1)),this._updateLatestPointer(t,e,n),this._scopeFire("interactions:update-pointer",{pointer:t,event:e,eventTarget:n,down:r,pointerInfo:l,pointerIndex:c,interaction:this}),c}},{key:"removePointer",value:function(t,e){var n=this.getPointerIndex(t);if(-1!==n){var r=this.pointers[n];this._scopeFire("interactions:remove-pointer",{pointer:t,event:e,eventTarget:null,pointerIndex:n,pointerInfo:r,interaction:this}),this.pointers.splice(n,1),this.pointerIsDown=!1}}},{key:"_updateLatestPointer",value:function(t,e,n){this._latestPointer.pointer=t,this._latestPointer.event=e,this._latestPointer.eventTarget=n}},{key:"destroy",value:function(){this._latestPointer.pointer=null,this._latestPointer.event=null,this._latestPointer.eventTarget=null}},{key:"_createPreparedEvent",value:function(t,e,n,r){return new Se(this,t,this.prepared.name,e,this.element,n,r)}},{key:"_fireEvent",value:function(t){var e;null==(e=this.interactable)||e.fire(t),(!this.prevEvent||t.timeStamp>=this.prevEvent.timeStamp)&&(this.prevEvent=t)}},{key:"_doPhase",value:function(t){var e=t.event,n=t.phase,r=t.preEnd,i=t.type,o=this.rect;if(o&&"move"===n&&(H(this.edges,o,this.coords.delta[this.interactable.options.deltaSource]),o.width=o.right-o.left,o.height=o.bottom-o.top),!1===this._scopeFire("interactions:before-action-".concat(n),t))return!1;var a=t.iEvent=this._createPreparedEvent(e,n,r,i);return this._scopeFire("interactions:action-".concat(n),t),"start"===n&&(this.prevEvent=a),this._fireEvent(a),this._scopeFire("interactions:after-action-".concat(n),t),!0}},{key:"_now",value:function(){return Date.now()}}]),t}();function Ie(t){Me(t.interaction)}function Me(t){if(!function(t){return!(!t.offset.pending.x&&!t.offset.pending.y)}(t))return!1;var e=t.offset.pending;return Ae(t.coords.cur,e),Ae(t.coords.delta,e),H(t.edges,t.rect,e),e.x=0,e.y=0,!0}function ze(t){var e=t.x,n=t.y;this.offset.pending.x+=e,this.offset.pending.y+=n,this.offset.total.x+=e,this.offset.total.y+=n}function Ae(t,e){var n=t.page,r=t.client,i=e.x,o=e.y;n.x+=i,n.y+=o,r.x+=i,r.y+=o}Oe.offsetBy="";var Re={id:"offset",before:["modifiers","pointer-events","actions","inertia"],install:function(t){t.Interaction.prototype.offsetBy=ze},listeners:{"interactions:new":function(t){t.interaction.offset={total:{x:0,y:0},pending:{x:0,y:0}}},"interactions:update-pointer":function(t){return function(t){t.pointerIsDown&&(Ae(t.coords.cur,t.offset.total),t.offset.pending.x=0,t.offset.pending.y=0)}(t.interaction)},"interactions:before-action-start":Ie,"interactions:before-action-move":Ie,"interactions:before-action-end":function(t){var e=t.interaction;if(Me(e))return e.move({offset:!0}),e.end(),!1},"interactions:stop":function(t){var e=t.interaction;e.offset.total.x=0,e.offset.total.y=0,e.offset.pending.x=0,e.offset.pending.y=0}}},Ce=Re;var je=function(){function t(e){r(this,t),this.active=!1,this.isModified=!1,this.smoothEnd=!1,this.allowResume=!1,this.modification=void 0,this.modifierCount=0,this.modifierArg=void 0,this.startCoords=void 0,this.t0=0,this.v0=0,this.te=0,this.targetOffset=void 0,this.modifiedOffset=void 0,this.currentOffset=void 0,this.lambda_v0=0,this.one_ve_v0=0,this.timeout=void 0,this.interaction=void 0,this.interaction=e}return o(t,[{key:"start",value:function(t){var e=this.interaction,n=Fe(e);if(!n||!n.enabled)return!1;var r=e.coords.velocity.client,i=Q(r.x,r.y),o=this.modification||(this.modification=new me(e));if(o.copyFrom(e.modification),this.t0=e._now(),this.allowResume=n.allowResume,this.v0=i,this.currentOffset={x:0,y:0},this.startCoords=e.coords.cur.page,this.modifierArg=o.fillArg({pageCoords:this.startCoords,preEnd:!0,phase:"inertiastart"}),this.t0-e.coords.cur.timeStamp<50&&i>n.minSpeed&&i>n.endSpeed)this.startInertia();else{if(o.result=o.setAll(this.modifierArg),!o.result.changed)return!1;this.startSmoothEnd()}return e.modification.result.rect=null,e.offsetBy(this.targetOffset),e._doPhase({interaction:e,event:t,phase:"inertiastart"}),e.offsetBy({x:-this.targetOffset.x,y:-this.targetOffset.y}),e.modification.result.rect=null,this.active=!0,e.simulation=this,!0}},{key:"startInertia",value:function(){var t=this,e=this.interaction.coords.velocity.client,n=Fe(this.interaction),r=n.resistance,i=-Math.log(n.endSpeed/this.v0)/r;this.targetOffset={x:(e.x-i)/r,y:(e.y-i)/r},this.te=i,this.lambda_v0=r/this.v0,this.one_ve_v0=1-n.endSpeed/this.v0;var o=this.modification,a=this.modifierArg;a.pageCoords={x:this.startCoords.x+this.targetOffset.x,y:this.startCoords.y+this.targetOffset.y},o.result=o.setAll(a),o.result.changed&&(this.isModified=!0,this.modifiedOffset={x:this.targetOffset.x+o.result.delta.x,y:this.targetOffset.y+o.result.delta.y}),this.onNextFrame((function(){return t.inertiaTick()}))}},{key:"startSmoothEnd",value:function(){var t=this;this.smoothEnd=!0,this.isModified=!0,this.targetOffset={x:this.modification.result.delta.x,y:this.modification.result.delta.y},this.onNextFrame((function(){return t.smoothEndTick()}))}},{key:"onNextFrame",value:function(t){var e=this;this.timeout=Lt.request((function(){e.active&&t()}))}},{key:"inertiaTick",value:function(){var t,e,n,r,i,o,a,s=this,c=this.interaction,l=Fe(c).resistance,u=(c._now()-this.t0)/1e3;if(u=0;a--){var d=p[a];if(d.selector===t&&d.context===e){for(var h=d.listeners,v=h.length-1;v>=0;v--){var g=h[v];if(g.func===i&&Ne(g.options,u)){h.splice(v,1),h.length||(p.splice(a,1),s(e,n,c),s(e,n,l,!0)),f=!0;break}}if(f)break}}},delegateListener:c,delegateUseCapture:l,delegatedEvents:r,documents:i,targets:n,supportsOptions:!1,supportsPassive:!1};function a(t,e,r,i){if(t.addEventListener){var a=Ge(i),s=bt(n,(function(e){return e.eventTarget===t}));s||(s={eventTarget:t,events:{}},n.push(s)),s.events[e]||(s.events[e]=[]),bt(s.events[e],(function(t){return t.func===r&&Ne(t.options,a)}))||(t.addEventListener(e,r,o.supportsOptions?a:a.capture),s.events[e].push({func:r,options:a}))}}function s(t,e,r,i){if(t.addEventListener&&t.removeEventListener){var a=yt(n,(function(e){return e.eventTarget===t})),c=n[a];if(c&&c.events)if("all"!==e){var l=!1,u=c.events[e];if(u){if("all"===r){for(var p=u.length-1;p>=0;p--){var f=u[p];s(t,e,f.func,f.options)}return}for(var d=Ge(i),h=0;h=2)continue;if(!i.interacting()&&e===i.pointerType)return i}return null}};function Ke(t,e){return t.pointers.some((function(t){return t.id===e}))}var $e=He,Je=["pointerDown","pointerMove","pointerUp","updatePointer","removePointer","windowBlur"];function Qe(t,e){return function(n){var r=e.interactions.list,i=dt(n),o=ht(n),a=o[0],s=o[1],c=[];if(/^touch/.test(n.type)){e.prevTouchTime=e.now();for(var l=0,u=n.changedTouches;l=0;r--){var i=e.interactions.list[r];i.interactable===n&&(i.stop(),e.fire("interactions:destroy",{interaction:i}),i.destroy(),e.interactions.list.length>2&&e.interactions.list.splice(r,1))}}},onDocSignal:tn,doOnInteractions:Qe,methodNames:Je},nn=en,rn=function(t){return t[t.On=0]="On",t[t.Off=1]="Off",t}(rn||{}),on=function(){function t(e,n,i,o){r(this,t),this.target=void 0,this.options=void 0,this._actions=void 0,this.events=new Ve,this._context=void 0,this._win=void 0,this._doc=void 0,this._scopeEvents=void 0,this._actions=n.actions,this.target=e,this._context=n.context||i,this._win=y(B(e)?this._context:e),this._doc=this._win.document,this._scopeEvents=o,this.set(n)}return o(t,[{key:"_defaults",get:function(){return{base:{},perAction:{},actions:{}}}},{key:"setOnEvents",value:function(t,e){return w.func(e.onstart)&&this.on("".concat(t,"start"),e.onstart),w.func(e.onmove)&&this.on("".concat(t,"move"),e.onmove),w.func(e.onend)&&this.on("".concat(t,"end"),e.onend),w.func(e.oninertiastart)&&this.on("".concat(t,"inertiastart"),e.oninertiastart),this}},{key:"updatePerActionListeners",value:function(t,e,n){var r,i=this,o=null==(r=this._actions.map[t])?void 0:r.filterEventType,a=function(t){return(null==o||o(t))&&ve(t,i._actions)};(w.array(e)||w.object(e))&&this._onOff(rn.Off,t,e,void 0,a),(w.array(n)||w.object(n))&&this._onOff(rn.On,t,n,void 0,a)}},{key:"setPerAction",value:function(t,e){var n=this._defaults;for(var r in e){var i=r,o=this.options[t],a=e[i];"listeners"===i&&this.updatePerActionListeners(t,o.listeners,a),w.array(a)?o[i]=mt(a):w.plainObject(a)?(o[i]=V(o[i]||{},ge(a)),w.object(n.perAction[i])&&"enabled"in n.perAction[i]&&(o[i].enabled=!1!==a.enabled)):w.bool(a)&&w.object(n.perAction[i])?o[i].enabled=a:o[i]=a}}},{key:"getRect",value:function(t){return t=t||(w.element(this.target)?this.target:null),w.string(this.target)&&(t=t||this._context.querySelector(this.target)),L(t)}},{key:"rectChecker",value:function(t){var e=this;return w.func(t)?(this.getRect=function(n){var r=V({},t.apply(e,n));return"width"in r||(r.width=r.right-r.left,r.height=r.bottom-r.top),r},this):null===t?(delete this.getRect,this):this.getRect}},{key:"_backCompatOption",value:function(t,e){if(B(e)||w.object(e)){for(var n in this.options[t]=e,this._actions.map)this.options[n][t]=e;return this}return this.options[t]}},{key:"origin",value:function(t){return this._backCompatOption("origin",t)}},{key:"deltaSource",value:function(t){return"page"===t||"client"===t?(this.options.deltaSource=t,this):this.options.deltaSource}},{key:"getAllElements",value:function(){var t=this.target;return w.string(t)?Array.from(this._context.querySelectorAll(t)):w.func(t)&&t.getAllElements?t.getAllElements():w.element(t)?[t]:[]}},{key:"context",value:function(){return this._context}},{key:"inContext",value:function(t){return this._context===t.ownerDocument||M(this._context,t)}},{key:"testIgnoreAllow",value:function(t,e,n){return!this.testIgnore(t.ignoreFrom,e,n)&&this.testAllow(t.allowFrom,e,n)}},{key:"testAllow",value:function(t,e,n){return!t||!!w.element(n)&&(w.string(t)?F(n,t,e):!!w.element(t)&&M(t,n))}},{key:"testIgnore",value:function(t,e,n){return!(!t||!w.element(n))&&(w.string(t)?F(n,t,e):!!w.element(t)&&M(t,n))}},{key:"fire",value:function(t){return this.events.fire(t),this}},{key:"_onOff",value:function(t,e,n,r,i){w.object(e)&&!w.array(e)&&(r=n,n=null);var o=$(e,n,i);for(var a in o){"wheel"===a&&(a=I.wheelEvent);for(var s=0,c=o[a];s=0;n--){var r=e[n],i=r.selector,o=r.context,a=r.listeners;i===this.target&&o===this._context&&e.splice(n,1);for(var s=a.length-1;s>=0;s--)this._scopeEvents.removeDelegate(this.target,this._context,t,a[s][0],a[s][1])}else this._scopeEvents.remove(this.target,"all")}}]),t}(),an=function(){function t(e){var n=this;r(this,t),this.list=[],this.selectorMap={},this.scope=void 0,this.scope=e,e.addListeners({"interactable:unset":function(t){var e=t.interactable,r=e.target,i=w.string(r)?n.selectorMap[r]:r[n.scope.id],o=yt(i,(function(t){return t===e}));i.splice(o,1)}})}return o(t,[{key:"new",value:function(t,e){e=V(e||{},{actions:this.scope.actions});var n=new this.scope.Interactable(t,e,this.scope.document,this.scope.events);return this.scope.addDocument(n._doc),this.list.push(n),w.string(t)?(this.selectorMap[t]||(this.selectorMap[t]=[]),this.selectorMap[t].push(n)):(n.target[this.scope.id]||Object.defineProperty(t,this.scope.id,{value:[],configurable:!0}),t[this.scope.id].push(n)),this.scope.fire("interactable:new",{target:t,options:e,interactable:n,win:this.scope._win}),n}},{key:"getExisting",value:function(t,e){var n=e&&e.context||this.scope.document,r=w.string(t),i=r?this.selectorMap[t]:t[this.scope.id];if(i)return bt(i,(function(e){return e._context===n&&(r||e.inContext(t))}))}},{key:"forEachMatch",value:function(t,e){for(var n=0,r=this.list;nMath.abs(u.y),l.coords,l.rect),V(i,l.coords));return l.eventProps},defaults:{ratio:"preserve",equalDelta:!1,modifiers:[],enabled:!1}};function gn(t,e,n){var r=t.startCoords,i=t.edgeSign;e?n.y=r.y+(n.x-r.x)*i.y:n.x=r.x+(n.y-r.y)*i.x}function mn(t,e,n,r){var i=t.startRect,o=t.startCoords,a=t.ratio,s=t.edgeSign;if(e){var c=r.width/a;n.y=o.y+(c-i.height)*s.y}else{var l=r.height*a;n.x=o.x+(l-i.width)*s.x}}var yn=be(vn,"aspectRatio"),bn=function(){};bn._defaults={};var xn=bn;function wn(t,e,n){return w.func(t)?G(t,e.interactable,e.element,[n.x,n.y,e]):G(t,e.interactable,e.element)}var En={start:function(t){var e=t.rect,n=t.startOffset,r=t.state,i=t.interaction,o=t.pageCoords,a=r.options,s=a.elementRect,c=V({left:0,top:0,right:0,bottom:0},a.offset||{});if(e&&s){var l=wn(a.restriction,i,o);if(l){var u=l.right-l.left-e.width,p=l.bottom-l.top-e.height;u<0&&(c.left+=u,c.right+=u),p<0&&(c.top+=p,c.bottom+=p)}c.left+=n.left-e.width*s.left,c.top+=n.top-e.height*s.top,c.right+=n.right-e.width*(1-s.right),c.bottom+=n.bottom-e.height*(1-s.bottom)}r.offset=c},set:function(t){var e=t.coords,n=t.interaction,r=t.state,i=r.options,o=r.offset,a=wn(i.restriction,n,e);if(a){var s=function(t){return!t||"left"in t&&"top"in t||((t=V({},t)).left=t.x||0,t.top=t.y||0,t.right=t.right||t.left+t.width,t.bottom=t.bottom||t.top+t.height),t}(a);e.x=Math.max(Math.min(s.right-o.right,e.x),s.left+o.left),e.y=Math.max(Math.min(s.bottom-o.bottom,e.y),s.top+o.top)}},defaults:{restriction:null,elementRect:null,offset:null,endOnly:!1,enabled:!1}},Tn=be(En,"restrict"),Sn={top:1/0,left:1/0,bottom:-1/0,right:-1/0},_n={top:-1/0,left:-1/0,bottom:1/0,right:1/0};function Pn(t,e){for(var n=0,r=["top","left","bottom","right"];n .btn { + font-size: 1.08333333rem; + padding: 6px 8px; + margin-top: 39px; + margin-left: -39px; + } + + .dropdown-item { + font-size: 1.08333333rem; + font-weight: 500; + } + + .dropdown-addblock, .o-dropdown-menu { + left: -3.5em; + padding: 0em 7em; + margin-top: 38px; + width: 276px; + } + + .toggle-btn { + position: absolute; + margin-top: -16px; + margin-left: 56px; + left: 1.2em; + top: 2.3em; + height: 10px; + } + + #edit_layout { + display: none; + border-radius: inherit; + } + + #search-button { + position: relative; + left: 3.5em; + } + + #searchclear { + position: absolute; + margin: 0.3rem 0 0 9.9rem; + } + + .search-box { + top: -3em; + left: 7.5em; + margin: 0em -1.9em; + width: 79%; + } + + + .navbar-toggler { + margin: 0.2em 1em; + padding: 0.25rem 0.5rem; + } + + .dropdown-add-items { + position: absolute; + color: #e4e4e4; + top: -2.6em; + left: 3.5em; + } +} diff --git a/odoo_dynamic_dashboard/static/src/js/DynamicDashboard.js b/odoo_dynamic_dashboard/static/src/js/DynamicDashboard.js deleted file mode 100644 index cbdfd2213..000000000 --- a/odoo_dynamic_dashboard/static/src/js/DynamicDashboard.js +++ /dev/null @@ -1,54 +0,0 @@ -/** @odoo-module */ - -import { registry} from '@web/core/registry'; -import { DynamicDashboardTile} from './DynamicDashboardTile' -import { DynamicDashboardChart} from './DynamicDashboardChart' -import { useService } from "@web/core/utils/hooks"; -const { Component, mount} = owl -export class DynamicDashboard extends Component { - setup(){ - this.action = useService("action"); - this.rpc = this.env.services.rpc - this.renderDashboard() - } - async renderDashboard() { - const action = this.action - const rpc = this.rpc - await this.rpc('/get/values', {'action_id': this.props.actionId}).then(function(response){ - if ($('.o_dynamic_dashboard')[0]){ - for (let i = 0; i < response.length; i++) { - if (response[i].type === 'tile'){ - mount(DynamicDashboardTile, $('.o_dynamic_tile')[0], { props: { - widget: response[i], doAction: action - }}); - } - else{ - mount(DynamicDashboardChart, $('.o_dynamic_graph')[0], { props: { - widget: response[i], doAction: action, rpc: rpc - }}); - } - } - } - }) - } - async _onClick_add_block(e){ - var self = this; - var self_props = this.props; - var self_env = self.env; - var type = $(e.target).attr('data-type'); - await this.rpc('/create/tile',{'type' : type, 'action_id': self.props.actionId}).then(function(response){ - if(response['type'] == 'tile'){ - mount(DynamicDashboardTile, $('.o_dynamic_tile')[0], { props: { - widget: response, doAction: self.action - }}); - } - else{ - mount(DynamicDashboardChart, $('.o_dynamic_graph')[0], { props: { - widget: response, doAction: self.action - }}); - } - }) - } -} -DynamicDashboard.template = "owl.dynamic_dashboard" -registry.category("actions").add("owl.dynamic_dashboard", DynamicDashboard) diff --git a/odoo_dynamic_dashboard/static/src/js/DynamicDashboardChart.js b/odoo_dynamic_dashboard/static/src/js/DynamicDashboardChart.js deleted file mode 100644 index 743a27352..000000000 --- a/odoo_dynamic_dashboard/static/src/js/DynamicDashboardChart.js +++ /dev/null @@ -1,82 +0,0 @@ -/** @odoo-module */ - -import { registry} from '@web/core/registry'; -import { loadJS} from '@web/core/assets'; -import { getColor } from "@web/core/colors/colors"; -const { Component, xml, onWillStart, useRef, onMounted } = owl - -export class DynamicDashboardChart extends Component { - setup() { - this.doAction = this.props.doAction.doAction - this.chartRef = useRef("chart") - onWillStart(async () => { - await loadJS("https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js") - }) - onMounted(()=> this.renderChart()) - } - renderChart(){ - if (this.props.widget.graph_type){ - const x_axis = this.props.widget.x_axis - const y_axis = this.props.widget.y_axis - const data = [] - for (let i = 0; i < x_axis.length; i++) { - const value = { key: x_axis[i], value: y_axis[i] } - data.push(value); - } - new Chart( - this.chartRef.el, - { - type: this.props.widget.graph_type || 'bar', - data: { - labels: data.map(row => row.key), - datasets: [ - { - label: this.props.widget.measured_field, - data: data.map(row => row.value), - backgroundColor: data.map((_, index) => getColor(index)), - hoverOffset : 4 - } - ] - }, - } - ); - } - } - async getConfiguration(){ - var id = this.props.widget.id - await this.doAction({ - type: 'ir.actions.act_window', - res_model: 'dashboard.block', - res_id: id, - view_mode: 'form', - views: [[false, "form"]] - }); - } -} -DynamicDashboardChart.template = xml ` -
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-` - - diff --git a/odoo_dynamic_dashboard/static/src/js/DynamicDashboardTile.js b/odoo_dynamic_dashboard/static/src/js/DynamicDashboardTile.js deleted file mode 100644 index 1ce94ed6f..000000000 --- a/odoo_dynamic_dashboard/static/src/js/DynamicDashboardTile.js +++ /dev/null @@ -1,52 +0,0 @@ -/** @odoo-module */ - -import { registry} from '@web/core/registry'; -const { Component, xml } = owl -export class DynamicDashboardTile extends Component { - setup() { - super.setup(...arguments); - this.action = this.props.doAction - } - async getRecord() { - var model_name = this.props.widget.model_name - if (model_name){ - await this.action.doAction({ - type: 'ir.actions.act_window', - res_model: model_name, - view_mode: 'tree', - views: [[false, "tree"]], - domain: this.props.widget.domain, - }); - } - } - async getConfiguration() { - var id = this.props.widget.id - await this.action.doAction({ - type: 'ir.actions.act_window', - res_model: 'dashboard.block', - res_id: id, - view_mode: 'form', - views: [[false, "form"]] - }); - } -} -DynamicDashboardTile.template = xml`
-
- - - -
-
- -
-
-

-
-

-
-
-
-
-
` - diff --git a/odoo_dynamic_dashboard/static/src/js/dynamic_dashboard.js b/odoo_dynamic_dashboard/static/src/js/dynamic_dashboard.js new file mode 100644 index 000000000..af5ae262d --- /dev/null +++ b/odoo_dynamic_dashboard/static/src/js/dynamic_dashboard.js @@ -0,0 +1,391 @@ +/** @odoo-module **/ +import { registry } from "@web/core/registry"; +import { loadJS } from '@web/core/assets'; +import { DynamicDashboardTile} from './dynamic_dashboard_tile'; +import { DynamicDashboardChart} from './dynamic_dashboard_chart'; +import { useService } from "@web/core/utils/hooks"; +const { Component, useRef, mount, onWillStart, onMounted} = owl; + +export class OdooDynamicDashboard extends Component { + // Setup function to run when the template of the class OdooDynamicDashboard renders + setup() { + this.ThemeSelector = useRef('ThemeSelector'); + this.action = useService("action"); + this.orm = useService("orm"); + this.dialog = useService("dialog"); + this.actionId = this.props.actionId + this.rpc = useService("rpc"); + onWillStart(async () => { + await loadJS("https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js") + await loadJS("https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js") + await loadJS("https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.9.3/html2pdf.bundle.min.js") + }) + onMounted(()=>{ + this.renderDashboard(); + }) + } + + onChangeTheme(){ + /* Function for changing color of the theme of the dashboard */ + $(".container").attr('style', this.ThemeSelector.el.value + 'min-height:-webkit-fill-available;') + } + + ResizeDrag() { + /* Function for resizing and dragging the div resize-drag */ + $('.items .resize-drag').each(function(index, element) { + interact(element).resizable({ + edges: { left: true, right: true, bottom: true, top: true }, + listeners: { + move (event) { + var target = event.target + var x = (parseFloat(target.getAttribute('data-x')) || 0) + var y = (parseFloat(target.getAttribute('data-y')) || 0) + // update the element's style + target.style.width = event.rect.width + 'px' + target.style.height = event.rect.height + 'px' + // translate when resizing from top or left edges + x += event.deltaRect.left + y += event.deltaRect.top + } + }, + modifiers: [ + // keep the edges inside the parent + interact.modifiers.restrictEdges({ + outer: 'parent' + }), + // minimum size + interact.modifiers.restrictSize({ + min: { width: 100, height: 50 } + }) + ], + inertia: true + }).draggable({ + listeners: {move: dragMoveListener}, + inertia: true, + modifiers: [ + interact.modifiers.restrictRect({ + restriction: 'parent', + endOnly: true + }) + ] + }) + + function dragMoveListener (event) { + var target = event.target + // keep the dragged position in the data-x/data-y attributes + var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx + var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy + // translate the element + target.style.transform = 'translate(' + x + 'px, ' + y + 'px)' + // update the posiion attributes + target.setAttribute('data-x', x) + target.setAttribute('data-y', y) + } + // this function is used later in the resizing + window.dragMoveListener = dragMoveListener + }); + } + + async renderDashboard(){ + /* Function for rendering the dashboard */ + var self = this; + $("#save_layout").hide(); + await this.orm.call('dashboard.theme', 'get_records', [[]]).then(function (response) { + response.forEach((ev) => { + const options = document.createElement("option"); + options.value = ev.style; + options.text = ev.name; + self.ThemeSelector.el.append(options) + }); + }) + await this.orm.call("dashboard.block", "get_dashboard_vals", [[], this.actionId]).then( function (response){ + for (let i = 0; i < response.length; i++) { + if (response[i].type === 'tile'){ + mount(DynamicDashboardTile, $('.items')[0], { props: { + widget: response[i], doAction: self.action, dialog:self.dialog, orm: self.orm + }}); + } + else{ + mount(DynamicDashboardChart, $('.items')[0], { props: { + widget: response[i], doAction: self.action, rpc: self.rpc, dialog:self.dialog, orm: self.orm + }}); + } + } + }) + } + + editLayout(ev) { + /* Function for editing the layout , it enables resizing and dragging functionality */ + $('.items .resize-drag').each(function(index, element) { + interact(element).draggable(true) + interact(element).resizable(true) + }); + ev.stopPropagation(); + ev.preventDefault(); + $("#edit_layout").hide(); + $("#save_layout").show(); + this.ResizeDrag() + } + + saveLayout(ev){ + /* Function for saving the layout */ + var self = this; + ev.stopPropagation(); + ev.preventDefault(); + $("#edit_layout").show(); + $("#save_layout").hide(); + var data_list = [] + $('.items .resize-drag').each(function(index, element) { + interact(element).draggable(false) + interact(element).resizable(false) + data_list.push({ + 'id' : element.dataset['id'], + 'data-x': element.dataset['x'], + 'data-y': element.dataset['y'], + 'height': element.clientHeight, + 'width': element.clientWidth, + }) + }); + self.orm.call('dashboard.block','get_save_layout', [[], data_list]).then( function (response){ + window.location.reload(); + }); + } + + changeViewMode(ev){ + /* Function for changing the mode of the view */ + ev.stopPropagation(); + ev.preventDefault(); + const currentMode = $(".mode").attr("mode"); + if (currentMode == "light"){ + $('.theme').attr('style','display: none;') + $(".container").attr('style', 'background-color: #383E45;min-height:-webkit-fill-available; !important') + $(".mode").attr("mode", "dark") + $(".bi-moon-stars-fill").attr('class', 'bi bi-cloud-sun-fill view-mode-icon') + $(".bi-cloud-sun-fill").attr('style', 'color:black;margin-left:10px;') + $(".mode").attr('style','display: none !important'); + $("#search-input-chart").attr('style', 'background-color: white; !important') + $("#search-button").attr('style', 'background-color: #BB86FC; !important') + $("#dropdownMenuButton").attr('style', 'background-color: #03DAC5;margin-top:-4px; !important') + $("#text_add").attr('style', 'color: black; !important') + $(".date-label").attr('style', 'color: black;font-family:monospace; !important') + $(".block_setting").attr('style', 'color: white; !important') + $(".block_delete").attr('style', 'color: white; !important') + $(".block_image").attr('style', 'color: #03DAC5; !important') + $(".block_pdf").attr('style', 'color: #03DAC5; !important') + $(".block_csv").attr('style', 'color: #03DAC5; !important') + $(".block_xlsx").attr('style', 'color: #03DAC5; !important') + } + else { + $('.theme').attr('style','display: block;') + $(".container").attr('style', this.ThemeSelector.el.value + 'min-height:-webkit-fill-available;') + $(".mode").attr("mode", "light") + $(".bi-cloud-sun-fill").attr('class', 'bi bi-moon-stars-fill view-mode-icon') + $(".view-mode-icon").attr('style', 'color:black;margin-left:10px; !important') + $(".mode").attr('style','display: none !important'); + $(".mode").attr('style','color: white !important'); + $("#search-input-chart").attr('style', 'background-color: none; !important') + $("#search-button").attr('style', 'background-color: none; !important') + $("#dropdownMenuButton").attr('style', 'background-color: none;margin-top:-4px; !important') + $("#text_add").attr('style', 'color: white; !important') + $(".date-label").attr('style', 'color: black; !important;font-family:monospace; !important') + $(".block_setting").attr('style', 'color: black; !important') + $(".block_delete").attr('style', 'color: black; !important') + $(".block_image").attr('style', 'color: black; !important') + $(".block_pdf").attr('style', 'color: black; !important') + $(".block_csv").attr('style', 'color: black; !important') + $(".block_xlsx").attr('style', 'color: black; !important') + } + } + onClickAdd(event){ + /* For enabling the toggle button */ + event.stopPropagation(); + event.preventDefault(); + $(".dropdown-addblock").toggle() + } + onClickAddItem(event){ + /* Function for adding tiles and charts */ + event.stopPropagation(); + event.preventDefault(); + self = this; + var type = event.target.getAttribute('data-type'); + if (type == 'graph'){ + var chart_type = event.target.getAttribute('data-chart_type'); + } + if (type == 'tile'){ + var randomColor = '#' + ('000000' + Math.floor(Math.random() * 16777216).toString(16)).slice(-6); + this.action.doAction({ + type: 'ir.actions.act_window', + res_model: 'dashboard.block', + view_mode: 'form', + views: [[false, 'form']], + context: { + 'form_view_initial_mode': 'edit', + 'default_name': 'New Tile', + 'default_type': type, + 'default_height': '155px', + 'default_width': '300px', + 'default_tile_color': randomColor, + 'default_text_color': '#FFFFFF', + 'default_val_color': '#F3F3F3', + 'default_fa_icon': 'fa fa-bar-chart', + 'default_client_action_id': parseInt(self.actionId) + } + }) + } + else{ + this.action.doAction({ + type: 'ir.actions.act_window', + res_model: 'dashboard.block', + view_mode: 'form', + views: [[false, 'form']], + context: { + 'form_view_initial_mode': 'edit', + 'default_name': 'New ' + chart_type, + 'default_type': type, + 'default_height': '565px', + 'default_width': '588px', + 'default_graph_type': chart_type, + 'default_fa_icon': 'fa fa-bar-chart', + 'default_client_action_id': parseInt(self.actionId) + }, + }) + } + } + + dateFilter(){ + /* Function for filtering the data based on the creation date */ + $(".items").empty(); + var start_date = $("#start-date").val(); + var end_date = $("#end-date").val(); + var self = this; + if (!start_date){ + start_date = "null" + } + if (!end_date){ + end_date = "null" + } + this.orm.call("dashboard.block", "get_dashboard_vals", [[], this.actionId, start_date, end_date]).then( function (response){ + for (let i = 0; i < response.length; i++) { + if (response[i].type === 'tile'){ + mount(DynamicDashboardTile, $('.items')[0], { props: { + widget: response[i], doAction: self.action, dialog:self.dialog, orm: self.orm + }}); + } + else{ + mount(DynamicDashboardChart, $('.items')[0], { props: { + widget: response[i], doAction: self.action, rpc: self.rpc, dialog:self.dialog, orm: self.orm + }}); + } + } + }) + } + + async clickSearch(){ + /* Function for searching the blocks with their names */ + var input = $("#search-input-chart").val(); + await this.rpc('/custom_dashboard/search_input_chart', {'search_input': input}).then(function (response) { + var blocks = $(".items .resize-drag"); + blocks.each(function(index, element){ + var dataId = $(element).data('id'); + if (response.includes(dataId)){ + $(element).css("visibility", "visible"); + } + else{ + $(element).css("visibility", "hidden"); + } + }) + }) + } + + showViewMode(ev){ + /* Function for showing the mode text */ + const currentMode = $(".mode").attr("mode"); + if (currentMode == "light"){ + $(".mode").text("Dark Mode") + $(".mode").attr('style','display: inline-block !important; color: black !important'); + } + else{ + $(".mode").text("Light Mode") + $(".mode").attr('style','display: inline-block !important; color: black !important'); + } + } + hideViewMode(ev){ + /* Function for hiding the mode text */ + $(".mode").fadeOut(2000); + } + clearSearch(){ + /* Function for clearing the search input */ + $("#search-input-chart").val(''); + var blocks = $(".items .resize-drag"); + blocks.each(function(index, element){ + $(element).css("visibility", "visible"); + }) + } + + async printPdf() { + /* Function for printing whole dashboard in pdf format */ + var elements = $('.items .resize-drag') + var newElement = document.createElement('div'); + newElement.className = 'pdf'; + elements.each(function(index, elem){ + newElement.appendChild(elem); + }); + for (var x=0; x< $(newElement)[0].children.length; x++){ + $($(newElement)[0].children[x])[0].style.transform = "" + } + var opt = { + margin: 0.3, + filename: 'Dashboard.pdf', + image: { type: 'jpeg', quality: 1 }, + html2canvas: { scale: 1 }, + jsPDF: { unit: 'mm', format: 'a3', orientation: 'portrait' } + }; + html2pdf().set(opt).from(newElement).save().then(()=>{ + window.location.reload() + }) + } + + async createPDF(){ + /* Function for getting pdf data in string format */ + var elements = $('.items .resize-drag') + var newElement = document.createElement('div'); + newElement.className = 'pdf'; + elements.each(function(index, elem){ + newElement.appendChild(elem); + }); + for (var x=0; x< $(newElement)[0].children.length; x++){ + $($(newElement)[0].children[x])[0].style.transform = "" + } + var opt = { + margin: 0.3, + filename: 'Dashboard.pdf', + image: { type: 'jpeg', quality: 1 }, + html2canvas: { scale: 1 }, + jsPDF: { unit: 'mm', format: 'a3', orientation: 'portrait' } + }; + var pdf = html2pdf().set(opt).from(newElement).toPdf() + var pdfOutput = await pdf.output('datauristring'); + console.log(pdfOutput) + return pdfOutput + } + + async sendMail(){ + /* Function for creating pdf and sending mail to the selected users */ + /* This function calls the createPDF() function and returns the pdf datas */ + var created_pdf = await this.createPDF(); + var base64code = created_pdf.split(',')[1]; + this.action.doAction({ + type: 'ir.actions.act_window', + name: 'SEND MAIL', + res_model: 'dashboard.mail', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + context: { + 'default_base64code': base64code, + } + }) + } +} +OdooDynamicDashboard.template = "owl.OdooDynamicDashboard" +registry.category("actions").add("OdooDynamicDashboard", OdooDynamicDashboard) diff --git a/odoo_dynamic_dashboard/static/src/js/dynamic_dashboard_chart.js b/odoo_dynamic_dashboard/static/src/js/dynamic_dashboard_chart.js new file mode 100644 index 000000000..da150776e --- /dev/null +++ b/odoo_dynamic_dashboard/static/src/js/dynamic_dashboard_chart.js @@ -0,0 +1,215 @@ +/** @odoo-module **/ +import { loadJS } from '@web/core/assets'; +import { getColor } from "@web/core/colors/colors"; +import { _t } from "@web/core/l10n/translation"; +import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; +const { Component, xml, onWillStart, useRef, onMounted } = owl + +export class DynamicDashboardChart extends Component { + // Setup function of the class DynamicDashboardChart + setup() { + this.doAction = this.props.doAction.doAction; + this.chartRef = useRef("chart"); + this.dialog = this.props.dialog; + onWillStart(async () => { + await loadJS("https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js") + await loadJS("https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js") + await loadJS("https://cdn.jsdelivr.net/npm/exceljs@4.4.0/dist/exceljs.min.js") + }) + onMounted(()=> this.renderChart()) + } + // Function to export the chart in pdf, image, xlsx and csv format + exportItem(ev){ + ev.stopPropagation(); + ev.preventDefault(); + var type = $(ev.currentTarget).attr('data-type'); + var canvas = $($($(ev.currentTarget)[0].offsetParent)[0].children[0].lastChild).find("#canvas")[0] + var dataTitle = $(canvas).attr('data-title') + var bgCanvas = document.createElement("canvas"); + bgCanvas.width = canvas.width; + bgCanvas.height = canvas.height; + var bgCtx = bgCanvas.getContext("2d"); + bgCtx.fillStyle = "white"; + bgCtx.fillRect(0, 0, canvas.width, canvas.height); + bgCtx.drawImage(canvas, 0, 0); + var imgData = bgCanvas.toDataURL("image/png"); + if (type === 'png') { + var downloadLink = document.createElement('a'); + downloadLink.href = imgData; + downloadLink.download = `${dataTitle}.png`; + document.body.appendChild(downloadLink); + downloadLink.click(); + document.body.removeChild(downloadLink); + } + if (type === 'pdf') { + var pdf = new jsPDF(); + pdf.addImage(imgData, 'PNG', 0, 0); + pdf.save(`${dataTitle}.pdf`); + } + if (type === 'xlsx'){ + var rows = []; + var items = $('.resize-drag'); + for (let i = 0; i < items.length; i++) { + if ($(items[i]).attr('data-id') === $(ev.currentTarget).attr('data-id')) { + rows.push(this.props.widget.x_axis); + rows.push(this.props.widget.y_axis); + } + } + // Prepare the workbook + const workbook = new ExcelJS.Workbook(); + const worksheet = workbook.addWorksheet('My Sheet'); + for(let i = 0; i < rows.length; i++){ + worksheet.addRow(rows[i]); + } + const image = workbook.addImage({ + base64: imgData, + extension: 'png', + }); + worksheet.addImage(image, { + tl: { col: 0, row: 4 }, + ext: { width: canvas.width, height: canvas.height } + }); + // Save workbook to a file + workbook.xlsx.writeBuffer() + .then((buffer) => { + // Create a Blob object from the buffer + let blob = new Blob([buffer], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}); + let link = document.createElement('a'); + link.href = window.URL.createObjectURL(blob); + link.setAttribute("download", `${dataTitle}.xlsx`); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }) + } + if (type === 'csv') { + var rows = []; + var items = $('.resize-drag') + for (let i = 0; i < items.length; i++) { + if ($(items[i]).attr('data-id') === $(ev.currentTarget).attr('data-id')) { + rows.push(this.props.widget.x_axis); + rows.push(this.props.widget.y_axis); + } + } + let csvContent = "data:text/csv;charset=utf-8,"; + rows.forEach(function (rowArray) { + let row = rowArray.join(","); + csvContent += row + "\r\n"; + }); + var link = document.createElement("a"); + link.setAttribute("href", encodeURI(csvContent)); + link.setAttribute("download", `${dataTitle}.csv`); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + } + // Function to get the configuration of the chart + async getConfiguration(ev){ + ev.stopPropagation(); + ev.preventDefault(); + var id = this.props.widget.id + await this.doAction({ + type: 'ir.actions.act_window', + res_model: 'dashboard.block', + res_id: id, + view_mode: 'form', + views: [[false, "form"]] + }); + } + // Function to remove the chart + async removeTile(ev){ + ev.stopPropagation(); + ev.preventDefault(); + this.dialog.add(ConfirmationDialog, { + title: _t("Delete Confirmation"), + body: _t("Are you sure you want to delete this item?"), + confirmLabel: _t("YES, I'M SURE"), + cancelLabel: _t("NO, GO BACK"), + confirm: async () => { + await this.props.orm.unlink("dashboard.block", [this.props.widget.id]); + location.reload(); + }, + cancel: () => {}, + }); + } + // Function to render the chart + renderChart(){ + if (this.props.widget.graph_type){ + const x_axis = this.props.widget.x_axis + const y_axis = this.props.widget.y_axis + const data = [] + for (let i = 0; i < x_axis.length; i++) { + const value = { key: x_axis[i], value: y_axis[i] } + data.push(value); + } + new Chart( + this.chartRef.el, + { + type: this.props.widget.graph_type || 'bar', + data: { + labels: data.map(row => row.key), + datasets: [ + { + label: this.props.widget.measured_field || 'Data', + data: data.map(row => row.value), + backgroundColor: data.map((_, index) => getColor(index)), + hoverOffset : 4 + } + ] + }, + } + ); + } + } +} + +DynamicDashboardChart.template = xml` +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+

+ +

+
+
+ +
+
+
+
+ ` diff --git a/odoo_dynamic_dashboard/static/src/js/dynamic_dashboard_tile.js b/odoo_dynamic_dashboard/static/src/js/dynamic_dashboard_tile.js new file mode 100644 index 000000000..8cfd238f0 --- /dev/null +++ b/odoo_dynamic_dashboard/static/src/js/dynamic_dashboard_tile.js @@ -0,0 +1,89 @@ +/** @odoo-module **/ +import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; +import { _t } from "@web/core/l10n/translation"; +const { Component, xml } = owl; + +export class DynamicDashboardTile extends Component { + // Setup function of the class DynamicDashboardTile + setup() { + this.doAction = this.props.doAction.doAction; + this.dialog = this.props.dialog; + this.orm = this.props.orm; + } + // Function to get the configuration of the tile + async getConfiguration(ev){ + ev.stopPropagation(); + ev.preventDefault(); + var id = this.props.widget.id + await this.doAction({ + type: 'ir.actions.act_window', + res_model: 'dashboard.block', + res_id: id, + view_mode: 'form', + views: [[false, "form"]] + }); + } + // Function to remove the tile + async removeTile(ev){ + ev.stopPropagation(); + ev.preventDefault(); + this.dialog.add(ConfirmationDialog, { + title: _t("Delete Confirmation"), + body: _t("Are you sure you want to delete this item?"), + confirmLabel: _t("YES, I'M SURE"), + cancelLabel: _t("NO, GO BACK"), + confirm: async () => { + await this.orm.unlink("dashboard.block", [this.props.widget.id]); + location.reload(); + }, + cancel: () => {}, + }); + } + // Function for getting records by double click + async getRecords(){ + var model_name = this.props.widget.model_name; + if (model_name){ + await this.doAction({ + type: 'ir.actions.act_window', + res_model: model_name, + view_mode: 'tree', + views: [[false, "tree"]], + domain: this.props.widget.domain, + }); + } + } +} +DynamicDashboardTile.template = xml ` +
+
+ + + + + + +
+ +
+
+

+ +

+
+

+ +

+
+
+
+
` \ No newline at end of file diff --git a/odoo_dynamic_dashboard/static/src/scss/dynamic_dashboard.scss b/odoo_dynamic_dashboard/static/src/scss/dynamic_dashboard.scss new file mode 100644 index 000000000..051245037 --- /dev/null +++ b/odoo_dynamic_dashboard/static/src/scss/dynamic_dashboard.scss @@ -0,0 +1,208 @@ + +:root { + /* Colors */ + --green: #00C689; + --blue: #3DA5F4; + --red: #F1536E; + --yellow: #FDA006; + /*Fonts*/ + --primary-font: 'Roboto', sans-serif; +} + +html .o_web_client > .o_action_manager { + overflow: auto; +} + +.bg-green { + background-color: var(--green); +} + +.bg-blue { + background-color: var(--blue); +} + +.bg-red { + background-color: var(--red); +} + +.bg-yellow { + background-color: var(--yellow); +} + +.text-color-yellow { + color: var(--yellow); +} + +.text-color-green { + color: var(--green); +} + +.text-color-blue { + color: var(--blue); +} + +.text-color-red { + color: var(--red); +} + +.text-color-yellow { + color: var(--yellow); +} + +.tile-container__icon-container { + border-radius: 50%; + width: 4.75rem; + height: 4.75rem; + font-size: 28px; + margin-left: 15px; +} + +.tile-container__status-container { + margin-left: 2em; +} + +.status-container__title { + font-family: var(--primary-font); + font-weight: 500; + font-size: 1.5rem; + line-height: 1.5rem; +} + +.status-container__figures { + font-family: var(--primary-font); +} + +.status-container__figures > h3 { + font-weight: 700; + font-size: 1.5rem; + line-height: 1.813rem; +} + +.tile-container__setting-icon { + top: 0.638rem; +} + +.dark-theme { + /* Add dark theme styles here */ + .block_edit { + color: #b7b7b7 !important; +} + +.theme_icon:hover { +text-shadow: 0px 0px 5px #9388ff; +} +.add_block{ + color: #dfdfdf; +} + #ExportMenu { + color: #626262; + } + + .chart_title { + color: #dfdfdf; + margin: -0.5em 0 2em 0; + } + + .btn-search_edit, { + color: #313131; + border: none; + background-color: #979797 !important; + } + + #search-input-chart { + color: #ffffff; + border: 1px solid #4e4e4e; + background-color: #2A2A2A !important; + } + + #search-clear { + color: #1f1f1f !important; + } + + div.navbar { + color: #909090; + background-color: #2A2A2A !important; + } + + .navbar-collapse { + margin-bottom: 12px !important; + } + + h3 { + color: #909090; + } + + div.dropdown-addblock { + color: #909090; + background-color: #2A2A2A !important; + } + + #dropdownMenuButton { + color: #313131; + background-color: #979797 !important; + } + + div.card-body { + border-radius: 0.5em !important; + background-color: #2a2a2a !important; + } + + .o_kanban_record { + background-color: #e8e8e8 !important; + } + + .o_kanban_renderer { + background-color: #e8e8e8 !important; + } + + .oe_module_name { + background-color: #ffffff !important; + } + + background-color: #1C1B1B; + @media (max-width: 767px) { + .navbar-light .navbar-toggler { + color: #A7A7A72D; + border-color: #F6F6F621; + } + } +} +.btn-align-items{ + width: 134px; + font-size: small; + border-radius: revert; + height: 33px; +} +#edit_layout{ + background-color: #0c8444; +} +#save_layout{ + background-color: #b53c5d; + width: 134px; + height: 33px; + font-size: small; + border-radius: revert; +} +#search-button{ + width: 69px; + margin-left: 5px; +} +#search-input-chart{ + width: 206px; + height: 34px; + border: 1px solid black; +} +.search-clear{ + margin-left: -80px; +} +label input{ + appearance: none; +} +.mode{ + padding-left: 7px; + font-family: 'odoo_ui_icons'; + display: none; +} +.view-mode-icon{ + font-size: x-large; +} \ No newline at end of file diff --git a/odoo_dynamic_dashboard/static/src/scss/style.scss b/odoo_dynamic_dashboard/static/src/scss/style.scss deleted file mode 100644 index 4648339d0..000000000 --- a/odoo_dynamic_dashboard/static/src/scss/style.scss +++ /dev/null @@ -1,109 +0,0 @@ -.card{ - border: none; - border-radius: 0px; - box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; - -webkit-box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; - -moz-box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; -} -.card-header{ - background-color: transparent; - border: none; -} -:root { - /* Colors */ - --green: #00C689; - --blue: #3DA5F4; - --red: #F1536E; - --yellow: #FDA006; - /*Fonts*/ - --primary-font: 'Roboto', sans-serif; - } - - html .o_web_client > .o_action_manager { - overflow:auto; - } - - .bg-green { - background-color: var(--green); - } - - .bg-blue { - background-color: var(--blue); - } - - .bg-red { - background-color: var(--red); - } - - .bg-yellow { - background-color: var(--yellow); - } - - .text-color-yellow { - color: var(--yellow); - } - - .text-color-green { - color: var(--green); - } - - .text-color-blue { - color: var(--blue); - } - - .text-color-red { - color: var(--red); - } - - .text-color-yellow { - color: var(--yellow); - } - - .tile-container { - padding: 3.2rem 1.5rem; - border-radius: 2rem; - } - - .tile-container__icon-container { - border-radius: 50%; - width: 4.75rem; - height: 4.75rem; - font-size: 28px; - } - - .status-container__title { - font-family: var(--primary-font); - font-weight: 500; - font-size: 1.5rem; - line-height: 1.5rem; - } - - .status-container__figures { - font-family: var(--primary-font); - } - - .status-container__figures>h3 { - font-weight: 700; - font-size: 1.5rem; - line-height: 1.813rem; - } - - .tile-container__setting-icon { - top: 0.638rem; - right: 1rem; - } - -// Main Navbar Dropdown menu location override -.o_menu_systray > .o_user_menu > .o-dropdown--menu{ - left: auto !important; - right: 0px !important; -} -.button-container{ - padding-top: 5px; -} -.add_block{ - margin-left: 6px; -} -.tile-container__status-container{ - padding-left: 7px; -} diff --git a/odoo_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml b/odoo_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml index fe1372a49..079723614 100644 --- a/odoo_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml +++ b/odoo_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml @@ -1,21 +1,129 @@ - -
-
- - -
-
-
+ + + +
+ + + +
+ +
+ + + +
-
+ +
+ +
+
+ +
+
+
+
+
diff --git a/odoo_dynamic_dashboard/views/dashboard_menu_view.xml b/odoo_dynamic_dashboard/views/dashboard_menu_view.xml deleted file mode 100644 index d903439da..000000000 --- a/odoo_dynamic_dashboard/views/dashboard_menu_view.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - dashboard.menu.view.form - dashboard.menu - -
- - - - - - - - - - -
-
-
- - - dashboard.menu.view.tree - dashboard.menu - - - - - - - - - - Dashboard Menu - ir.actions.act_window - dashboard.menu - tree,form - - - - - -
diff --git a/odoo_dynamic_dashboard/views/dashboard_menu_views.xml b/odoo_dynamic_dashboard/views/dashboard_menu_views.xml new file mode 100644 index 000000000..72d6ce90e --- /dev/null +++ b/odoo_dynamic_dashboard/views/dashboard_menu_views.xml @@ -0,0 +1,66 @@ + + + + + dashboard.menu.view.kanban + dashboard.menu + + + + + +
+
+

+ Name: + +

+
+
+
+
+ Parent: +
+
+ +
+
+
+
+
+
+
+
+ + + dashboard.menu.view.form + dashboard.menu + +
+ + + + + + + + + + +
+
+
+ + + + Dashboards Menu + ir.actions.act_window + dashboard.menu + kanban,form + + + +
diff --git a/odoo_dynamic_dashboard/views/dashboard_theme_views.xml b/odoo_dynamic_dashboard/views/dashboard_theme_views.xml new file mode 100644 index 000000000..73f845146 --- /dev/null +++ b/odoo_dynamic_dashboard/views/dashboard_theme_views.xml @@ -0,0 +1,56 @@ + + + + + dashboard.theme.view.form + dashboard.theme + +
+ + +
+ +
+
+ + + + + + + + + + + +
+
+
+
+ + + + dashboard.theme.view.tree + dashboard.theme + + + + + + + + + Dashboard Theme + ir.actions.act_window + dashboard.theme + tree,form + + + + +
diff --git a/odoo_dynamic_dashboard/views/dashboard_view.xml b/odoo_dynamic_dashboard/views/dashboard_view.xml deleted file mode 100644 index 9d2187da2..000000000 --- a/odoo_dynamic_dashboard/views/dashboard_view.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Dashboard - owl.dynamic_dashboard - - - - - - diff --git a/odoo_dynamic_dashboard/views/dashboard_views.xml b/odoo_dynamic_dashboard/views/dashboard_views.xml new file mode 100644 index 000000000..182b7f7da --- /dev/null +++ b/odoo_dynamic_dashboard/views/dashboard_views.xml @@ -0,0 +1,14 @@ + + + + + Dashboard + OdooDynamicDashboard + + + + diff --git a/odoo_dynamic_dashboard/views/dynamic_block_view.xml b/odoo_dynamic_dashboard/views/dynamic_block_views.xml similarity index 61% rename from odoo_dynamic_dashboard/views/dynamic_block_view.xml rename to odoo_dynamic_dashboard/views/dynamic_block_views.xml index b310d1b55..c4dd7bade 100644 --- a/odoo_dynamic_dashboard/views/dynamic_block_view.xml +++ b/odoo_dynamic_dashboard/views/dynamic_block_views.xml @@ -1,50 +1,58 @@ - + dashboard.block.view.form dashboard.block
-
-

- -

-
+ + +
+ +
+
+
- + required="[('edit_mode','=', True)]" + options="{'no_create_edit':True,'no_create': True}"/> + - + - - - - - + - + - @@ -52,7 +60,7 @@
- + dashboard.block.view.tree dashboard.block @@ -64,7 +72,7 @@ - + Dashboard Block ir.actions.act_window @@ -72,8 +80,8 @@ tree,form {'default_edit_mode' : True} - - + + sequence="5" action="dashboard_block_action"/>
diff --git a/odoo_dynamic_dashboard/models/dashboard_block_line.py b/odoo_dynamic_dashboard/wizard/__init__.py similarity index 67% rename from odoo_dynamic_dashboard/models/dashboard_block_line.py rename to odoo_dynamic_dashboard/wizard/__init__.py index f9b7c0f0c..9a996c029 100644 --- a/odoo_dynamic_dashboard/models/dashboard_block_line.py +++ b/odoo_dynamic_dashboard/wizard/__init__.py @@ -19,15 +19,4 @@ # If not, see . # ############################################################################# -from odoo import fields, models - - -class DashboardBlockLine(models.Model): - """ Creates the model Dashboard Block Line""" - _name = "dashboard.block.line" - _description = "Dashboard Block Line" - - sequence = fields.Integer(string="Sequence", - help="Sequence of the block lines") - block_size = fields.Integer(string="Block size", - help="Block size of the dashboard block line") +from . import dashboard_mail diff --git a/odoo_dynamic_dashboard/wizard/dashboard_mail.py b/odoo_dynamic_dashboard/wizard/dashboard_mail.py new file mode 100644 index 000000000..abfb30579 --- /dev/null +++ b/odoo_dynamic_dashboard/wizard/dashboard_mail.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import fields, models + + +class DashboardMail(models.TransientModel): + _name = 'dashboard.mail' + _description = 'Dashboard Mail' + + user_ids = fields.Many2many('res.users', string="Users", + domain="[('id','!=', uid)]", + help="Select User") + base64code = fields.Char(string='Base 64', help='Base64 Code of the pdf') + + def send_mail(self): + """ + Function for sending mail to the selected users + """ + for user in self.user_ids: + mail_content = ( + 'Hi %s,
' + 'I hope this mail finds you well. I am pleased to share the Dashboard Report with you.
' + 'Please find the attachment
') % user.name + mail_values = { + 'subject': 'Dashboard Report', + 'author_id': self.env.user.partner_id.id, + 'body_html': mail_content, + 'email_to': user.email, + } + mail_id = self.env['mail.mail'].create(mail_values) + attachment_values = { + 'name': 'Dashboard.pdf', + 'datas': self.base64code, + 'type': 'binary', + 'res_model': 'mail.mail', + 'res_id': mail_id.id, + } + attachment_id = self.env['ir.attachment'].create(attachment_values) + mail_id.write({ + 'attachment_ids': [(4, attachment_id.id)] + }) + mail_id.send() + + return { + 'type': 'ir.actions.client', + 'tag': 'reload', + } + + def cancel_mail(self): + """ + Function for refreshing the page while clicking cancel + """ + return { + 'type': 'ir.actions.client', + 'tag': 'reload', + } diff --git a/odoo_dynamic_dashboard/wizard/dashboard_mail_views.xml b/odoo_dynamic_dashboard/wizard/dashboard_mail_views.xml new file mode 100644 index 000000000..f58b57d4e --- /dev/null +++ b/odoo_dynamic_dashboard/wizard/dashboard_mail_views.xml @@ -0,0 +1,22 @@ + + + + + dashboard.mail.view.form + dashboard.mail + +
+ + + + + +
+
+
+
\ No newline at end of file