diff --git a/advanced_dynamic_dashboard/README.rst b/advanced_dynamic_dashboard/README.rst new file mode 100644 index 000000000..a7cfa2f7a --- /dev/null +++ b/advanced_dynamic_dashboard/README.rst @@ -0,0 +1,46 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg + :target: https://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +Advanced 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 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 `__ + +Credits +------- +* Developers: (V17) Arjun S, + (V16) Robin, Afra MP +* Contact: odoo@cybrosys.com + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com +* Website : http://www.cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +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 https://www.cybrosys.com + +Further information +=================== +HTML Description: ``__ diff --git a/advanced_dynamic_dashboard/__init__.py b/advanced_dynamic_dashboard/__init__.py new file mode 100644 index 000000000..027e09ba9 --- /dev/null +++ b/advanced_dynamic_dashboard/__init__.py @@ -0,0 +1,24 @@ +# -*- 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 . import controllers +from . import models +from . import wizard diff --git a/advanced_dynamic_dashboard/__manifest__.py b/advanced_dynamic_dashboard/__manifest__.py new file mode 100644 index 000000000..4733f0462 --- /dev/null +++ b/advanced_dynamic_dashboard/__manifest__.py @@ -0,0 +1,62 @@ +# -*- 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 . +# +############################################################################# +{ + 'name': "Advanced Dynamic Dashboard", + 'version': '17.0.1.0.0', + 'category': 'Productivity', + 'summary': """Create Configurable Dashboards Easily""", + 'description': """Create Configurable Advanced 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", + 'depends': ['web'], + 'data': [ + 'security/ir.model.access.csv', + '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': [ + 'advanced_dynamic_dashboard/static/src/css/**/*.css', + 'advanced_dynamic_dashboard/static/src/scss/**/*.scss', + 'advanced_dynamic_dashboard/static/src/js/**/*.js', + 'advanced_dynamic_dashboard/static/src/xml/**/*.xml', + 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css', + 'advanced_dynamic_dashboard/static/lib/js/interactjs.js', + ], + }, + 'images': ['static/description/banner.jpg'], + 'license': "AGPL-3", + 'installable': True, + 'auto_install': False, + 'application': True, +} + + + diff --git a/advanced_dynamic_dashboard/controllers/__init__.py b/advanced_dynamic_dashboard/controllers/__init__.py new file mode 100644 index 000000000..eeb473ce9 --- /dev/null +++ b/advanced_dynamic_dashboard/controllers/__init__.py @@ -0,0 +1,22 @@ +# -*- 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 . import advanced_dynamic_dashboard diff --git a/advanced_dynamic_dashboard/controllers/advanced_dynamic_dashboard.py b/advanced_dynamic_dashboard/controllers/advanced_dynamic_dashboard.py new file mode 100644 index 000000000..93ee85789 --- /dev/null +++ b/advanced_dynamic_dashboard/controllers/advanced_dynamic_dashboard.py @@ -0,0 +1,34 @@ +# -*- 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 http +from odoo.http import request + + +class DynamicDashboard(http.Controller): + """Class to search and filter values in dashboard""" + + @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/advanced_dynamic_dashboard/data/dashboard_theme_data.xml b/advanced_dynamic_dashboard/data/dashboard_theme_data.xml new file mode 100644 index 000000000..29c052ae3 --- /dev/null +++ b/advanced_dynamic_dashboard/data/dashboard_theme_data.xml @@ -0,0 +1,12 @@ + + + + + + Demo + #4158D0 + #C850C0 + #FFCC70 + + + \ No newline at end of file diff --git a/advanced_dynamic_dashboard/doc/RELEASE_NOTES.md b/advanced_dynamic_dashboard/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..1633dfcbd --- /dev/null +++ b/advanced_dynamic_dashboard/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 18.05.2024 +#### Version 17.0.1.0.0 +##### ADD +- Initial commit for Advanced Dynamic Dashboard \ No newline at end of file diff --git a/advanced_dynamic_dashboard/models/__init__.py b/advanced_dynamic_dashboard/models/__init__.py new file mode 100644 index 000000000..33f213fd2 --- /dev/null +++ b/advanced_dynamic_dashboard/models/__init__.py @@ -0,0 +1,25 @@ +# -*- 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 . import dashboard_block +from . import dashboard_menu +from . import dashboard_theme +from . import domain_to_sql diff --git a/advanced_dynamic_dashboard/models/dashboard_block.py b/advanced_dynamic_dashboard/models/dashboard_block.py new file mode 100644 index 000000000..b16f86187 --- /dev/null +++ b/advanced_dynamic_dashboard/models/dashboard_block.py @@ -0,0 +1,182 @@ +# -*- 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 ast import literal_eval +from odoo import api, fields, models +from odoo.osv import expression + + +class DashboardBlock(models.Model): + """Class is used to create charts and tiles in dashboard""" + _name = "dashboard.block" + _description = "Dashboard Block" + + def get_default_action(self): + """Function to get values from dashboard if action_id is true return + id else return false""" + action_id = self.env.ref( + 'advanced_dynamic_dashboard.dashboard_view_action') + if action_id: + return action_id.id + return False + + name = fields.Char(string="Name", help='Name of the block') + 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', + required=True) + graph_type = fields.Selection( + selection=[("bar", "Bar"), ("radar", "Radar"), ("pie", "Pie"), + ("polarArea", "polarArea"), ("line", "Line"), + ("doughnut", "Doughnut")], + string="Chart Type", help='Type of Chart') + 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="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="Add filter") + model_id = fields.Many2one('ir.model', string='Model', + 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) + + @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_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')] + 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': + 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: + 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: + 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 = '%.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/advanced_dynamic_dashboard/models/dashboard_menu.py b/advanced_dynamic_dashboard/models/dashboard_menu.py new file mode 100644 index 000000000..21ae2e3d8 --- /dev/null +++ b/advanced_dynamic_dashboard/models/dashboard_menu.py @@ -0,0 +1,80 @@ +# -*- 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 DashboardMenu(models.Model): + """Class to create new dashboard menu""" + _name = "dashboard.menu" + _description = "Dashboard Menu" + + 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='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 of the " + "corresponding dashboard menu") + + @api.model + def create(self, vals): + """Function to create new dashboard menu""" + action_id = self.env['ir.actions.client'].create({ + 'name': vals['name'], + 'tag': 'AdvancedDynamicDashboard', + }) + vals['client_action_id'] = action_id.id + self.env['ir.ui.menu'].create({ + 'name': vals['name'], + 'parent_id': vals['menu_id'], + 'action': 'ir.actions.client,%d' % (action_id.id,) + }) + return super(DashboardMenu, self).create(vals) + + def write(self, 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): + """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/advanced_dynamic_dashboard/models/dashboard_theme.py b/advanced_dynamic_dashboard/models/dashboard_theme.py new file mode 100644 index 000000000..1f1787bff --- /dev/null +++ b/advanced_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/advanced_dynamic_dashboard/models/domain_to_sql.py b/advanced_dynamic_dashboard/models/domain_to_sql.py new file mode 100644 index 000000000..f0de3a4df --- /dev/null +++ b/advanced_dynamic_dashboard/models/domain_to_sql.py @@ -0,0 +1,74 @@ +# -*- 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 models + + +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) + 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) + rec_name = self.env[group_by.relation]._rec_name_fallback() + 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)) + 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 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: + 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 + + +models.BaseModel.get_query = get_query diff --git a/advanced_dynamic_dashboard/security/ir.model.access.csv b/advanced_dynamic_dashboard/security/ir.model.access.csv new file mode 100644 index 000000000..fcade0113 --- /dev/null +++ b/advanced_dynamic_dashboard/security/ir.model.access.csv @@ -0,0 +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_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/advanced_dynamic_dashboard/static/description/assets/cybro-icon.png b/advanced_dynamic_dashboard/static/description/assets/cybro-icon.png new file mode 100644 index 000000000..06e73e11d Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/cybro-icon.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/cybro-odoo.png b/advanced_dynamic_dashboard/static/description/assets/cybro-odoo.png new file mode 100644 index 000000000..ed02e07a4 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/cybro-odoo.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/h2.png b/advanced_dynamic_dashboard/static/description/assets/h2.png new file mode 100644 index 000000000..0bfc4707d Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/h2.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/check.png b/advanced_dynamic_dashboard/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/check.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/chevron.png b/advanced_dynamic_dashboard/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/chevron.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/cogs.png b/advanced_dynamic_dashboard/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/cogs.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/consultation.png b/advanced_dynamic_dashboard/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/consultation.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/down.svg b/advanced_dynamic_dashboard/static/description/assets/icons/down.svg new file mode 100644 index 000000000..f21c36271 --- /dev/null +++ b/advanced_dynamic_dashboard/static/description/assets/icons/down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/ecom-black.png b/advanced_dynamic_dashboard/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/ecom-black.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/education-black.png b/advanced_dynamic_dashboard/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/education-black.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/faq.png b/advanced_dynamic_dashboard/static/description/assets/icons/faq.png new file mode 100644 index 000000000..4250b5b81 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/faq.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/feature.png b/advanced_dynamic_dashboard/static/description/assets/icons/feature.png new file mode 100644 index 000000000..ac7a785c0 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/feature.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/hotel-black.png b/advanced_dynamic_dashboard/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/hotel-black.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/license.png b/advanced_dynamic_dashboard/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/license.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/lifebuoy.png b/advanced_dynamic_dashboard/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/lifebuoy.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/manufacturing-black.png b/advanced_dynamic_dashboard/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/manufacturing-black.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/notes.png b/advanced_dynamic_dashboard/static/description/assets/icons/notes.png new file mode 100644 index 000000000..ee5e95404 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/notes.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/pos-black.png b/advanced_dynamic_dashboard/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/pos-black.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/puzzle.png b/advanced_dynamic_dashboard/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/puzzle.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/restaurant-black.png b/advanced_dynamic_dashboard/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/restaurant-black.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/screenshot.png b/advanced_dynamic_dashboard/static/description/assets/icons/screenshot.png new file mode 100644 index 000000000..cef272529 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/screenshot.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/service-black.png b/advanced_dynamic_dashboard/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/service-black.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/skype.png b/advanced_dynamic_dashboard/static/description/assets/icons/skype.png new file mode 100644 index 000000000..51b409fb3 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/skype.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/star-1.svg b/advanced_dynamic_dashboard/static/description/assets/icons/star-1.svg new file mode 100644 index 000000000..7e55ab162 --- /dev/null +++ b/advanced_dynamic_dashboard/static/description/assets/icons/star-1.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/star-2.svg b/advanced_dynamic_dashboard/static/description/assets/icons/star-2.svg new file mode 100644 index 000000000..5ae9f507a --- /dev/null +++ b/advanced_dynamic_dashboard/static/description/assets/icons/star-2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/support.png b/advanced_dynamic_dashboard/static/description/assets/icons/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/support.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/test-1 - Copy.png b/advanced_dynamic_dashboard/static/description/assets/icons/test-1 - Copy.png new file mode 100644 index 000000000..f6a902663 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/test-1 - Copy.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/test-1.png b/advanced_dynamic_dashboard/static/description/assets/icons/test-1.png new file mode 100644 index 000000000..0908add2b Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/test-1.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/test-2.png b/advanced_dynamic_dashboard/static/description/assets/icons/test-2.png new file mode 100644 index 000000000..4671fe91e Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/test-2.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/trading-black.png b/advanced_dynamic_dashboard/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/trading-black.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/training.png b/advanced_dynamic_dashboard/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/training.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/update.png b/advanced_dynamic_dashboard/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/update.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/user.png b/advanced_dynamic_dashboard/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/user.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/video.png b/advanced_dynamic_dashboard/static/description/assets/icons/video.png new file mode 100644 index 000000000..576705b17 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/video.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/whatsapp.png b/advanced_dynamic_dashboard/static/description/assets/icons/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/whatsapp.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/icons/wrench.png b/advanced_dynamic_dashboard/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/icons/wrench.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/modules/1.gif b/advanced_dynamic_dashboard/static/description/assets/modules/1.gif new file mode 100644 index 000000000..ae3a880a2 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/modules/1.gif differ diff --git a/advanced_dynamic_dashboard/static/description/assets/modules/2.jpg b/advanced_dynamic_dashboard/static/description/assets/modules/2.jpg new file mode 100644 index 000000000..a1dc39c89 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/modules/2.jpg differ diff --git a/advanced_dynamic_dashboard/static/description/assets/modules/3.png b/advanced_dynamic_dashboard/static/description/assets/modules/3.png new file mode 100644 index 000000000..8513873ea Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/modules/3.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/modules/4.png b/advanced_dynamic_dashboard/static/description/assets/modules/4.png new file mode 100644 index 000000000..3bedf7981 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/modules/4.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/modules/5.png b/advanced_dynamic_dashboard/static/description/assets/modules/5.png new file mode 100644 index 000000000..0e311ca87 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/modules/5.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/modules/6.jpg b/advanced_dynamic_dashboard/static/description/assets/modules/6.jpg new file mode 100644 index 000000000..67c7f7062 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/modules/6.jpg differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/1.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..3a464825c Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/1.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/10.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/10.png new file mode 100644 index 000000000..613bde7a0 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/10.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/11.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/11.png new file mode 100644 index 000000000..205f94be9 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/11.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/12.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/12.png new file mode 100644 index 000000000..cab8db9c5 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/12.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/13.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/13.png new file mode 100644 index 000000000..ec9aa1a73 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/13.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/14.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/14.png new file mode 100644 index 000000000..81d2d5576 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/14.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/15.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/15.png new file mode 100644 index 000000000..40486af70 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/15.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/16.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/16.png new file mode 100644 index 000000000..daf765693 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/16.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/17.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/17.png new file mode 100644 index 000000000..5e2731302 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/17.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/18.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/18.png new file mode 100644 index 000000000..d08bbeb79 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/18.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/19.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/19.png new file mode 100644 index 000000000..e8a7442d6 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/19.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/2.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..477d20d61 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/2.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/3.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..5bf5777dc Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/3.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/4.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..cf1687e38 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/4.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/5.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..197c8586c Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/5.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/6.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..b6c767a8a Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/6.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/7.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..ae546d9df Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/7.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/8.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..2de8dc893 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/8.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/9.png b/advanced_dynamic_dashboard/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..e3d23314c Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/9.png differ diff --git a/advanced_dynamic_dashboard/static/description/assets/screenshots/hero.gif b/advanced_dynamic_dashboard/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..9975087f6 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/screenshots/hero.gif differ diff --git a/advanced_dynamic_dashboard/static/description/assets/y18.jpg b/advanced_dynamic_dashboard/static/description/assets/y18.jpg new file mode 100644 index 000000000..eea1714f2 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/assets/y18.jpg differ diff --git a/advanced_dynamic_dashboard/static/description/banner.jpg b/advanced_dynamic_dashboard/static/description/banner.jpg new file mode 100644 index 000000000..6b7e5c522 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/banner.jpg differ diff --git a/advanced_dynamic_dashboard/static/description/icon.png b/advanced_dynamic_dashboard/static/description/icon.png new file mode 100644 index 000000000..8c1cfd096 Binary files /dev/null and b/advanced_dynamic_dashboard/static/description/icon.png differ diff --git a/advanced_dynamic_dashboard/static/description/index.html b/advanced_dynamic_dashboard/static/description/index.html new file mode 100644 index 000000000..f2c40c648 --- /dev/null +++ b/advanced_dynamic_dashboard/static/description/index.html @@ -0,0 +1,1129 @@ + + + + + + + + Document + + + + + + + + +
+ +
+
+

+ Advanced Dynamic Dashboard +

+

+ This Module Helps To Create Configurable Dashboards Easily.

+
+
+

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

+
+ +
+ +
+ + + +
+
+ +
+ + + +
+
+

+ KEY HIGHLIGHTS +

+
+
+
+
+ +
+
+

+ Easily Create Dynamic Charts And Tiles

+
+
+
+
+
+
+ +
+
+

+ Create A Dynamic Dashboard Menu In Any Model

+
+
+
+
+
+
+ +
+
+

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

+
+
+
+
+
+
+ +
+
+

+ Edit And Configure Charts And Tiles

+
+
+
+
+
+
+ + + +
+
+ +
+

+ Overview + +

+

+ Advanced Dynamic Dashboard

+
+ +
+
+ This module helps to create configurable dashboards easily. + Advanced 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. +
+
+
+
+ + +
+ + + +
+
+ +
+ +
+ +
+ +
+
+ +
+

+ Screenshots + +

+

+ Advanced Dynamic Dashboard

+
+ +
+

+ Advanced Dynamic Dashboard Menu +

+
+ +
+
+ +
+

+ Export And Delete Features +

+

+ 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 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. +

+
+ +
+
+ +
+

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

+
+ +
+
+ +
+

+ Change The Mode +

+

+ Dark mode +

+
+ +
+
+ +
+

+ 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. +

+
+ +
+
+ +
+

+ Dashboard Theme +

+

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

+
+ +
+
+ +
+
+ +
+

+ Print PDF +

+

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

+
+ +
+
+ +
+
+ +
+

+ 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

+
+ +
+ +
+
+
+
+ + + +
+ Edit And Configure Charts And Tiles. +
+
+
+
+
+ +
+
+
+
+ + + +
+ Easily Create Dynamic Charts And Tiles. +
+
+
+
+
+ +
+
+
+
+ + + +
+ Available Dark Mode And Light Mode. +
+
+
+
+
+ +
+
+
+
+ + + +
+ Create A Advanced Dynamic Dashboard Menu In Any Model. +
+
+
+
+
+ +
+
+
+
+ + + +
+ Charts Can Export Into Image, PDF,XLSX And CSV. +
+
+
+
+
+ +
+
+
+
+ + + +
+ Drag And Resize The Chart And Tile. +
+
+
+
+
+ +
+
+
+
+ +
+
+
+

+ RELEASE NOTES + +

+
+
+
+
+ +
+

Version 17.0.1 I Released on : 18th May 2024 +

+

Initial commit for + advanced_dynamic_dashboard

+
+ +
+
+
+
+ + +
+ +
+
+ + + + +
+
+
+

Related Modules

+

Explore our related modules

+
+
+
+ +
+
+ +
+ +
+ +
+
+ +
+

+ Our Services +

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

Our Industries

+
+
+
+
+ +
+ Trading +
+

+ Easily procure + and + sell your products

+
+
+
+
+ +
+ POS +
+

+ Easy + configuration + and convivial experience

+
+
+
+
+ +
+ Education +
+

+ A platform for + educational management

+
+
+
+
+ +
+ Manufacturing +
+

+ Plan, track and + schedule your operations

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

+ Mobile + friendly, + awe-inspiring product pages

+
+
+
+
+ +
+ Service Management +
+

+ Keep track of + services and invoice

+
+
+
+
+ +
+ Restaurant +
+

+ Run your bar or + restaurant methodically

+
+
+
+
+ +
+ 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

+
+
+
+
+
+
+
+ +
+
+

WhatsApp

+

Say hi to us on + WhatsApp!

+ +

+91 + 86068 + 27707

+
+
+
+
+ + +
+
+
+ +
+
+

Skype

+

Say hi to us on Skype!

+ +

cybroopenerp

+
+
+
+
+ + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + + + + + \ No newline at end of file diff --git a/advanced_dynamic_dashboard/static/lib/js/interactjs.js b/advanced_dynamic_dashboard/static/lib/js/interactjs.js new file mode 100644 index 000000000..b57c73bdb --- /dev/null +++ b/advanced_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/advanced_dynamic_dashboard/static/src/js/dynamic_dashboard.js b/advanced_dynamic_dashboard/static/src/js/dynamic_dashboard.js new file mode 100644 index 000000000..844852901 --- /dev/null +++ b/advanced_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 AdvancedDynamicDashboard extends Component { + // Setup function to run when the template of the class AdvancedDynamicDashboard 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, + } + }) + } +} +AdvancedDynamicDashboard.template = "owl.AdvancedDynamicDashboard" +registry.category("actions").add("AdvancedDynamicDashboard", AdvancedDynamicDashboard) diff --git a/advanced_dynamic_dashboard/static/src/js/dynamic_dashboard_chart.js b/advanced_dynamic_dashboard/static/src/js/dynamic_dashboard_chart.js new file mode 100644 index 000000000..da150776e --- /dev/null +++ b/advanced_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/advanced_dynamic_dashboard/static/src/js/dynamic_dashboard_tile.js b/advanced_dynamic_dashboard/static/src/js/dynamic_dashboard_tile.js new file mode 100644 index 000000000..8cfd238f0 --- /dev/null +++ b/advanced_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/advanced_dynamic_dashboard/static/src/scss/dynamic_dashboard.scss b/advanced_dynamic_dashboard/static/src/scss/dynamic_dashboard.scss new file mode 100644 index 000000000..051245037 --- /dev/null +++ b/advanced_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/advanced_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml b/advanced_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml new file mode 100644 index 000000000..678f3bfa7 --- /dev/null +++ b/advanced_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml @@ -0,0 +1,129 @@ + + + + +
+ +
+
+
+ +
+ + diff --git a/advanced_dynamic_dashboard/views/dashboard_menu_views.xml b/advanced_dynamic_dashboard/views/dashboard_menu_views.xml new file mode 100644 index 000000000..ebd5f942b --- /dev/null +++ b/advanced_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/advanced_dynamic_dashboard/views/dashboard_theme_views.xml b/advanced_dynamic_dashboard/views/dashboard_theme_views.xml new file mode 100644 index 000000000..99668b8d3 --- /dev/null +++ b/advanced_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/advanced_dynamic_dashboard/views/dashboard_views.xml b/advanced_dynamic_dashboard/views/dashboard_views.xml new file mode 100644 index 000000000..ff8740917 --- /dev/null +++ b/advanced_dynamic_dashboard/views/dashboard_views.xml @@ -0,0 +1,14 @@ + + + + + Dashboard + AdvancedDynamicDashboard + + + + diff --git a/advanced_dynamic_dashboard/views/dynamic_block_views.xml b/advanced_dynamic_dashboard/views/dynamic_block_views.xml new file mode 100644 index 000000000..b3a4d8e39 --- /dev/null +++ b/advanced_dynamic_dashboard/views/dynamic_block_views.xml @@ -0,0 +1,87 @@ + + + + + dashboard.block.view.form + dashboard.block + +
+ + + +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + dashboard.block.view.tree + dashboard.block + + + + + + + + + + + Dashboard Block + ir.actions.act_window + dashboard.block + tree,form + {'default_edit_mode' : True} + + + +
diff --git a/advanced_dynamic_dashboard/wizard/__init__.py b/advanced_dynamic_dashboard/wizard/__init__.py new file mode 100644 index 000000000..9a996c029 --- /dev/null +++ b/advanced_dynamic_dashboard/wizard/__init__.py @@ -0,0 +1,22 @@ +# -*- 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 . import dashboard_mail diff --git a/advanced_dynamic_dashboard/wizard/dashboard_mail.py b/advanced_dynamic_dashboard/wizard/dashboard_mail.py new file mode 100644 index 000000000..abfb30579 --- /dev/null +++ b/advanced_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/advanced_dynamic_dashboard/wizard/dashboard_mail_views.xml b/advanced_dynamic_dashboard/wizard/dashboard_mail_views.xml new file mode 100644 index 000000000..f58b57d4e --- /dev/null +++ b/advanced_dynamic_dashboard/wizard/dashboard_mail_views.xml @@ -0,0 +1,22 @@ + + + + + dashboard.mail.view.form + dashboard.mail + +
+ + + + + +
+
+
+
\ No newline at end of file