Browse Source

Jul 15 : [ADD] Initial Commit 'advanced_dynamic_dashboard'

pull/266/head
Shijin V 2 years ago
parent
commit
783535668c
  1. 47
      advanced_dynamic_dashboard/README.rst
  2. 24
      advanced_dynamic_dashboard/__init__.py
  3. 65
      advanced_dynamic_dashboard/__manifest__.py
  4. 22
      advanced_dynamic_dashboard/controllers/__init__.py
  5. 44
      advanced_dynamic_dashboard/controllers/advanced_dynamic_dashboard.py
  6. 6
      advanced_dynamic_dashboard/doc/RELEASE_NOTES.md
  7. 24
      advanced_dynamic_dashboard/models/__init__.py
  8. 155
      advanced_dynamic_dashboard/models/dashboard_block.py
  9. 78
      advanced_dynamic_dashboard/models/dashboard_menu.py
  10. 68
      advanced_dynamic_dashboard/models/domain_to_sql.py
  11. 3
      advanced_dynamic_dashboard/security/ir.model.access.csv
  12. BIN
      advanced_dynamic_dashboard/static/description/assets/cybro-odoo.png
  13. BIN
      advanced_dynamic_dashboard/static/description/assets/hero.gif
  14. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/1.png
  15. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/2.png
  16. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/3.png
  17. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/4.png
  18. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/5.png
  19. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/6.png
  20. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/check.png
  21. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/chevron.png
  22. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/cogs.png
  23. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/consultation.png
  24. 1
      advanced_dynamic_dashboard/static/description/assets/icons/down.svg
  25. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/ecom-black.png
  26. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/education-black.png
  27. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/faq.png
  28. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/feature.png
  29. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/hotel-black.png
  30. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/license.png
  31. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/lifebuoy.png
  32. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/manufacturing-black.png
  33. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/pos-black.png
  34. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/puzzle.png
  35. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/restaurant-black.png
  36. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/screenshot.png
  37. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/service-black.png
  38. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/support.png
  39. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/test-2.png
  40. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/trading-black.png
  41. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/training.png
  42. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/update.png
  43. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/user.png
  44. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/video.png
  45. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/whatsapp.png
  46. BIN
      advanced_dynamic_dashboard/static/description/assets/icons/wrench.png
  47. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/1.png
  48. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/10.png
  49. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/11.png
  50. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/12.png
  51. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/13.png
  52. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/14.png
  53. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/15.png
  54. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/17.png
  55. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/18.png
  56. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/2.png
  57. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/3.png
  58. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/4.png
  59. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/5.png
  60. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/6.png
  61. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/8.png
  62. BIN
      advanced_dynamic_dashboard/static/description/assets/screenshots/9.png
  63. BIN
      advanced_dynamic_dashboard/static/description/banner.png
  64. BIN
      advanced_dynamic_dashboard/static/description/icon.png
  65. 1045
      advanced_dynamic_dashboard/static/description/index.html
  66. 1
      advanced_dynamic_dashboard/static/lib/css/gridstack.min.css
  67. 386
      advanced_dynamic_dashboard/static/src/css/dynamic_dashboard.css
  68. 510
      advanced_dynamic_dashboard/static/src/js/dynamic_dashboard.js
  69. 179
      advanced_dynamic_dashboard/static/src/scss/dynamic_dashboard.scss
  70. 196
      advanced_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml
  71. 62
      advanced_dynamic_dashboard/views/dashboard_menu_views.xml
  72. 12
      advanced_dynamic_dashboard/views/dashboard_views.xml
  73. 82
      advanced_dynamic_dashboard/views/dynamic_block_views.xml

47
advanced_dynamic_dashboard/README.rst

@ -0,0 +1,47 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://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.
Installation
============
- www.odoo.com/documentation/16.0/setup/install.html
- Install our custom addon
License
-------
GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (AGPL v3)
(https://www.odoo.com/documentation/user/16.0/legal/licenses/licenses.html)
Company
-------
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__
Credits
-------
* Developer:
(V16) Robin @Cybrosys, Afra MP @Cybrosys
Contacts
--------
* Mail Contact : odoo@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: `<static/description/index.html>`__

24
advanced_dynamic_dashboard/__init__.py

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Robin, Afra MP (odoo@cybrosys.com)
#
# 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 <http://www.gnu.org/licenses/>.
#
################################################################################
from . import controllers
from . import models

65
advanced_dynamic_dashboard/__manifest__.py

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Robin, Afra MP (odoo@cybrosys.com)
#
# 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 <http://www.gnu.org/licenses/>.
#
################################################################################
{
'name': "Advanced Dynamic Dashboard",
'version': '16.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',
'website': "https://www.cybrosys.com",
'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions',
'depends': ['base', 'web'],
'data': [
'security/ir.model.access.csv',
'views/dashboard_views.xml',
'views/dynamic_block_views.xml',
'views/dashboard_menu_views.xml',
],
'assets': {
'web.assets_backend': [
"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css",
'advanced_dynamic_dashboard/static/lib/css/gridstack.min.css',
'advanced_dynamic_dashboard/static/src/css/dynamic_dashboard.css',
'advanced_dynamic_dashboard/static/src/scss/dynamic_dashboard.scss',
"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.3/font/bootstrap-icons.css",
'https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/0.2.6/gridstack.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.js',
"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.min.js",
'https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js',
'advanced_dynamic_dashboard/static/src/js/dynamic_dashboard.js',
'advanced_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml',
'https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700'
],
},
'images': ['static/description/banner.png'],
'license': "AGPL-3",
'installable': True,
'auto_install': False,
'application': True,
}

22
advanced_dynamic_dashboard/controllers/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Robin, Afra MP (odoo@cybrosys.com)
#
# 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 <http://www.gnu.org/licenses/>.
#
################################################################################
from . import advanced_dynamic_dashboard

44
advanced_dynamic_dashboard/controllers/advanced_dynamic_dashboard.py

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Robin, Afra MP (odoo@cybrosys.com)
#
# 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 <http://www.gnu.org/licenses/>.
#
################################################################################
from odoo import http
from odoo.http import request
class DynamicDashboard(http.Controller):
"""Class to search and filter values in dashboard"""
@http.route('/tile/details', type='json', auth='user')
def tile_details(self, **kw):
"""Function to get tile details"""
tile_id = request.env['dashboard.block'].sudo().browse(int(kw.get('id')))
if tile_id:
return {'model': tile_id.model_id.model, 'filter': tile_id.filter,
'model_name': tile_id.model_id.name}
else:
return False
@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

6
advanced_dynamic_dashboard/doc/RELEASE_NOTES.md

@ -0,0 +1,6 @@
## Module <advanced_dynamic_dashboard>
#### 15.07.2023
#### Version 16.0.1.0.0
#### ADD
- Initial commit for Advanced Dynamic Dashboard

24
advanced_dynamic_dashboard/models/__init__.py

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Robin, Afra MP (odoo@cybrosys.com)
#
# 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 <http://www.gnu.org/licenses/>.
#
################################################################################
from . import dashboard_block
from . import dashboard_menu
from . import domain_to_sql

155
advanced_dynamic_dashboard/models/dashboard_block.py

@ -0,0 +1,155 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Robin, Afra MP (odoo@cybrosys.com)
#
# 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 <http://www.gnu.org/licenses/>.
#
################################################################################
from ast import literal_eval
from odoo import 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 Blocks"
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
else:
return False
name = fields.Char(string="Name", help='Name of the block')
fa_icon = fields.Char(string="Icon", help="Add icon for tile")
graph_size = fields.Selection(
selection=[("col-lg-4", "Small"), ("col-lg-6", "Medium"),
("col-lg-12", "Large")],
string="Graph Size", default='col-lg-4', help="Select the graph size")
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")
x_pos = fields.Integer(string="X-Position", help="Chart X-axis position")
y_pos = fields.Integer(string="X-Position", help="Chart Y-axis position")
height = fields.Integer(string="height", help="Chart height")
width = fields.Integer(string="width", help="Chart width")
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)
def get_dashboard_vals(self, action_id):
"""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))]):
vals = {'id': rec.id, 'name': rec.name, 'type': rec.type,
'graph_type': rec.graph_type, 'icon': rec.fa_icon,
'cols': rec.graph_size,
'color': 'background-color: %s;' % rec.tile_color if rec.tile_color else '#1f6abb;',
'text_color': 'color: %s;' % rec.text_color if rec.text_color else '#FFFFFF;',
'val_color': 'color: %s;' % rec.val_color if rec.val_color else '#FFFFFF;',
'icon_color': 'color: %s;' % rec.tile_color if rec.tile_color else '#1f6abb;',
'x_pos': rec.x_pos,'y_pos': rec.y_pos, 'height': rec.height,
'width': rec.width}
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,
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))
records = self._cr.dictfetchall()
magnitude = 0
total = records[0].get('value')
while abs(total) >= 1000:
magnitude += 1
total /= 1000.0
# add more suffixes if you need them
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, act_id, grid_data_list):
"""Function fetch edited values while edit layout of the chart or tile
and save values in a database"""
for block in self.env['dashboard.block'].sudo().search(
[('client_action_id', '=', int(act_id))]):
for data in grid_data_list:
if block['id'] == data['id']:
block.write({
'x_pos': int(data['x']),
'y_pos': int(data['y']),
'height': int(data['height']),
'width': int(data['width']),
})

78
advanced_dynamic_dashboard/models/dashboard_menu.py

@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Robin, Afra MP (odoo@cybrosys.com)
#
# 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 <http://www.gnu.org/licenses/>.
#
################################################################################
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")
@api.model
def create(self, vals):
"""Function to create new dashboard menu"""
action_id = self.env['ir.actions.client'].create({
'name': vals['name'],
'tag': 'advanced_dynamic_dashboard',
})
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()

68
advanced_dynamic_dashboard/models/domain_to_sql.py

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Robin, Afra MP (odoo@cybrosys.com)
#
# 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 <http://www.gnu.org/licenses/>.
#
################################################################################
from odoo import models
def get_query(self, args, operation, field, 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 'company_id' in self._fields:
if len(self.env.companies.ids) > 1:
operator = 'in'
company = str(tuple(self.env.companies.ids))
else:
operator = '='
company = self.env.companies.ids[0]
if where_str == '':
add = ' where'
else:
add = ' and'
multicompany_condition = '%s "%s".company_id %s %s' % (add, self._table, operator, company)
else:
multicompany_condition = ''
query_str = 'SELECT %s FROM ' % data + from_clause + join + where_str + multicompany_condition + group_by_str
return query_str % tuple(map(lambda x: "'" + str(x) + "'", where_clause_params))
models.BaseModel.get_query = get_query

3
advanced_dynamic_dashboard/security/ir.model.access.csv

@ -0,0 +1,3 @@
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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_dashboard_block access.dashboard.block model_dashboard_block base.group_user 1 1 1 1
3 access_dashboard_menu access.dashboard.menu model_dashboard_menu base.group_user 1 1 1 1

BIN
advanced_dynamic_dashboard/static/description/assets/cybro-odoo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/chevron.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/cogs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/consultation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

1
advanced_dynamic_dashboard/static/description/assets/icons/down.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="512" height="512" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><g id="Layer_2" data-name="Layer 2"><path d="m12 1a11 11 0 1 0 11 11 11.013 11.013 0 0 0 -11-11zm5.707 9.707-5 5a1 1 0 0 1 -1.414 0l-5-5a1 1 0 0 1 1.414-1.414l4.293 4.293 4.293-4.293a1 1 0 0 1 1.414 1.414z" fill="#781d96" data-original="#000000" class=""></path></g></g></svg>

After

Width:  |  Height:  |  Size: 542 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/ecom-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/education-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/faq.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/feature.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/hotel-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/license.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/lifebuoy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/manufacturing-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/pos-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/puzzle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/restaurant-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/service-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/test-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/trading-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/training.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/update.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

BIN
advanced_dynamic_dashboard/static/description/assets/icons/video.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/whatsapp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/icons/wrench.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/10.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/11.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/12.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/14.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/15.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/17.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/18.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
advanced_dynamic_dashboard/static/description/assets/screenshots/9.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

BIN
advanced_dynamic_dashboard/static/description/banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
advanced_dynamic_dashboard/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

1045
advanced_dynamic_dashboard/static/description/index.html

File diff suppressed because it is too large

1
advanced_dynamic_dashboard/static/lib/css/gridstack.min.css

File diff suppressed because one or more lines are too long

386
advanced_dynamic_dashboard/static/src/css/dynamic_dashboard.css

@ -0,0 +1,386 @@
.row {
margin: 1rem;
}
.o_dynamic_navbar {
margin: 1rem 0 1rem 0;
}
.card {
background-color: transparent !important;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 5px !important;
cursor: pointer;
transition: transform 0.2s;
}
.grid-stack-item {
position: absolute !important;
font-size: 100%;
overflow:hidden;
}
h2, h3 {
font-size: 150% !important;
}
.card:hover {
transform: scale(1.05);
}
.card-header {
border-radius: 0.5rem 0.5rem 0 0 !important;
}
.container {
max-width: 100% !important;
}
.tile-container {
padding: 0 0 0 10px;
border-radius: 0.5rem !important;
}
.tile {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 5px;
cursor: pointer;
transition: transform 0.2s;
}
.tile:hover {
transform: scale(1.1);
}
#search-input-chart {
margin-left: auto;
height: 2.4rem;
}
#searchclear {
position: absolute;
margin: 0.3rem 0 0 16.5rem;
}
.navbar-collapse {
margin-bottom: 8px !important;
}
div.card-header {
color: #383838;
background-color: #70659647 !important;
}
/* The toggle-btn - the box around the slider */
.layout-switch {
font-size: 15px;
position: absolute;
left: 10.5em;
display: inline-block;
padding: 0 0 0 0;
border-radius: 0.5rem;
}
.toggle-btn {
font-size: 15px;
position: absolute;
left: 18.2em;
display: inline-block;
padding: 4px 0 0 0;
border-radius: 0.5rem;
}
.theme-text {
font-family: Arial, sans-serif;
font-size: 1em;
opacity: 70%;
}
.search-group {
position: absolute;
right: 1.7em;
}
/*!* Hide default HTML checkbox *!*/
.toggle-btn input {
opacity: 0;
width: 0;
height: 0;
}
/*!* The slider *!*/
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.theme_icon {
font-weight: bold;
}
.theme_icon:hover {
text-shadow: 2px 1px 20px #795db3;
}
.block_setting {
position: absolute;
top: 7px;
left: 10px;
}
.block_delete {
position: absolute;
top: 7px;
right: 20px;
}
.export_option {
position: absolute;
top: 9px;
right: 10px;
font-size: 15px;
}
#ExportMenu::after {
display: none;
}
.tile_edit {
color: #d2d2d2;
}
.block_edit {
color: #3f3f3f;
}
.dropdown-export {
right: 0;
top: 1em;
}
.block_setting, .block_delete, .block_export {
display: none;
}
.grid-stack-item:hover .block_setting,
.grid-stack-item:hover .block_export,
.grid-stack-item:hover .block_delete {
display: block;
}
.chart-edit {
position: absolute;
top: 0px;
left: 3px;
font-size: 16px;
}
.chart-setting {
position: absolute;
top: 0px;
right: 3px;
font-size: 16px;
}
.chart_title {
padding-top: 0.7em;
text-align: center;
font-size: 16px;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.move_slider {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
margin: -1px 6px 11px 0;
}
.slider.round:before {
border-radius: 50%;
}
.o_kanban_record {
background-color: #e8e8e8 !important;
}
.oe_module_name {
background-color: #ffffff !important;
}
.bootbox .modal-footer .btn-danger:hover {
background-color: #ff595c;
}
.bootbox .modal-footer .btn-danger {
box-shadow: none;
margin-left: 0.5rem;
}
.bootbox .modal-header {
text-shadow: -1px 3px 5px #b4b4b4;
}
.bootbox .modal-body {
font-size: 14px;
text-shadow: -1px 3px 5px #b4b4b4;
color: #000000;
}
.dropdown-addblock, .o-dropdown-menu {
left: 2.5em;
top: 2.5em;
}
.navbar {
padding: 1.2rem 0 2.6rem 0 !important;
border-bottom: 1px solid #3f3f3f1a !important;
color: #444444;
background-color: #e1e2e26e !important;
border-bottom: 5px solid #eef0f0 !important;
}
.theme_icon:hover {
text-shadow: 0px 0px 5px #9388ff;
}
.navbar-style {
margin-top: 1.2em;
border-radius: 0.5em;
}
.grid-stack > .grid-stack-item > .grid-stack-item-content {
overflow-x: unset !important;
overflow-y: unset !important;
}
.dropdown-add-items {
position: absolute;
color: #e4e4e4;
left: 2em;
font-size: 16px;
text-transform: uppercase;
background-color: #71639e;
border: 1px solid transparent;
padding: 0.375rem 0.75rem;
font-size: 1.08333333rem;
border-radius: 0.25rem;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.dropdown-add-items:hover {
color: white;
background-color: #59507b;
}
@media (max-width: 767px) {
.navbar {
padding: 1.2rem 0 1.2rem 0;
}
.navbar:focus, .navbar:active {
padding: 1.2rem 0 2rem 0;
}
.search-group {
width: 50%;
position: absolute;
right: 3.7em;
}
.o_web_client.o_touch_device .btn, .o_web_client.o_touch_device .btn .btn-sm, .o_web_client.o_touch_device .btn .btn-group-sm > .btn {
font-size: 1.08333333rem;
padding: 6px 8px;
margin-top: 39px;
margin-left: -39px;
}
.grid-stack-item-content {
cursor: move;
width: 211px;
height: 63px;
}
/* !* styles for mobile devices *!*/
.grid-stack.grid-stack-one-column-mode > .grid-stack-item {
width: 100% !important;
}
.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;
}
#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;
}
}

510
advanced_dynamic_dashboard/static/src/js/dynamic_dashboard.js

@ -0,0 +1,510 @@
odoo.define('advanced_dynamic_dashboard.Dashboard', function (require) {
"use strict";
var AbstractAction = require('web.AbstractAction');
var ajax = require('web.ajax');
var core = require('web.core');
var rpc = require('web.rpc');
var QWeb = core.qweb;
var Dialog = require('web.Dialog');
var DynamicDashboard = AbstractAction.extend({
template: 'advanced_dynamic_dashboard',
events: {
'click .add_block': '_onClick_add_block',
'click .block_setting': '_onClick_block_setting',
'click .block_delete': '_onClick_block_delete',
'click #search-button': 'search_chart',
'click #searchclear': 'clear_search',
'click #dropdownNavbar': 'navbar_toggle',
'click #dropdownMenuButton': 'dropdown_toggle',
'click .chart_item_export': 'export_item',
'click #edit_layout': '_onClick_edit_layout',
'click #save_layout': '_onClick_save_layout',
'change #theme-toggle': 'switch_mode',
'mouseenter #theme-change-icon': 'show_mode_text',
'mouseleave #theme-change-icon': 'hide_mode_text',
'click .tile': '_onClick_tile',
},
init: function (parent, context) {//Function to Initializes all the values while loading the file
this.action_id = context['id'];
this._super(parent, context);
this.block_ids = [];
},
willStart: function () {//Returns the function fetch_data when page load.
var self = this;
return $.when(this._super()).then(function () {
return self.fetch_data();
});
},
start: function () {//Function return render_dashboards() and gridstack_init()
self = this;
this.set("title", 'Dashboard');
return this._super().then(function () {
self.render_dashboards();
self.gridstack_init(self);
});
},
fetch_data: function () {//Fetch data and call rpc query to create chart or tile. return block_ids
self = this;
var def1 = this._rpc({
model: 'dashboard.block',
method: 'get_dashboard_vals',
args: [[], this.action_id]
}).then(function (result) {
self.block_ids = result;
});
return $.when(def1);
},
show_mode_text: function () {//Function change text of dark and light mode while clicking the dark and light button.
this.$el.find('.theme_icon').next(this.el.querySelector('.theme-text')).remove();
if ( this.$el.find('#theme-toggle').is(':checked')) {//Set text "Light Mode"
this.$el.find('.theme_icon').after('<span style="color: #d6e7ff" class="theme-text">⠀Light Mode</span>');
} else {//Set text "Dark Mode"
this.$el.find('.theme_icon').after('<span style="color: #000000" class="theme-text">⠀Dark Mode</span>');
}
this.$el.find('.theme_icon').next(this.el.querySelector('.theme-text')).fadeIn();
},
hide_mode_text: function () {//While click button, hide the mode icon and text
this.$el.find('.theme_icon').next(this.el.querySelector('.theme-text')).fadeOut(function () {
$(this).remove();
});
},
switch_mode: function (ev) {//Function to change dashboard theme dark and light mode.
this.$el.find('.theme_icon').next('.theme-text').remove();
const isDarkTheme = this.$el.find('#theme-toggle').is(':checked');
$(this.el.parentElement).toggleClass('dark-theme', isDarkTheme);
this.$el.find('.theme_icon').toggleClass('bi-sun-fill', isDarkTheme);
this.$el.find('.theme_icon').toggleClass('bi-moon-stars-fill', !isDarkTheme);
this.$el.find('.dropdown-export').toggleClass('dropdown-menu-dark', isDarkTheme);
},
get_colors: function (x_axis) {//Function fetch random color values and set chart color
return x_axis.map(() => `rgb(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)})`);
},
get_values_bar: function (block) {//Set bar chart label, color, data and options. And return data and options
var data = {
labels: block.x_axis,
datasets: [{
data: block.y_axis,
backgroundColor: this.get_colors(block.x_axis),
borderColor: 'rgba(200, 200, 200, 0.75)',
borderWidth: 1
}]
};
var options = {
scales: {
y: {
beginAtZero: true
}
}
};
return [data, options];
},
get_values_pie: function (block) {//Set pie chart data and options. And return data and options.
var data = {
labels: block['x_axis'],
datasets: [{
label: '',
data: block['y_axis'],
backgroundColor: this.get_colors(block['x_axis']),
hoverOffset: 4
}]
};
return [data, {}];
},
get_values_line: function (block) {//Set line chart label, data and options. And return data and options.
var data = {
labels: block['x_axis'],
datasets: [{
label: '',
data: block['y_axis'],
fill: false,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
};
return [data, {}];
},
get_values_doughnut: function (block) {// Set doughnut chart data and options. And return data and options.
var data = {
labels: block['x_axis'],
datasets: [{
label: '',
data: block['y_axis'],
backgroundColor: this.get_colors(block['x_axis']),
hoverOffset: 4
}]
};
return [data, {}];
},
get_values_polarArea: function (block) {// Set polarArea chart data and options. And return data and options.
var data = {
labels: block['x_axis'],
datasets: [{
label: '',
data: block['y_axis'],
backgroundColor: this.get_colors(block['x_axis']),
hoverOffset: 4
}]
};
return [data, {}];
},
get_values_radar: function (block) {// Set radar chart data and options. And return data and options.
var data = {
labels: block['x_axis'],
datasets: [{
label: '',
data: block['y_axis'],
fill: true,
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgb(255, 99, 132)',
pointBackgroundColor: 'rgb(255, 99, 132)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgb(255, 99, 132)'
}]
};
var options = {
elements: {
line: {
borderWidth: 3
}
}
}
return [data, options];
},
gridstack_init: function (self) {// Used gridstack to drag and resize chart and tile.
self.$('.grid-stack').gridstack({
animate: true,
duration: 200,
handle: '.grid-stack-item-content',
draggable: {
handle: '.grid-stack-item-content',
scroll: true
},
resizable:{
aspectRatio:20/18,
},
alwaysShowResizeHandle: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
float: true
});
self.gridstack_off(self);
},
gridstack_on: function (self) {// Enable move and resize functionality
var gridstack = self.$('.grid-stack').data('gridstack');
gridstack.enableMove(true);
gridstack.enableResize(true);
},
gridstack_off: function (self) {// Disable move and resize functionality
var gridstack = self.$('.grid-stack').data('gridstack');
gridstack.enableMove(false);
gridstack.enableResize(false);
},
render_dashboards: function () {
self = this;
self.$("#save_layout").hide();//Hide save_layout button
_.each(this.block_ids, function (block) {//Loop all chart and tile
if (block['type'] == 'tile') {
self.$('.o_dynamic_dashboard').append(QWeb.render('DynamicDashboardTile', {widget: block}));
} else {//Block type = 'chart'
self.$('.o_dynamic_dashboard').append(QWeb.render('DynamicDashboardChart', {widget: block}));
if (!('x_axis' in block)) {
return false
}
var type = block['graph_type']
var chart_type = 'self.get_values_' + `${type}(block)`
new Chart(self.$('.chart_graphs').last(), {
type: type,
data: eval(chart_type)[0],
options: eval(chart_type)[1]
});
}
});
// Toggling dropdown for exporting, clicked item, closing all others
// When clicked on one, also when mouse leaves parent.
self.$(".block_export").on({
click: function () {//Show the export dropdown.
if ($(this).next(".dropdown-export").is(':visible')) {
$(this).next(".dropdown-export").hide();
} else {
$(this).next('.dropdown-export').hide();
$(this).next(".dropdown-export").show();
}
}
});
self.$(".grid-stack-item").on({//Function to hide dropdown-export list while mouse leave the block.
mouseleave: function () {
self.$('.dropdown-export').hide();
}
});
self.$(".dropdown-addblock").on({//Function to hide dropdown-addblock list if mouse leave dropdown list.
mouseleave: function () {
self.$(".dropdown-addblock").hide();
}
});
self.gridstack_init(self);
if (localStorage.getItem("toggleState") == 'true') {
self.$(".toggle").prop('checked', true)
$(self.el.parentElement).addClass('dark-theme');
self.$(".theme_icon").removeClass('bi-moon-stars-fill');
self.$(".theme_icon").addClass('bi-sun-fill');
self.$(".dropdown-export").addClass('dropdown-menu-dark');
} else {
$(self.el.parentElement).removeClass('dark-theme');
self.$(".theme_icon").removeClass('bi-sun-fill');
self.$(".theme_icon").addClass('bi-moon-stars-fill');
self.$(".dropdown-export").removeClass('dropdown-menu-dark');
}
},
navbar_toggle: function () {//Function to toggle the navbar.
this.$('.navbar-collapse').toggle();
},
export_item: function (e) {//Function to export chart into jpg, png or csv formate.
var type = $(e.currentTarget).attr('data-type');
var canvas = $(e.currentTarget).closest('.export_option').siblings('.row').find('#canvas')[0];
var dataTitle = canvas.getAttribute("data-title");
// Create a new canvas with a white background
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);
// Draw the chart onto the new canvas
bgCtx.drawImage(canvas, 0, 0);
// Export the new canvas as an image
var imgData = bgCanvas.toDataURL("image/png");
if (type === 'png') {
this.$el.find('.chart_png_export').attr({
href: imgData,
download: `${dataTitle}.png`
});
}
if (type === 'pdf') {
var pdf = new jsPDF();
pdf.addImage(bgCanvas.toDataURL("image/png"), 'PNG', 0, 0);
pdf.save(`${dataTitle}.pdf`);
}
if (type === 'csv') {
var rows = [];
// Check if the id inside the object is equal to this id
for (var obj of this.block_ids) {
if (obj.id == $(e.currentTarget).attr('data-id')) {
rows.push(obj.x_axis);
rows.push(obj.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); // Required for FF
link.click();
}
},
dropdown_toggle: function () {//Function to toggle the button Add Items.
this.$el.find('.dropdown-addblock').toggle();
},
on_reverse_breadcrumb: function () {//Function return all block in exact position.
self = this;
this.fetch_data().then(function () {//Fetch all datas
self.render_dashboards();
self.gridstack_init(self);
location.reload();
});
},
search_chart: function (e) {// Fetch search input value and filter the chart and tile.
e.stopPropagation()
self = this;
$(this).next("#theme-change-icon").hide();
this.$("#edit_layout").hide();
this.$("#save_layout").hide();
this.myDiv = this.$('.o_dynamic_dashboard');
this.$('.o_dynamic_dashboard').empty();
ajax.jsonRpc("/custom_dashboard/search_input_chart", 'call', {//Ajax call to get filtered data
'search_input': self.$("#search-input-chart").val()
}).then(function (res) {
_.each(self.block_ids, function (block) {
if (res.includes(block['id'])) {
if (block['type'] == 'tile') {
self.$('.o_dynamic_dashboard').append(QWeb.render('DynamicDashboardTile', {widget: block}));
} else {
self.$('.o_dynamic_dashboard').append(QWeb.render('DynamicDashboardChart', {widget: block}));
if (!('x_axis' in block)) {
return false
}
var chart_type = 'self.get_values_' + `${block['graph_type']}(block)`
new Chart(self.$('.chart_graphs').last(), {
type: block['graph_type'],
data: eval(chart_type)[0],
options: eval(chart_type)[1]
});
}
}
});
});
},
clear_search: function () {//Function to clear search box and call the functon on_reverse_breadcrumb().
self = this;
self.$("#search-input-chart").val("");
self.$("#theme-change-icon").show();
self.$("#edit_layout").show();
self.$("#save_layout").hide();
this.block_ids = [];
self.on_reverse_breadcrumb();
},
_onClick_block_setting: function (event) {//Function to edit blocks and redirect to the model dashboard.block
event.stopPropagation();
self = this;
this.do_action({
type: 'ir.actions.act_window',
res_model: 'dashboard.block',
view_mode: 'form',
res_id: parseInt($(event.currentTarget).closest('.block').attr('data-id')),
views: [[false, 'form']],
context: {'form_view_initial_mode': 'edit'},
}, {on_reverse_breadcrumb: self.on_reverse_breadcrumb})
},
_onClick_block_delete: function (event) {//While click on cross icon, the block will be deleted.
event.stopPropagation();
self = this;
bootbox.confirm({//Popup to conform delete
message: "Are you sure you want to delete this item?",
title: "Delete confirmation",
buttons: {
cancel: {
label: 'NO, GO BACK',
className: 'btn-primary'
},
confirm: {
label: 'YES, I\'M SURE',
className: 'btn-danger'
}
},
callback: function (result) {//Function to unlink block
if (result) {
rpc.query({
model: 'dashboard.block',
method: 'unlink',
args: [parseInt($(event.currentTarget).closest('.block').attr('data-id'))], // ID of the record to unlink
}).then(function (result) {
location.reload()
self.on_reverse_breadcrumb();
}).catch(function (error) {
console.log('Error unlinking record: ', error);
});
} else {
// Do nothing
}
}
});
},
_onClick_add_block: function (e) {//Fetch data and create chart or tile
self = this;
var type = $(e.currentTarget).attr('data-type');
if (type == 'graph') {
var chart_type = $(e.currentTarget).attr('data-chart_type');
}
if (type === 'tile') {
var randomColor = '#' + ('000000' + Math.floor(Math.random() * 16777216).toString(16)).slice(-6);
this.do_action({// Redirect to dashboard.block
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': 2,
'default_width': 2,
'default_tile_color': randomColor,
'default_text_color': '#FFFFFF',
'default_fa_icon': 'fa fa-bar-chart',
'default_client_action_id': parseInt(self.action_id)
},
on_close: function () {
window.location.reload();
}
});
} else {
this.do_action({
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': 5,
'default_width': 4,
'default_graph_type': chart_type,
'default_graph_size': 'col-lg-4',
'default_fa_icon': 'fa fa-bar-chart',
'default_client_action_id': parseInt(self.action_id)
},
on_close: function(){
window.location.reload();
}
});
}
// FETCHING SAVED LAYOUT FROM LOCAL STORAGE MEMORY
},
_onClick_edit_layout: function (e) {// Function to hide edit_layout button and show save_layout button. and also work the function gridstack_on(self)
e.stopPropagation();
self = this;
self.$("#edit_layout").hide();
self.$("#save_layout").show();
self.gridstack_on(self);
},
_onClick_save_layout: function (e) {//Function to save the edited value
e.stopPropagation();
self = this;
self.$("#edit_layout").show();
self.$("#save_layout").hide();
var grid_data_list = [];
this.$el.find('.grid-stack-item').each(function () {
grid_data_list.push({
'id': $(this).data('id'),
'x': $(this).data('gs-x'),
'y': $(this).data('gs-y'),
'width': $(this).data('gs-width'),
'height': $(this).data('gs-height')
})
});
this._rpc({
model: 'dashboard.block',
method: 'get_save_layout',
args: [[], this.action_id, grid_data_list]
});
self.gridstack_off(self);
},
_onClick_tile: function (e) {// Function to view the tree view of the tile.
e.stopPropagation();
self = this;
ajax.jsonRpc('/tile/details', 'call', {
'id': $(e.currentTarget).attr('data-id')
}).then(function (result) {
if (result['model_name']) {
self.do_action({
name: result['model_name'],
type: 'ir.actions.act_window',
res_model: result['model'],
view_mode: 'tree,form',
views: [[false, 'list'], [false, 'form']],
domain: result['filter']
});
} else {
Dialog.alert(this, "Configure the tile's model and parameters.");
}
});
},
});
core.action_registry.add('advanced_dynamic_dashboard', DynamicDashboard);
return DynamicDashboard;
});

179
advanced_dynamic_dashboard/static/src/scss/dynamic_dashboard.scss

@ -0,0 +1,179 @@
: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;
}
.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;
right: 1rem;
}
.dark-theme {
/* Add dark theme styles here */
.block_edit {
color: #b7b7b7 !important;
}
.navbar {
padding: 1.2rem 0 2.6rem 0 !important;
border-bottom: 1px solid #3f3f3f1a !important;
}
.theme_icon:hover {
text-shadow: 0px 0px 5px #9388ff;
}
.add_block{
color: #dfdfdf;
}
#ExportMenu {
color: #626262;
}
.dropdown-export {
background-color: #2a2a2a;
color: #dfdfdf;
}
.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;
}
}
}

196
advanced_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml

@ -0,0 +1,196 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<!--DASHBOARD VIEW WITH NAVIGATION-BAR, GRID_STACK TEMPLATE-->
<t t-name="advanced_dynamic_dashboard">
<div class="container mx-auto">
<div class="navbar navbar-expand-md navbar-light bg-light mb-4 navbar-style border-bottom"
role="navigation">
<button class="navbar-toggler" id="dropdownNavbar" type="button"
data-toggle="collapse"
data-target="#navbarCollapse"
aria-controls="navbarCollapse" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"/>
</button>
<div class="collapse navbar-collapse"
aria-labelledby="dropdownNavbar">
<ul class="navbar-nav mr-auto">
<label class="navbar-items dropdown drop-down-add">
<button class="btn dropdown-add-items dropdown-toggle"
type="button" id="dropdownMenuButton"
data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<i class="bi bi-plus-circle-fill"/>
<span>⠀Add Items</span>
</button>
<div class="dropdown-menu dropdown-addblock"
aria-labelledby="dropdownMenuButton">
<a class="dropdown-item add_block"
data-type="tile">Tile</a>
<a class="dropdown-item add_block"
data-type="graph" data-chart_type="bar">Bar Chart</a>
<a class="dropdown-item add_block"
data-type="graph" data-chart_type="doughnut">Doughnut Chart</a>
<a class="dropdown-item add_block"
data-type="graph"
data-chart_type="line">Line Chart</a>
<a class="dropdown-item add_block"
data-type="graph" data-chart_type="pie">Pie Chart</a>
<a class="dropdown-item add_block"
data-type="graph"
data-chart_type="polarArea">Polar Area Chart</a>
<a class="dropdown-item add_block"
data-type="graph"
data-chart_type="radar">Radar Chart</a>
</div>
</label>
<label class="navbar-items layout-switch"
id="edit-layout-label">
<button class="navbar-items btn-search_edit btn btn-primary my-2 mx-2 my-sm-0"
type="button"
id="edit_layout">Edit
Layout
</button>
<button class="navbar-items btn-search_edit btn btn-primary my-2 mx-2 my-sm-0"
type="button"
id="save_layout">Save Layout
</button>
</label>
<label class="navbar-items toggle-btn"
id="theme-change-icon">
<input type="checkbox" class="toggle"
id="theme-toggle"/>
<i class="theme_icon bi bi-moon-stars-fill"/>
</label>
<label class="search-group">
<div class="navbar-items btn-group search-box">
<input class="form-control mr-sm-2" type="text"
placeholder="Search"
id="search-input-chart"
aria-label="Search"/>
<span id="searchclear">
<i class='fa fa-times search-clear'/>
</span>
</div>
<button class="navbar-items btn-search_edit btn btn-outline-primary my-2 my-sm-0 search-btn"
type="button"
id="search-button">Search</button>
</label>
</ul>
</div>
</div>
<div class="o_dynamic_dashboard row m-2 grid-stack"
name="gridstack">
<!--CONTAINER FOR CONTENT GENERATION :TILE & CHART(FROM DynamicDashboardTile & DynamicDashboardChart-->
</div>
</div>
</t>
<t t-name="DynamicDashboardTile">
<!-- TILE BUILDING TEMPLATE-->
<div t-att-class="'grid-stack-item ' + widget.name"
t-att-data-gs-y="widget.y_pos"
t-att-data-gs-x="widget.x_pos" t-att-data-gs-width="widget.width"
t-att-data-gs-height="widget.height"
t-att-data-id="widget.id">
<div class="grid-stack-item-content tile block"
t-att-data-id="widget.id"
t-att-style="widget.color+widget.text_color">
<div t-att-style="widget.color+widget.text_color"
class="tile-container d-flex align-items-center w-100 my-3">
<a class="block_setting tile_edit tile-container__setting-icon">
<i class="fa fa-edit"/>
</a>
<a class="block_delete tile_edit tile-container__delete-icon">
<i class="fa fa-times"/>
</a>
<div>
</div>
<div t-att-style="widget.icon_color"
class="tile-container__icon-container bg-white d-flex justify-content-center align-items-center">
<i t-att-class="widget.icon"/>
</div>
<div t-att-style="widget.text_color"
class="tile-container__status-container">
<h2 t-att-style="widget.text_color"
class="status-container__title">
<t t-esc="widget.name"/>
</h2>
<div class="status-container__figures d-flex flex-wrap align-items-baseline">
<h3 class="mb-0 mb-md-1 mb-lg-0 mr-1"
t-att-style="widget.val_color">
<t t-esc="widget.value"/>
</h3>
</div>
</div>
</div>
</div>
</div>
</t>
<t t-name="DynamicDashboardChart">
<!-- CHART BUILDING TEMPLATE-->
<div t-att-class="'grid-stack-item ' + widget.name"
t-att-data-gs-x="widget.x_pos"
t-att-data-gs-y="widget.y_pos" t-att-data-gs-width="widget.width"
t-att-data-gs-height="widget.height"
t-att-data-id="widget.id">
<div class="grid-stack-item-content block card"
t-att-data-id="widget.id">
<div class="card-body mt-1" id="in_ex_body_hide">
<div class="block_edit block_setting">
<i title="Configuration"
class="fa fa-pencil block_setting chart-edit"/>
</div>
<div class="block_edit block_delete">
<i title="Delete"
class="fa fa-times block_delete chart-setting"/>
</div>
<div class="dropdown export_option">
<div class="block_edit fa fa-ellipsis-v block_export dropdown-toggle"
type="button"
id="ExportMenu" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
</div>
<div class="dropdown-menu dropdown-export"
aria-labelledby="ExportMenu">
<div class="m-2 chart_export_menu_header">
<span>Export</span>
</div>
<a class="dropdown-item chart_item_export chart_png_export"
data-type="png">
<i class="fa fa-file-image-o"/>
<span>Save as Image</span>
</a>
<button class="dropdown-item chart_pdf_export chart_item_export"
data-type="pdf">
<i class="fa fa-file-pdf-o"/>
<span>Save as PDF</span>
</button>
<button class="dropdown-item chart_csv_export chart_item_export"
data-type="csv"
t-att-data-id="widget.id">
<i class="fa fa-file-code-o"/>
<span>Export to CSV</span>
</button>
</div>
</div>
<h3 class="chart_title">
<t t-esc="widget.name"/>
</h3>
<div class="row">
<div class="col-md-12 chart_canvas" id="chart_canvas"
t-att-data-id="widget.id">
<canvas id="canvas" class="chart_graphs" width="300"
height="200"
t-att-data-title="widget.name"/>
</div>
</div>
</div>
</div>
</div>
</t>
</templates>

62
advanced_dynamic_dashboard/views/dashboard_menu_views.xml

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Action specified for the dashboard menu-->
<record id="dashboard_menu_view_action" model="ir.actions.act_window">
<field name="name">Dashboards Menu</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">dashboard.menu</field>
<field name="view_mode">kanban,form</field>
</record>
<!--Kanban view of the dashboard menu-->
<record id="dashboard_menu_view_kanban" model="ir.ui.view">
<field name="name">dashboard.menu.view.kanban</field>
<field name="model">dashboard.menu</field>
<field name="arch" type="xml">
<kanban>
<field name="name"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_global_click">
<h3 class="my-2 ms-3">
Name:
<field name="name"/>
</h3>
<div class="row">
<hr class="mt4 mb4"/>
<div class="col-6 text-center">
<strong>Parent:</strong>
</div>
<div class="col-6 text-center">
<field name="menu_id"/>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<!--Form view of the dashboard menu-->
<record id="dashboard_menu_view_form" model="ir.ui.view">
<field name="name">dashboard.menu.view.form</field>
<field name="model">dashboard.menu</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="name" class="oe_inline"/>
<field name="menu_id" class="oe_inline"/>
<field name="group_ids" widget="many2many_tags"
invisible="1"/>
<field name="client_action_id" invisible="1"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<menuitem name="Dashboards" id="dashboard_menu_view_menu_action"
parent="advanced_dynamic_dashboard.dashboard_menu_action"
sequence="0" action="dashboard_menu_view_action"/>
</odoo>

12
advanced_dynamic_dashboard/views/dashboard_views.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- The client action record to view the Dashboard-->
<record id="dashboard_view_action" model="ir.actions.client">
<field name="name">Dashboard</field>
<field name="tag">advanced_dynamic_dashboard</field>
</record>
<menuitem name="Dynamic Dashboards" action="dashboard_view_action"
id="dashboard_menu_action"
web_icon="advanced_dynamic_dashboard,static/description/icon.png"
sequence="-1"/>
</odoo>

82
advanced_dynamic_dashboard/views/dynamic_block_views.xml

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--Form view of the dashboard block-->
<record id="dashboard_block_view_form" model="ir.ui.view">
<field name="name">dashboard.block.view.form</field>
<field name="model">dashboard.block</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<div>
<field name="name" class="oe_inline"
style="font-size: 30px;"
placeholder="Block Name" required="1"/>
</div>
</group>
</group>
<group>
<group>
<field name="model_id"
attrs="{'required':[('edit_mode','=', True)]}"/>
<field name="client_action_id" invisible="1"/>
<field name="model_name" invisible="1"/>
<field name="edit_mode" invisible="1"/>
<field name="operation"
attrs="{'required':[('edit_mode','=', True)]}"/>
<field name="measured_field_id" required="1"
domain="[('model_id','=',model_id), ('ttype','in',['float','integer','monetary']), ('store', '=', True)]"
attrs="{'required':[('edit_mode','=', True)]}"/>
<field name="filter" widget="domain"
options="{'model': 'model_name'}"/>
</group>
</group>
<group string="Block Information">
<group>
<field name="type" required="1"/>
<field name="graph_type"
attrs="{'invisible': [('type','not in', 'graph')]}"/>
<field name="graph_size"
attrs="{'invisible': [('type','not in', 'graph')]}"/>
<field name="fa_icon"
attrs="{'invisible': [('type','not in', 'tile')]}"/>
<field name="group_by_id"
attrs="{'invisible': [('type','not in', 'graph')], 'required':[('edit_mode','=', True),('type','=','graph')]}"
domain="[('model_id','=',model_id), ('ttype','!=','one2many'), ('store', '=', True)]"/>
<field name="tile_color"
attrs="{'invisible': [('type','not in', 'tile')]}"
widget="color"/>
<field name="val_color"
attrs="{'invisible': [('type','not in', 'tile')]}"
widget="color"/>
<field name="text_color"
attrs="{'invisible': [('type','not in', 'tile')]}"
widget="color"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<!--Tree view of the dashboard block-->
<record id="dashboard_block_view_tree" model="ir.ui.view">
<field name="name">dashboard.block.view.tree</field>
<field name="model">dashboard.block</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="model_id"/>
<field name="type"/>
</tree>
</field>
</record>
<!-- Action specified for the dashboard block-->
<record id="dashboard_block_action" model="ir.actions.act_window">
<field name="name">Dashboard Block</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">dashboard.block</field>
<field name="view_mode">tree,form</field>
<field name="context">{'default_edit_mode' : True}</field>
</record>
</odoo>
Loading…
Cancel
Save