Browse Source

July 22: [FIX] Bug Fixed 'odoo_dynamic_dashboard'

pull/331/head
RisvanaCybro 9 months ago
parent
commit
98867a750b
  1. 25
      odoo_dynamic_dashboard/README.rst
  2. 3
      odoo_dynamic_dashboard/__init__.py
  3. 34
      odoo_dynamic_dashboard/__manifest__.py
  4. 41
      odoo_dynamic_dashboard/controllers/odoo_dynamic_dashboard.py
  5. 12
      odoo_dynamic_dashboard/data/dashboard_theme_data.xml
  6. 12
      odoo_dynamic_dashboard/doc/RELEASE_NOTES.md
  7. 2
      odoo_dynamic_dashboard/models/__init__.py
  8. 174
      odoo_dynamic_dashboard/models/dashboard_block.py
  9. 93
      odoo_dynamic_dashboard/models/dashboard_menu.py
  10. 53
      odoo_dynamic_dashboard/models/dashboard_theme.py
  11. 43
      odoo_dynamic_dashboard/models/domain_to_sql.py
  12. 3
      odoo_dynamic_dashboard/security/ir.model.access.csv
  13. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/1.png
  14. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/10.png
  15. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/11.png
  16. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/2.png
  17. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/3.png
  18. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/4.png
  19. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/5.png
  20. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/6.png
  21. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/7.png
  22. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/8.png
  23. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/9.png
  24. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/Tiles.png
  25. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/additems.png
  26. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/blocks.png
  27. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/charts.png
  28. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/darkmode.png
  29. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/dashboard_create.png
  30. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/delete.png
  31. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/editlayout.png
  32. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/email.png
  33. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/email_report.png
  34. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/hero.gif
  35. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/mainpage.png
  36. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/newbar.png
  37. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/newtile.png
  38. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/pdf.png
  39. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/pdf_and_mail.png
  40. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/redirect.png
  41. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/sales_dashboard.png
  42. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/savetypes.png
  43. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/themes.png
  44. BIN
      odoo_dynamic_dashboard/static/description/assets/screenshots/themeselect.png
  45. 471
      odoo_dynamic_dashboard/static/description/index.html
  46. 4
      odoo_dynamic_dashboard/static/lib/js/interactjs.js
  47. 344
      odoo_dynamic_dashboard/static/src/css/dynamic_dashboard.css
  48. 54
      odoo_dynamic_dashboard/static/src/js/DynamicDashboard.js
  49. 82
      odoo_dynamic_dashboard/static/src/js/DynamicDashboardChart.js
  50. 52
      odoo_dynamic_dashboard/static/src/js/DynamicDashboardTile.js
  51. 391
      odoo_dynamic_dashboard/static/src/js/dynamic_dashboard.js
  52. 215
      odoo_dynamic_dashboard/static/src/js/dynamic_dashboard_chart.js
  53. 89
      odoo_dynamic_dashboard/static/src/js/dynamic_dashboard_tile.js
  54. 208
      odoo_dynamic_dashboard/static/src/scss/dynamic_dashboard.scss
  55. 109
      odoo_dynamic_dashboard/static/src/scss/style.scss
  56. 128
      odoo_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml
  57. 46
      odoo_dynamic_dashboard/views/dashboard_menu_view.xml
  58. 66
      odoo_dynamic_dashboard/views/dashboard_menu_views.xml
  59. 56
      odoo_dynamic_dashboard/views/dashboard_theme_views.xml
  60. 13
      odoo_dynamic_dashboard/views/dashboard_view.xml
  61. 14
      odoo_dynamic_dashboard/views/dashboard_views.xml
  62. 56
      odoo_dynamic_dashboard/views/dynamic_block_views.xml
  63. 13
      odoo_dynamic_dashboard/wizard/__init__.py
  64. 75
      odoo_dynamic_dashboard/wizard/dashboard_mail.py
  65. 22
      odoo_dynamic_dashboard/wizard/dashboard_mail_views.xml

25
odoo_dynamic_dashboard/README.rst

@ -3,33 +3,32 @@
:alt: License: AGPL-3 :alt: License: AGPL-3
Odoo Dynamic Dashboard Odoo Dynamic Dashboard
====================== ==========================
* Dynamically Arrange the dashboard to get the information that are relevant to your business, department, or a specific process or need. * Dynamically Arrange the dashboard to get the information that are relevant to your business, department, or a specific process or need.
Configuration Configuration
============= =============
* No additional configurations needed. - No configuration needed
License
=======
Affero General Public License v3.0 (AGPL v3)
(https://www.gnu.org/licenses/agpl-3.0-standalone.html)
Company Company
------- -------
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ * `Cybrosys Techno Solutions <https://cybrosys.com/>`__
License
-------
Affero General Public License, Version 3 (AGPL-3).
(https://www.gnu.org/licenses/agpl-3.0-standalone.html)
Credits Credits
------- -------
* Developers : Irfan, Afras, * Developers: (V17) Arjun S,
(V16) Amal Prasad, (V16) Robin, Afra MP
(V17) Arjun S,
* Contact: odoo@cybrosys.com * Contact: odoo@cybrosys.com
Contacts Contacts
-------- --------
* Mail Contact : odoo@cybrosys.com * Mail Contact : odoo@cybrosys.com
* Website : https://cybrosys.com * Website : http://www.cybrosys.com
Bug Tracker Bug Tracker
----------- -----------
@ -39,10 +38,8 @@ Maintainer
========== ==========
.. image:: https://cybrosys.com/images/logo.png .. image:: https://cybrosys.com/images/logo.png
:target: https://cybrosys.com :target: https://cybrosys.com
This module is maintained by Cybrosys Technologies. This module is maintained by Cybrosys Technologies.
For support and more information, please visit https://www.cybrosys.com
For support and more information, please visit `Our Website <https://cybrosys.com/>`__
Further information Further information
=================== ===================

3
odoo_dynamic_dashboard/__init__.py

@ -19,5 +19,6 @@
# If not, see <http://www.gnu.org/licenses/>. # If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################# #############################################################################
from . import models
from . import controllers from . import controllers
from . import models
from . import wizard

34
odoo_dynamic_dashboard/__manifest__.py

@ -21,34 +21,42 @@
############################################################################# #############################################################################
{ {
'name': "Odoo Dynamic Dashboard", 'name': "Odoo Dynamic Dashboard",
'version': '17.0.1.0.0', 'version': '17.0.2.0.0',
'category': 'Extra Tools ', 'category': 'Productivity',
'summary': """Odoo Dynamic Dashboard, Dynamic Dashboard, Odoo Dashboard, Dynamic Dashbaord, AI Dashboard, Odoo17 Dashboard, Dashboard, Odoo17, Configurable Dashboard""", 'summary': """Create Configurable Dashboards Easily""",
'description': """Create Configurable Dashboard Dynamically to get the 'description': """Create Configurable Odoo Dynamic Dashboard to get the
information that are relevant to your business, department, information that are relevant to your business, department, or a specific
or a specific process or need, Dynamic Dashboard, Dashboard , process or need""",
Dashboard Odoo""",
'author': 'Cybrosys Techno Solutions', 'author': 'Cybrosys Techno Solutions',
'company': 'Cybrosys Techno Solutions', 'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions', 'maintainer': 'Cybrosys Techno Solutions',
'website': 'https://www.cybrosys.com', 'website': "https://www.cybrosys.com",
'depends': ['web'], 'depends': ['web'],
'data': [ 'data': [
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'views/dashboard_view.xml', 'data/dashboard_theme_data.xml',
'views/dashboard_menu_view.xml', 'views/dashboard_views.xml',
'views/dynamic_block_view.xml', 'views/dynamic_block_views.xml',
'views/dashboard_menu_views.xml',
'views/dashboard_theme_views.xml',
'wizard/dashboard_mail_views.xml',
], ],
'assets': { 'assets': {
'web.assets_backend': [ 'web.assets_backend': [
'odoo_dynamic_dashboard/static/src/js/**/*.js', 'odoo_dynamic_dashboard/static/src/css/**/*.css',
'odoo_dynamic_dashboard/static/src/scss/**/*.scss', 'odoo_dynamic_dashboard/static/src/scss/**/*.scss',
'odoo_dynamic_dashboard/static/src/js/**/*.js',
'odoo_dynamic_dashboard/static/src/xml/**/*.xml', 'odoo_dynamic_dashboard/static/src/xml/**/*.xml',
'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css',
'odoo_dynamic_dashboard/static/lib/js/interactjs.js',
], ],
}, },
'images': ['static/description/banner.png'], 'images': ['static/description/banner.jpg'],
'license': "AGPL-3", 'license': "AGPL-3",
'installable': True, 'installable': True,
'auto_install': False, 'auto_install': False,
'application': True, 'application': True,
} }

41
odoo_dynamic_dashboard/controllers/odoo_dynamic_dashboard.py

@ -24,38 +24,11 @@ from odoo.http import request
class DynamicDashboard(http.Controller): class DynamicDashboard(http.Controller):
""" """Class to search and filter values in dashboard"""
This is the class DynamicDashboard which is the subclass of the class
http.Controller
"""
@http.route('/create/tile', type='json', auth='user') @http.route('/custom_dashboard/search_input_chart', type='json',
def tile_creation(self, **kw): auth="public", website=True)
"""This is the method to create the tile when create on the button def dashboard_search_input_chart(self, search_input):
ADD BLOCK""" """Function to filter search input in dashboard"""
tile_type = kw.get('type') return request.env['dashboard.block'].search([
action_id = kw.get('action_id') ('name', 'ilike', search_input)]).ids
request.env['dashboard.block'].get_dashboard_vals(action_id)
tile_id = request.env['dashboard.block'].sudo().create({
'name': 'New Block',
'type': tile_type,
'tile_color': '#1f6abb',
'text_color': '#FFFFFF',
'fa_icon': 'fa fa-money',
'fa_color': '#132e45',
'edit_mode': True,
'client_action': int(action_id),
})
return {'id': tile_id.id, 'name': tile_id.name, 'type': tile_type, 'icon': 'fa fa-money',
'color': '#1f6abb',
'tile_color': '#1f6abb',
'text_color': '#FFFFFF',
'icon_color': '#1f6abb'}
@http.route('/get/values', type='json', auth='user')
def get_value(self, **kw):
"""This is the method get_value which will get the records inside the
tile"""
action_id = kw.get('action_id')
datas = request.env['dashboard.block'].get_dashboard_vals(action_id)
return datas

12
odoo_dynamic_dashboard/data/dashboard_theme_data.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<!-- Demo Dashboard Theme -->
<record id="demo_theme" model="dashboard.theme">
<field name="name">Demo</field>
<field name="color_x">#4158D0</field>
<field name="color_y">#C850C0</field>
<field name="color_z">#FFCC70</field>
</record>
</data>
</odoo>

12
odoo_dynamic_dashboard/doc/RELEASE_NOTES.md

@ -1,7 +1,13 @@
## Module <odoo_dynamic_dashboard> ## Module <odoo_dynamic_dashboard>
#### 02.03.2024 #### 18.05.2024
#### Version 17.0.1.0.0 #### Version 17.0.1.0.0
#### ADD ##### ADD
- Initial commit for Odoo Dynamic Dashboard
- Initial Commit for Odoo Dynamic Dashboard ## Module <odoo_dynamic_dashboard>
#### 20.07.2024
#### Version 17.0.2.0.0
##### UPDT
- New Features has been Added.

2
odoo_dynamic_dashboard/models/__init__.py

@ -20,6 +20,6 @@
# #
############################################################################# #############################################################################
from . import dashboard_block from . import dashboard_block
from . import dashboard_block_line
from . import dashboard_menu from . import dashboard_menu
from . import dashboard_theme
from . import domain_to_sql from . import domain_to_sql

174
odoo_dynamic_dashboard/models/dashboard_block.py

@ -19,140 +19,164 @@
# If not, see <http://www.gnu.org/licenses/>. # If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################# #############################################################################
from odoo import models, fields, _
from odoo.exceptions import ValidationError
from odoo.osv import expression
from ast import literal_eval from ast import literal_eval
from odoo import api, fields, models
from odoo.osv import expression
class DashboardBlock(models.Model): class DashboardBlock(models.Model):
"""Creates the model Dashboard Blocks""" """Class is used to create charts and tiles in dashboard"""
_name = "dashboard.block" _name = "dashboard.block"
_description = "Dashboard Blocks" _description = "Dashboard Block"
def get_default_action(self): def get_default_action(self):
"""This is the method get_default_action which will return the default """Function to get values from dashboard if action_id is true return
action id.""" id else return false"""
action_id = self.env.ref( action_id = self.env.ref(
'odoo_dynamic_dashboard.dynamic_dashboard_action') 'odoo_dynamic_dashboard.dashboard_view_action')
if action_id: if action_id:
return action_id.id return action_id.id
return False return False
name = fields.Char(string="Name", help='Name of the block') name = fields.Char(string="Name", help='Name of the block')
field_id = fields.Many2one('ir.model.fields', string='Measured Field', fa_icon = fields.Char(string="Icon", help="Add icon for tile")
domain="[('store', '=', True), ('model_id', '=', model_id), ('ttype', 'in', ['float','integer','monetary'])]",
help='Measured field for the block')
fa_icon = fields.Char(string="Icon", help='Icon for the block')
graph_size = fields.Selection(
selection=[("col-lg-4", "Small"), ("col-lg-6", "Medium"),
("col-lg-12", "Large")],
string="Graph Size", default='col-lg-4', help="Size of the graph")
operation = fields.Selection( operation = fields.Selection(
selection=[("sum", "Sum"), ("avg", "Average"), ("count", "Count")], selection=[("sum", "Sum"), ("avg", "Average"), ("count", "Count")],
string="Operation", string="Operation",
help='Tile Operation that needs to bring values for tile') help='Tile Operation that needs to bring values for tile',
required=True)
graph_type = fields.Selection( graph_type = fields.Selection(
selection=[("bar", "Bar"), ("radar", "Radar"), ("pie", "Pie"), selection=[("bar", "Bar"), ("radar", "Radar"), ("pie", "Pie"),
("line", "Line"), ("doughnut", "Doughnut")], ("polarArea", "polarArea"), ("line", "Line"),
("doughnut", "Doughnut")],
string="Chart Type", help='Type of Chart') string="Chart Type", help='Type of Chart')
measured_field = fields.Many2one("ir.model.fields", string="Measured Field", measured_field_id = fields.Many2one("ir.model.fields",
help='Measure field for the chart') string="Measured Field",
client_action = fields.Many2one('ir.actions.client', help="Select the Measured")
client_action_id = fields.Many2one('ir.actions.client',
string="Client action",
default=get_default_action, default=get_default_action,
string="Client Action", help="Client action")
help='Client Action for the dashboard '
'block')
type = fields.Selection( type = fields.Selection(
selection=[("graph", "Chart"), ("tile", "Tile")], string="Type", selection=[("graph", "Chart"), ("tile", "Tile")],
help='Type of Block ie, Chart or Tile') string="Type", help='Type of Block ie, Chart or Tile')
x_axis = fields.Char(string="X-Axis", help="X-axis for the chart") x_axis = fields.Char(string="X-Axis", help="Chart X-axis")
y_axis = fields.Char(string="Y-Axis", help="Y-axis for the chart") y_axis = fields.Char(string="Y-Axis", help="Chart Y-axis")
group_by = fields.Many2one("ir.model.fields", store=True, 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)", string="Group by(Y-Axis)",
help='Field value for Y-Axis', help='Field value for Y-Axis')
domain="[('store', '=', True)]")
tile_color = fields.Char(string="Tile Color", help='Primary Color of Tile') tile_color = fields.Char(string="Tile Color", help='Primary Color of Tile')
text_color = fields.Char(string="Text Color", help='Text 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') fa_color = fields.Char(string="Icon Color", help='Icon Color of Tile')
filter = fields.Char(string="Filter", help='Filter for Tile') filter = fields.Char(string="Filter", help="Add filter")
model_id = fields.Many2one('ir.model', string='Model', model_id = fields.Many2one('ir.model', string='Model',
help='Model for Tile') help="Select the module name")
model_name = fields.Char(related='model_id.model', readonly=True, model_name = fields.Char(related='model_id.model', string="Model Name",
string="Model Name", help='Model Name of Tile') help="Added model_id model")
filter_by = fields.Many2one("ir.model.fields", string=" Filter By", edit_mode = fields.Boolean(string="Edit Mode",
help="Filter By for Tile") help="Enable to edit chart and tile",
filter_values = fields.Char(string="Filter Values", default=False, invisible=True)
help="Filter Values for tiles accordingly")
sequence = fields.Integer(string="Sequence", @api.onchange('model_id')
help="sequence of the dashboard") def _onchange_model_id(self):
edit_mode = fields.Boolean(default=False, invisible=True, if self.operation or self.measured_field_id:
string="Edit Mode", help="Edit mode of the tile") self.operation = False
self.measured_field_id = False
def get_dashboard_vals(self, action_id): def get_dashboard_vals(self, action_id, start_date=None, end_date=None):
"""Dashboard block values""" """Fetch block values from js and create chart"""
block_id = [] block_id = []
for rec in self.env['dashboard.block'].sudo().search( for rec in self.env['dashboard.block'].sudo().search(
[('client_action', '=', int(action_id))]): [('client_action_id', '=', int(action_id))]):
if rec.filter is False: if rec.filter is False:
rec.filter = "[]" rec.filter = "[]"
filter_list = literal_eval(rec.filter) filter_list = literal_eval(rec.filter)
filter_list = [filter_item for filter_item in filter_list if not ( filter_list = [filter_item for filter_item in filter_list if not (
isinstance(filter_item, tuple) and filter_item[ isinstance(filter_item, tuple) and filter_item[
0] == 'create_date')] 0] == 'create_date')]
vals = { rec.filter = repr(filter_list)
'id': rec.id, vals = {'id': rec.id, 'name': rec.name, 'type': rec.type,
'name': rec.name, 'graph_type': rec.graph_type, 'icon': rec.fa_icon,
'type': rec.type,
'graph_type': rec.graph_type,
'icon': rec.fa_icon,
'cols': rec.graph_size,
'color': rec.tile_color if rec.tile_color else '#1f6abb;',
'text_color': rec.text_color if rec.text_color else '#FFFFFF;',
'icon_color': rec.fa_color if rec.fa_color else '#1f6abb;',
'tile_color': rec.tile_color if rec.tile_color else '#FFFFFF;',
'model_name': rec.model_name, 'model_name': rec.model_name,
'measured_field': rec.measured_field.field_description if rec.measured_field else None, 'color': f'background-color: {rec.tile_color};' if rec.tile_color else '#1f6abb;',
'y_field': rec.measured_field.name, 'text_color': f'color: {rec.text_color};' if rec.text_color else '#FFFFFF;',
'x_field': rec.group_by.name, 'val_color': f'color: {rec.val_color};' if rec.val_color else '#FFFFFF;',
'operation': rec.operation, 'icon_color': f'color: {rec.tile_color};' if rec.tile_color else '#1f6abb;',
'domain': filter_list '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 = [] domain = []
if rec.filter: if rec.filter:
domain = expression.AND([literal_eval(rec.filter)]) domain = expression.AND([literal_eval(rec.filter)])
if rec.model_name: if rec.model_name:
if rec.type == 'graph': if rec.type == 'graph':
query = self.env[rec.model_name].get_query(domain, self._cr.execute(self.env[rec.model_name].get_query(domain,
rec.operation, rec.operation,
rec.measured_field, rec.measured_field_id,
group_by=rec.group_by) start_date,
try: end_date,
self._cr.execute(query) group_by=rec.group_by_id))
except Exception as exc:
raise ValidationError(
_(f"Could'nt fetch data try another group by field for {rec.name} block")) from exc
records = self._cr.dictfetchall() records = self._cr.dictfetchall()
x_axis = [] x_axis = []
for record in records: for record in records:
x_axis.append(record.get(rec.group_by.name)) if record.get('name') and type(
record.get('name')) == dict:
x_axis.append(record.get('name')[self._context.get(
'lang') or 'en_US'])
else:
x_axis.append(record.get(rec.group_by_id.name))
y_axis = [] y_axis = []
for record in records: for record in records:
y_axis.append(record.get('value')) y_axis.append(record.get('value'))
vals.update({'x_axis': x_axis, 'y_axis': y_axis}) vals.update({'x_axis': x_axis, 'y_axis': y_axis})
else: else:
query = self.env[rec.model_name].get_query(domain, self._cr.execute(self.env[rec.model_name].get_query(domain,
rec.operation, rec.operation,
rec.measured_field) rec.measured_field_id,
self._cr.execute(query) start_date,
end_date))
records = self._cr.dictfetchall() records = self._cr.dictfetchall()
magnitude = 0 magnitude = 0
total = records[0].get('value') total = records[0].get('value')
while abs(total) >= 1000: while abs(total) >= 1000:
magnitude += 1 magnitude += 1
total /= 1000.0 total /= 1000.0
val = f'{total:.2f}{" KMGTP"[magnitude]}' if magnitude else f'{total:.2f}' val = '%.2f%s' % (
total, ['', 'K', 'M', 'G', 'T', 'P'][magnitude])
records[0]['value'] = val records[0]['value'] = val
vals.update(records[0]) vals.update(records[0])
block_id.append(vals) block_id.append(vals)
return block_id 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

93
odoo_dynamic_dashboard/models/dashboard_menu.py

@ -23,77 +23,58 @@ from odoo import api, fields, models
class DashboardMenu(models.Model): class DashboardMenu(models.Model):
""" """Class to create new dashboard menu"""
This is the class DashboardMenu which is the subclass of the class Model
which is here used to create the model dashboard.menu.
"""
_name = "dashboard.menu" _name = "dashboard.menu"
_description = "Dashboard Menu" _description = "Dashboard Menu"
name = fields.Char(string="Name", help="Name of the dashboard") name = fields.Char(string="Name", ondelete='cascade',
parent_id = fields.Many2one('ir.ui.menu', string="Menu", help="Enter a name for the dashboard menu")
help="Parent of the dashboard") 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', group_ids = fields.Many2many('res.groups', string='Groups',
related='parent_id.groups_id', related='menu_id.groups_id',
help="User need to be at least in one " help="User need to be at least in one of "
"of these groups to see the menu") "these groups to see the menu")
client_action_id = fields.Many2one('ir.actions.client', client_action_id = fields.Many2one('ir.actions.client',
string='Client Action', string="Client Action",
help="Client Action Related " help="Client action of the "
"to the dashboard") "corresponding dashboard menu")
menu_id = fields.Many2one('ir.ui.menu', string="Created Menu",
help="Created menu")
@api.model @api.model
def create(self, vals): def create(self, vals):
""" """Function to create new dashboard menu"""
Summary: action_id = self.env['ir.actions.client'].create({
This is the create function of the model DashboardMenu which is
triggered when creating a new record in this model.
Args:
vals:
The values when the user creating a new record.
Returns:
res:
Returns the created record at the end
"""
values = {
'name': vals['name'], 'name': vals['name'],
'tag': 'owl.dynamic_dashboard', 'tag': 'OdooDynamicDashboard',
} })
action_id = self.env['ir.actions.client'].create(values)
vals['client_action_id'] = action_id.id vals['client_action_id'] = action_id.id
menu_id = self.env['ir.ui.menu'].create({ self.env['ir.ui.menu'].create({
'name': vals['name'], 'name': vals['name'],
'parent_id': vals['parent_id'], 'parent_id': vals['menu_id'],
'action': f'ir.actions.client,{action_id.id}' 'action': 'ir.actions.client,%d' % (action_id.id,)
}) })
res = super(DashboardMenu, self).create(vals) return super(DashboardMenu, self).create(vals)
res.menu_id = menu_id.id
return res
def write(self, vals): def write(self, vals):
""" """Function to save edited data in dashboard menu"""
Summary: for rec in self:
This is the write function of the model DashboardMenu which is client_act_id = rec['client_action_id'].id
triggered when changing any value in the corresponding record. self.env['ir.ui.menu'].search(
Args: [('parent_id', '=', rec['menu_id'].id),
vals: ('action', '=', f'ir.actions.client,{client_act_id}')]).write({
The values when the user creating a editing an record. 'name': vals['name'] if 'name' in vals.keys() else rec['name'],
Returns: 'parent_id': vals['menu_id'] if 'menu_id' in vals.keys() else
Returns the updated record at the end rec['menu_id'],
""" 'action': f'ir.actions.client,{client_act_id}'
if self.menu_id: })
self.menu_id.update(vals)
return super(DashboardMenu, self).write(vals) return super(DashboardMenu, self).write(vals)
def unlink(self): def unlink(self):
""" """Delete dashboard along with menu item"""
Summary: for rec in self:
This is the unlink function of the model DashboardMenu which is self.env['ir.ui.menu'].search(
triggered when unlinking any record in this model. [('parent_id', '=', rec['menu_id'].id),
Returns: ('action', '=',
Returns the record delete. f'ir.actions.client,{rec["client_action_id"].id}')]).unlink()
"""
self.menu_id.unlink()
return super(DashboardMenu, self).unlink() return super(DashboardMenu, self).unlink()

53
odoo_dynamic_dashboard/models/dashboard_theme.py

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.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 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"<div style='width:300px; height:300px;background-image: linear-gradient(50deg, {self.color_x} 0%, {self.color_y} 46%, {self.color_z} 100%);'/>"
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

43
odoo_dynamic_dashboard/models/domain_to_sql.py

@ -22,13 +22,15 @@
from odoo import models from odoo import models
def get_query(self, args, operation, field, group_by=False, apply_ir_rules=False): def get_query(self, args, operation, field, start_date=None, end_date=None,
group_by=False, apply_ir_rules=False):
""" Dashboard block Query Creation """ """ Dashboard block Query Creation """
query = self._where_calc(args) query = self._where_calc(args)
if apply_ir_rules: if apply_ir_rules:
self._apply_ir_rules(query, 'read') self._apply_ir_rules(query, 'read')
if operation and field: if operation and field:
data = 'COALESCE(%s("%s".%s),0) AS value' % (operation.upper(), self._table, field.name) data = 'COALESCE(%s("%s".%s),0) AS value' % (
operation.upper(), self._table, field.name)
join = '' join = ''
group_by_str = '' group_by_str = ''
if group_by: if group_by:
@ -37,31 +39,36 @@ def get_query(self, args, operation, field, group_by=False, apply_ir_rules=False
join = ' INNER JOIN %s on "%s".id = "%s".%s' % ( join = ' INNER JOIN %s on "%s".id = "%s".%s' % (
relation_model, relation_model, self._table, group_by.name) relation_model, relation_model, self._table, group_by.name)
rec_name = self.env[group_by.relation]._rec_name_fallback() rec_name = self.env[group_by.relation]._rec_name_fallback()
data = data + ',"%s".%s AS %s' % (relation_model, rec_name, group_by.name) data = data + ',"%s".%s AS %s' % (
relation_model, rec_name, group_by.name)
group_by_str = ' Group by "%s".%s' % (relation_model, rec_name) group_by_str = ' Group by "%s".%s' % (relation_model, rec_name)
else: else:
data = data + ',"%s".%s' % (self._table, group_by.name) data = data + ',"%s".%s' % (self._table, group_by.name)
group_by_str = ' Group by "%s".%s' % (self._table, str(group_by.name)) group_by_str = ' Group by "%s".%s' % (
self._table, str(group_by.name))
else: else:
data = '"%s".id' % (self._table) data = '"%s".id' % (self._table)
from_clause, where_clause, where_clause_params = query.get_sql() from_clause, where_clause, where_clause_params = query.get_sql()
where_str = where_clause and (" WHERE %s" % where_clause) or '' where_str = where_clause and (" WHERE %s" % where_clause) or ''
if 'company_id' in self._fields: if start_date and start_date != 'null':
if len(self.env.companies.ids) > 1: start_date_query = f' AND ({from_clause}."create_date" >= \'{start_date}\')'
operator = 'in'
company = str(tuple(self.env.companies.ids))
else: else:
operator = '=' start_date_query = ''
company = self.env.companies.ids[0] if end_date and end_date != 'null':
if where_str == '': end_date_query = f' AND ({from_clause}."create_date" <= \'{end_date}\')'
add = ' where'
else: else:
add = ' and' end_date_query = ''
multicompany_condition = '%s "%s".company_id %s %s' % (add, self._table, operator, company) 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: else:
multicompany_condition = '' return str(x)
exact_query = query_str % tuple(map(format_param, where_clause_params))
return exact_query
query_str = 'SELECT %s FROM ' % data + from_clause + join + where_str + multicompany_condition + group_by_str
where_clause_params = map(lambda x: "'" + str(x) + "'", where_clause_params)
return query_str % tuple(where_clause_params)
models.BaseModel.get_query = get_query models.BaseModel.get_query = get_query

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

@ -1,4 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 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_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_menu,access.dashboard.menu,model_dashboard_menu,base.group_user,1,1,1,1
access_dashboard_block_line,access.dashboard.block.line,model_dashboard_block_line,base.group_user,1,1,1,1 access_dashboard_theme,access.dashboard.theme,model_dashboard_theme,base.group_user,1,1,1,1
access_dashboard_mail,access.dashboard.mail,model_dashboard_mail,base.group_user,1,1,1,1

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
4 access_dashboard_block_line access_dashboard_theme access.dashboard.block.line access.dashboard.theme model_dashboard_block_line model_dashboard_theme base.group_user 1 1 1 1
5 access_dashboard_mail access.dashboard.mail model_dashboard_mail base.group_user 1 1 1 1

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/7.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/Tiles.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/additems.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/blocks.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/charts.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/darkmode.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/dashboard_create.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/delete.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/editlayout.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/email.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/email_report.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/hero.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 KiB

After

Width:  |  Height:  |  Size: 370 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/mainpage.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/newbar.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/newtile.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/pdf.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/pdf_and_mail.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/redirect.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/sales_dashboard.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/savetypes.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/themes.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
odoo_dynamic_dashboard/static/description/assets/screenshots/themeselect.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 KiB

471
odoo_dynamic_dashboard/static/description/index.html

@ -7,12 +7,9 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title> <title>Document</title>
<link rel="stylesheet" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
crossorigin="anonymous">
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
<style> <style>
.nav-tabs li .active { .nav-tabs li .active {
background-color: #a79822; background-color: #a79822;
@ -46,24 +43,6 @@
.collapsed:after { .collapsed:after {
content: '\002B'; content: '\002B';
} }
</style> </style>
<script> <script>
var acc = document.getElementsByClassName("accordion"); var acc = document.getElementsByClassName("accordion");
@ -79,24 +58,6 @@
} }
}); });
} }
</script> </script>
</head> </head>
@ -104,8 +65,7 @@
<!-- HERO --> <!-- HERO -->
<div class="container" style="width:84vw"> <div class="container" style="width:84vw">
<!--Slide--> <!--Slide-->
<div class="row" <div class="row" style="padding: 4rem 2.5rem 0 !important; background-color: #fff !important;">
style="padding: 4rem 2.5rem 0 !important; background-color: #fff !important;">
<div class="col-lg-12 d-flex flex-column align-items-center"> <div class="col-lg-12 d-flex flex-column align-items-center">
<h1 class="text-center text-uppercase" <h1 class="text-center text-uppercase"
style="font-family: Montserrat, 'sans-serif' !important; font-size: 67px !important; color: #5154a5; font-weight: 900 !important;"> style="font-family: Montserrat, 'sans-serif' !important; font-size: 67px !important; color: #5154a5; font-weight: 900 !important;">
@ -113,21 +73,19 @@
</h1> </h1>
<p class="my-1 text-center text-uppercase" <p class="my-1 text-center text-uppercase"
style="letter-spacing: 4px !important; color: #74788D !important;"> style="letter-spacing: 4px !important; color: #74788D !important;">
Advanced Dashboard Module For Odoo17</p> This Module Helps To Create Configurable Dashboards Easily.</p>
</div> </div>
<div class="col-lg-12 d-flex flex-column justify-content-center"> <div class="col-lg-12 d-flex flex-column justify-content-center">
<p class="my-1 text-center" <p class="my-1 text-center"
style="font-family: Montserrat, 'sans-serif' !important; color: #212121 !important;"> style="font-family: Montserrat, 'sans-serif' !important; color: #212121 !important;">
Dynamically Arrange the dashboard to get the information that Create Configurable Odoo Dynamic Dashboard to get the
are relevant to your business, department, or a specific process information that are relevant to your business, department, or a specific
or need. process or need.
</p> </p>
</div> </div>
<div class="col-lg-12 d-flex justify-content-center align-items-center" <div class="col-lg-12 d-flex justify-content-center align-items-center" style="margin: 2rem 0;">
style="margin: 2rem 0;"> <img src="./assets/cybro-odoo.png" width="100%" height="auto" style="width:50%;" class="img-responsive">
<img src="./assets/cybro-odoo.png" width="100%" height="auto"
style="width:50%;" class="img-responsive">
</div> </div>
@ -135,25 +93,19 @@
<a href="mailto:odoo@cybrosys.com" target="_blank" <a href="mailto:odoo@cybrosys.com" target="_blank"
style="background-color:#5154a5; border-radius:35px; font-family:Montserrat; display:inline-block; padding:7px 33px; border:1px solid #5154a5" style="background-color:#5154a5; border-radius:35px; font-family:Montserrat; display:inline-block; padding:7px 33px; border:1px solid #5154a5"
class="mx-1 mb-2 deep-1 deep_hover"> class="mx-1 mb-2 deep-1 deep_hover">
<img class="img" style="width:30px" <img class="img" style="width:30px" src="assets/icons/support.png">
src="assets/icons/support.png"> <span class="pl-2" style="color:#fff; font-size:16px; vertical-align:middle">Email Us</span>
<span class="pl-2"
style="color:#fff; font-size:16px; vertical-align:middle">Email Us</span>
</a> </a>
<a href="cybroopenerp" target="_blank" <a href="cybroopenerp" target="_blank"
style="background-color:#7f289b; font-family:Montserrat; display:inline-block; padding:7px 33px; border:1px solid #7f289b; border-radius:35px" style="background-color:#7f289b; font-family:Montserrat; display:inline-block; padding:7px 33px; border:1px solid #7f289b; border-radius:35px"
class="mx-1 mb-2 deep-1 deep_hover"> class="mx-1 mb-2 deep-1 deep_hover">
<img class="img" style="width:27px" <img class="img" style="width:27px" src="assets/icons/skype.png">
src="assets/icons/skype.png"> <span class="pl-2" style="color:#fff; font-size:16px; vertical-align:middle">Skype Us</span>
<span class="pl-2"
style="color:#fff; font-size:16px; vertical-align:middle">Skype Us</span>
</a> </a>
</div> </div>
</div> </div>
<div class="col-lg-12 d-flex justify-content-center align-items-center" <div class="col-lg-12 d-flex justify-content-center align-items-center" style="margin:2rem 0">
style="margin:2rem 0"> <img src="assets/screenshots/hero.gif" width="100%" height="auto" class="img-responsive">
<img src="assets/screenshots/hero.gif" width="100%" height="auto"
class="img-responsive">
</div> </div>
<!-- END OF slide --> <!-- END OF slide -->
@ -168,51 +120,39 @@
<div class="col-md-6 col-sm-12 mt32"> <div class="col-md-6 col-sm-12 mt32">
<div class="container shadow d-flex align-items-center justify-content-center" <div class="container shadow d-flex align-items-center justify-content-center"
style="border-radius: 5px;padding: 33px 0px;min-height: 180px; margin: 15px auto;"> style="border-radius: 5px;padding: 33px 0px;min-height: 180px; margin: 15px auto;">
<div class="col-md-3 d-flex align-items-center justify-content-center" <div class="col-md-3 d-flex align-items-center justify-content-center" style="float:left">
style="float:left"> <img class="img img-responsive" src="assets/icons/test-1.png">
<img class="img img-responsive"
src="assets/icons/test-1.png">
</div> </div>
<div class="col-md-9" <div class="col-md-9" style="padding-left:0; float:left; width:70%">
style="padding-left:0; float:left; width:70%">
<h3 class="mt16 mb0" <h3 class="mt16 mb0"
style="font-family:Roboto; font-weight:500; font-size:22px; color: #781d96;"> style="font-family:Roboto; font-weight:500; font-size:22px; color: #781d96;">
Dynamic Dashboard for configuring new chart & Easily Create Dynamic Charts And Tiles</h3>
tiles</h3>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6 col-sm-12 mt32"> <div class="col-md-6 col-sm-12 mt32">
<div class="container shadow d-flex align-items-center justify-content-center" <div class="container shadow d-flex align-items-center justify-content-center"
style="border-radius: 5px;padding: 33px 0px;min-height: 180px; margin: 15px auto;"> style="border-radius: 5px;padding: 33px 0px;min-height: 180px; margin: 15px auto;">
<div class="col-md-3 d-flex align-items-center justify-content-center" <div class="col-md-3 d-flex align-items-center justify-content-center" style="float:left">
style="float:left"> <img class="img img-responsive" src="assets/icons/test-1.png">
<img class="img img-responsive"
src="assets/icons/test-1.png">
</div> </div>
<div class="col-md-9" <div class="col-md-9" style="padding-left:0; float:left; width:70%">
style="padding-left:0; float:left; width:70%">
<h3 class="mt16 mb0" <h3 class="mt16 mb0"
style="font-family:Roboto; font-weight:500; font-size:22px; color: #781d96;"> style="font-family:Roboto; font-weight:500; font-size:22px; color: #781d96;">
Adding background and font color for tile, Create A Dynamic Dashboard Menu In Any Model</h3>
option for selecting FontAwesome icon</h3>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6 col-sm-12 mt32"> <div class="col-md-6 col-sm-12 mt32">
<div class="container shadow d-flex align-items-center justify-content-center" <div class="container shadow d-flex align-items-center justify-content-center"
style="border-radius: 5px;padding: 33px 0px;min-height: 180px; margin: 15px auto;"> style="border-radius: 5px;padding: 33px 0px;min-height: 180px; margin: 15px auto;">
<div class="col-md-3 d-flex align-items-center justify-content-center" <div class="col-md-3 d-flex align-items-center justify-content-center" style="float:left">
style="float:left"> <img class="img img-responsive" src="assets/icons/test-1.png">
<img class="img img-responsive"
src="assets/icons/test-1.png">
</div> </div>
<div class="col-md-9" <div class="col-md-9" style="padding-left:0; float:left; width:70%">
style="padding-left:0; float:left; width:70%">
<h3 class="mt16 mb0" <h3 class="mt16 mb0"
style="font-family:Roboto; font-weight:500; font-size:22px; color: #781d96;"> style="font-family:Roboto; font-weight:500; font-size:22px; color: #781d96;">
Chart types: Bar, Line, Radar, Pie, Doughnut, Charts Can Export Into Image, PDF, XLSX And CSV
option to select the chart sizes
</h3> </h3>
</div> </div>
</div> </div>
@ -220,16 +160,13 @@
<div class="col-md-6 col-sm-12 mt32"> <div class="col-md-6 col-sm-12 mt32">
<div class="container shadow d-flex align-items-center justify-content-center" <div class="container shadow d-flex align-items-center justify-content-center"
style="border-radius: 5px;padding: 33px 0px;min-height: 180px; margin: 15px auto;"> style="border-radius: 5px;padding: 33px 0px;min-height: 180px; margin: 15px auto;">
<div class="col-md-3 d-flex align-items-center justify-content-center" <div class="col-md-3 d-flex align-items-center justify-content-center" style="float:left">
style="float:left"> <img class="img img-responsive" src="assets/icons/test-1.png">
<img class="img img-responsive"
src="assets/icons/test-1.png">
</div> </div>
<div class="col-md-9" <div class="col-md-9" style="padding-left:0; float:left; width:70%">
style="padding-left:0; float:left; width:70%">
<h3 class="mt16 mb0" <h3 class="mt16 mb0"
style="font-family:Roboto; font-weight:500; font-size:22px; color: #781d96;"> style="font-family:Roboto; font-weight:500; font-size:22px; color: #781d96;">
Lets you create dynamic menu's easily</h3> Edit And Configure Charts And Tiles</h3>
</div> </div>
</div> </div>
</div> </div>
@ -257,9 +194,9 @@
<div class="col-sm-12 py-4"> <div class="col-sm-12 py-4">
<h5 style="text-indent: 2em; text-align: justify;"> <h5 style="text-indent: 2em; text-align: justify;">
This module helps to create configurable dashboards easily. This module helps to create configurable dashboards easily.
The Odoo Dynamic Dashboard module allows you to arrange the Odoo Dynamic Dashboard module helps to Arrange the dashboard to
dashboard to display information relevant to your business, get the information that are relevant to your business,
department, or specific processes and needs. department, or a specific process or need.
</h5> </h5>
</div> </div>
</div> </div>
@ -271,19 +208,16 @@
<!-- New section --> <!-- New section -->
<div class="container" style="width:84vw"> <div class="container" style="width:84vw">
<div class="row" <div class="row" style="padding: 4rem 2.5rem 0 !important; background-color: #fff !important;">
style="padding: 4rem 2.5rem 0 !important; background-color: #fff !important;">
<!-- tabs --> <!-- tabs -->
<div class="col-md-12"> <div class="col-md-12">
<ul role="tablist" role="tablist" <ul role="tablist" role="tablist"
class="nav nav-tabs justify-content-center d-flex justify-content-center" class="nav nav-tabs justify-content-center d-flex justify-content-center" data-tabs="tabs"
data-tabs="tabs"
style="border:none; background-color:unset; margin:0 auto"> style="border:none; background-color:unset; margin:0 auto">
<li class="nav-item" <li class="nav-item"
style="border-top-right-radius:10px; border-top-left-radius:10px; background-color:#ffe305; margin-right:10px; border:1px solid #ddd; border-bottom:0"> style="border-top-right-radius:10px; border-top-left-radius:10px; background-color:#ffe305; margin-right:10px; border:1px solid #ddd; border-bottom:0">
<a href="#Screenshot" data-bs-toggle="tab" <a href="#Screenshot" data-bs-toggle="tab" aria-expanded="true" class="show"
aria-expanded="true" class="show"
aria-expanded="true" class="show active" aria-expanded="true" class="show active"
style="font-family:Roboto; text-transform:uppercase; font-weight:600; font-size:15px; letter-spacing:1px; padding:11px 20px; border-top-left-radius:10px; border-top-right-radius:10px; color:#2b2b2b; border:1px solid transparent"> style="font-family:Roboto; text-transform:uppercase; font-weight:600; font-size:15px; letter-spacing:1px; padding:11px 20px; border-top-left-radius:10px; border-top-right-radius:10px; color:#2b2b2b; border:1px solid transparent">
<img src="assets/icons/screenshot.png" <img src="assets/icons/screenshot.png"
@ -297,8 +231,7 @@
<li class="nav-item" <li class="nav-item"
style="border-top-right-radius:10px; border-top-left-radius:10px; background-color:#ffe305; margin-right:10px; border:1px solid #ddd; border-bottom:0"> style="border-top-right-radius:10px; border-top-left-radius:10px; background-color:#ffe305; margin-right:10px; border:1px solid #ddd; border-bottom:0">
<a href="#Features" data-bs-toggle="tab" <a href="#Features" data-bs-toggle="tab" aria-expanded="true" class="show"
aria-expanded="true" class="show"
style="font-family:Roboto; text-transform:uppercase; font-weight:600; font-size:15px; letter-spacing:1px; padding:11px 20px; border-top-left-radius:10px; border-top-right-radius:10px; color:#2b2b2b; border:1px solid transparent"> style="font-family:Roboto; text-transform:uppercase; font-weight:600; font-size:15px; letter-spacing:1px; padding:11px 20px; border-top-left-radius:10px; border-top-right-radius:10px; color:#2b2b2b; border:1px solid transparent">
<img src="assets/icons/feature.png" <img src="assets/icons/feature.png"
style="width: 100%; width: 25px; margin-right: 10px;">Features style="width: 100%; width: 25px; margin-right: 10px;">Features
@ -311,8 +244,7 @@
<li class="nav-item" <li class="nav-item"
style="border-top-right-radius:10px; border-top-left-radius:10px; background-color:#ffe305; margin-right:10px; border:1px solid #ddd; border-bottom:0"> style="border-top-right-radius:10px; border-top-left-radius:10px; background-color:#ffe305; margin-right:10px; border:1px solid #ddd; border-bottom:0">
<a href="#ReleaseNotes" data-bs-toggle="tab" <a href="#ReleaseNotes" data-bs-toggle="tab" aria-expanded="true" class="show"
aria-expanded="true" class="show"
style="font-family:Roboto; text-transform:uppercase; font-weight:600; font-size:15px; letter-spacing:1px; padding:11px 20px; border-top-left-radius:10px; border-top-right-radius:10px; color:#2b2b2b; border:1px solid transparent"> style="font-family:Roboto; text-transform:uppercase; font-weight:600; font-size:15px; letter-spacing:1px; padding:11px 20px; border-top-left-radius:10px; border-top-right-radius:10px; color:#2b2b2b; border:1px solid transparent">
<img src="assets/icons/notes.png" <img src="assets/icons/notes.png"
style="width: 100%; width: 25px; margin-right: 10px;">Release style="width: 100%; width: 25px; margin-right: 10px;">Release
@ -329,10 +261,8 @@
<div class="col-md-12 tab-content ui-front" <div class="col-md-12 tab-content ui-front"
style="border-top-right-radius:15px;border-bottom-right-radius:15px;height:auto;"> style="border-top-right-radius:15px;border-bottom-right-radius:15px;height:auto;">
<div class="tab-pane fade active show" id="Screenshot" <div class="tab-pane fade active show" id="Screenshot" role="tabpanel" aria-labelledby="screenshot">
role="tabpanel" aria-labelledby="screenshot"> <div class="row" style="padding:4rem 2.5rem 0 !important; background-color:#fff !important">
<div class="row"
style="padding:4rem 2.5rem 0 !important; background-color:#fff !important">
<div class="col-lg-12 d-flex flex-column align-items-center"> <div class="col-lg-12 d-flex flex-column align-items-center">
<h1 class="text-center text-uppercase" <h1 class="text-center text-uppercase"
@ -352,7 +282,7 @@
Odoo Dynamic Dashboard View Odoo Dynamic Dashboard View
</h3> </h3>
<div style="margin:2rem"> <div style="margin:2rem">
<img src="assets/screenshots/1.png" <img src="assets/screenshots/mainpage.png"
class="col-lg-12 d-flex flex-column align-items-center"> class="col-lg-12 d-flex flex-column align-items-center">
</div> </div>
</div> </div>
@ -363,12 +293,10 @@
Dashboard Tiles View Dashboard Tiles View
</h3> </h3>
<p style="font-family:Roboto ; color:#280135"> <p style="font-family:Roboto ; color:#280135">
The tiled view of the records in Odoo is also The tiled view of the records in Odoo is also clickable; when you click on a record, you will be directed to the records tree view.
clickable; when you click on a record, you will be
directed to the records tree view.
</p> </p>
<div style="margin:2rem"> <div style="margin:2rem">
<img src="assets/screenshots/2.png" <img src="assets/screenshots/Tiles.png"
class="col-lg-12 d-flex flex-column align-items-center"> class="col-lg-12 d-flex flex-column align-items-center">
</div> </div>
</div> </div>
@ -379,10 +307,21 @@
Dashboard Charts View Dashboard Charts View
</h3> </h3>
<p style="font-family:Roboto ; color:#280135"> <p style="font-family:Roboto ; color:#280135">
The chart view of the records in Odoo is provided in The chart view of the records in Odoo is provided in the dashboard based on the filtering.</p>
the dashboard based on the filtering.</p> <div style="margin:2rem">
<img src="assets/screenshots/charts.png"
class="col-lg-12 d-flex flex-column align-items-center">
</div>
<p style="font-family:Roboto ; color:#280135">
Clicking on various icons generates different types of chart data, which can be exported as image, PDF, CSV, and XLSX formats.</p>
<div style="margin:2rem">
<img src="assets/screenshots/savetypes.png"
class="col-lg-12 d-flex flex-column align-items-center">
</div>
<p style="font-family:Roboto ; color:#280135">
By clicking cross icon you can delete the chart and also the same for tile.</p>
<div style="margin:2rem"> <div style="margin:2rem">
<img src="assets/screenshots/3.png" <img src="assets/screenshots/delete.png"
class="col-lg-12 d-flex flex-column align-items-center"> class="col-lg-12 d-flex flex-column align-items-center">
</div> </div>
</div> </div>
@ -390,65 +329,97 @@
<div class="mt-5 col-lg-12 d-flex flex-column align-items-center"> <div class="mt-5 col-lg-12 d-flex flex-column align-items-center">
<h3 class="mt16 mb0" <h3 class="mt16 mb0"
style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96"> style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96">
Creating New Tile Block Select Type Of The Item
</h3> </h3>
<div style="margin:2rem">
<img src="assets/screenshots/additems.png"
class="col-lg-12 d-flex flex-column align-items-center">
</div>
<p style="font-family:Roboto ; color:#280135">
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.</p>
<div style="margin:2rem">
<img src="assets/screenshots/newtile.png"
class="col-lg-12 d-flex flex-column align-items-center">
</div>
<p style="font-family:Roboto ; color:#280135"> <p style="font-family:Roboto ; color:#280135">
When you click the 'Add Block' button on the When opting for "Charts," provide a name for the chart,
dashboard, a new empty tile block will be added. You select a model, operation type, and measured field, along with filters.
can configure this tile block using the button in Additionally, choose the chart type, set its size,
the top right corner. You can also use this button and specify the grouping for the Y-axis.</p>
to edit existing tile blocks.</p>
<div style="margin:2rem"> <div style="margin:2rem">
<img src="assets/screenshots/4.png" <img src="assets/screenshots/newbar.png"
class="col-lg-12 d-flex flex-column align-items-center"> class="col-lg-12 d-flex flex-column align-items-center">
</div> </div>
<p style="font-family:Roboto ; color:#280135">You will </div>
then be redirected to the form view to configure the
tile block. Here, you can add a name to the tile and <div class="mt-5 col-lg-12 d-flex flex-column align-items-center">
specify the model from which records should be <h3 class="mt16 mb0"
displayed. You can choose an operation—such as sum, style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96">
count, or average—to be performed on the data shown Edit Layout
in the field on the dashboard. Additionally, you can </h3>
add a measurement field to determine the value <p style="font-family:Roboto ; color:#280135">
displayed in the tile. There is also an option to You can change the position of the item and resize the item.
set a filter, which will dictate the data shown in
the tile. Finally, in the tile information section,
you can set the tile type, tile icon, icon color,
tile color, and text color.
</p> </p>
<div style="margin:2rem"> <div style="margin:2rem">
<img src="assets/screenshots/5.png" <img src="assets/screenshots/editlayout.png"
class="col-lg-12 d-flex flex-column align-items-center"> class="col-lg-12 d-flex flex-column align-items-center">
</div> </div>
</div>
<div class="mt-5 col-lg-12 d-flex flex-column align-items-center">
<p style="font-family:Roboto ; color:#280135">
By double-clicking the tile, it will redirect to the corresponding tree view.
</p>
<div style="margin:2rem">
<img src="assets/screenshots/redirect.png"
class="col-lg-12 d-flex flex-column align-items-center">
</div>
</div>
<div class="mt-5 col-lg-12 d-flex flex-column align-items-center">
<h3 class="mt16 mb0"
style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96">
Change The Mode
</h3>
<p style="font-family:Roboto ; color:#280135">
Dark mode
</p>
<div style="margin:2rem">
<img src="assets/screenshots/darkmode.png"
class="col-lg-12 d-flex flex-column align-items-center">
</div>
</div> </div>
<div class="mt-5 col-lg-12 d-flex flex-column align-items-center"> <div class="mt-5 col-lg-12 d-flex flex-column align-items-center">
<h3 class="mt16 mb0" <h3 class="mt16 mb0"
style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96"> style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96">
Creating New Chart Block Dashboard Menu
</h3> </h3>
<p style="font-family:Roboto ; color:#280135"> <p style="font-family:Roboto ; color:#280135">
Click the 'Add Graph' button to add a new block. You can Add dashboard menu in any module. Add a name for the menu and select the parent
From here, you can click the configuration button in menu also
the top right corner to make the necessary </p>
configurations for the chart.</p> <div style="margin:2rem">
<img src="assets/screenshots/dashboard_create.png"
class="col-lg-12 d-flex flex-column align-items-center">
</div>
<div style="margin:2rem"> <div style="margin:2rem">
<img src="assets/screenshots/6.png" <img src="assets/screenshots/sales_dashboard.png"
class="col-lg-12 d-flex flex-column align-items-center"> class="col-lg-12 d-flex flex-column align-items-center">
</div> </div>
</div>
<div class="mt-5 col-lg-12 d-flex flex-column align-items-center">
<h3 class="mt16 mb0"
style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96">
Dashboard Block
</h3>
<p style="font-family:Roboto ; color:#280135"> <p style="font-family:Roboto ; color:#280135">
From here, you will be redirected to the form view You can see all the tiles and charts created in the Dashboard Block menu.
to configure the chart. You can set the name,
operation, measurement field, and filter, similar to
the tile block. However, in the block information
section for the chart, you have additional options
to set the block type, chart type, chart size, and
the reference field to display in the charts.
</p> </p>
<div style="margin:2rem"> <div style="margin:2rem">
<img src="assets/screenshots/7.png" <img src="assets/screenshots/blocks.png"
class="col-lg-12 d-flex flex-column align-items-center"> class="col-lg-12 d-flex flex-column align-items-center">
</div> </div>
</div> </div>
@ -456,16 +427,18 @@
<div class="mt-5 col-lg-12 d-flex flex-column align-items-center"> <div class="mt-5 col-lg-12 d-flex flex-column align-items-center">
<h3 class="mt16 mb0" <h3 class="mt16 mb0"
style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96"> style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96">
Blocks Dashboard Theme
</h3> </h3>
<p style="font-family:Roboto ; color:#280135"> <p style="font-family:Roboto ; color:#280135">
By clicking on the 'Blocks' menu from the Dashboard Customize the Dashboard Theme via Configuration > Dashboard Theme.
module, you can see the blocks created in the Then, choose the configured theme for your dashboard.
dashboard. From here, you can configure, delete, or
create new dashboard blocks.
</p> </p>
<div style="margin:2rem"> <div style="margin:2rem">
<img src="assets/screenshots/8.png" <img src="assets/screenshots/themes.png"
class="col-lg-12 d-flex flex-column align-items-center">
</div>
<div style="margin:2rem">
<img src="assets/screenshots/themeselect.png"
class="col-lg-12 d-flex flex-column align-items-center"> class="col-lg-12 d-flex flex-column align-items-center">
</div> </div>
</div> </div>
@ -473,44 +446,46 @@
<div class="mt-5 col-lg-12 d-flex flex-column align-items-center"> <div class="mt-5 col-lg-12 d-flex flex-column align-items-center">
<h3 class="mt16 mb0" <h3 class="mt16 mb0"
style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96"> style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96">
Dynamic Dashboard Print PDF
</h3> </h3>
<p style="font-family:Roboto ; color:#280135"> <p style="font-family:Roboto ; color:#280135">
From the configuration menu, select 'Dashboards' to Print & Mail whole dashboard in PDF format by clicking the PDF or Mail icon.
view the dynamic dashboards that have been created.
Here, you can create, remove, or edit dynamic
dashboards. To create a new dynamic dashboard, click
the 'NEW' button.
</p> </p>
<div style="margin:2rem"> <div style="margin:2rem">
<img src="assets/screenshots/9.png" <img src="assets/screenshots/pdf_and_mail.png"
class="col-lg-12 d-flex flex-column align-items-center">
</div>
<div style="margin:2rem">
<img src="assets/screenshots/pdf.png"
class="col-lg-12 d-flex flex-column align-items-center"> class="col-lg-12 d-flex flex-column align-items-center">
</div> </div>
</div>
<div class="mt-5 col-lg-12 d-flex flex-column align-items-center">
<h3 class="mt16 mb0"
style="mt-3 font-family:Roboto; font-weight:500; font-size:22px; color:#781d96">
Send Mail
</h3>
<p style="font-family:Roboto ; color:#280135"> <p style="font-family:Roboto ; color:#280135">
This is the form view for configuring the dynamic Clicking the Mail icon brings up a wizard,
dashboard. Here, you can set the name of the where you can select users and then click the SEND button.
dashboard menu and specify the parent menu under
which this dashboard menu should appear.
</p> </p>
<div style="margin:2rem"> <div style="margin:2rem">
<img src="assets/screenshots/10.png" <img src="assets/screenshots/email.png"
class="col-lg-12 d-flex flex-column align-items-center"> class="col-lg-12 d-flex flex-column align-items-center">
</div> </div>
<p style="font-family:Roboto ; color:#280135"> <p style="font-family:Roboto ; color:#280135">
From the parent menu, you can see that the new Demo Mail
dynamic dashboard has been created as specified in
the configuration.
</p> </p>
<div style="margin:2rem"> <div style="margin:2rem">
<img src="assets/screenshots/11.png" <img src="assets/screenshots/email_report.png"
class="col-lg-12 d-flex flex-column align-items-center"> class="col-lg-12 d-flex flex-column align-items-center">
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="tab-pane fade " id="Features" role="tabpanel" <div class="tab-pane fade " id="Features" role="tabpanel" aria-labelledby="features">
aria-labelledby="features">
<section class="oe_container pt16 mt16 pb-5"> <section class="oe_container pt16 mt16 pb-5">
<div class="row "> <div class="row ">
@ -528,14 +503,12 @@
</h1> </h1>
<p class="my-1 text-center text-uppercase" <p class="my-1 text-center text-uppercase"
style="letter-spacing:4px !important; color:#74788D !important"> style="letter-spacing:4px !important; color:#74788D !important">
Comprehensive Features of ADVANCED DYNAMIC Comprehensive Features of ODOO DYNAMIC DASHBOARD</p>
DASHBOARD</p>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-4 col-sm-6 mt48" <div class="col-md-4 col-sm-6 mt48" style="padding-bottom: 10px">
style="padding-bottom: 10px">
<div class="bg-white shadow card" <div class="bg-white shadow card"
style="border-radius:10px; padding:5px; height: 100%"> style="border-radius:10px; padding:5px; height: 100%">
<div class="text-center d-flex align-items-center card-body" <div class="text-center d-flex align-items-center card-body"
@ -548,16 +521,14 @@
<h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize" <h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize"
class="mb2 text-left mb-0"> class="mb2 text-left mb-0">
Edit And Configure Charts And Edit And Configure Charts And Tiles.
Tiles
</h5> </h5>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-4 col-sm-6 mt48" <div class="col-md-4 col-sm-6 mt48" style="padding-bottom: 10px">
style="padding-bottom: 10px">
<div class="bg-white shadow card" <div class="bg-white shadow card"
style="border-radius:10px; padding:5px; height: 100%"> style="border-radius:10px; padding:5px; height: 100%">
<div class="text-center d-flex align-items-center card-body" <div class="text-center d-flex align-items-center card-body"
@ -570,16 +541,14 @@
<h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize" <h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize"
class="mb2 text-left mb-0"> class="mb2 text-left mb-0">
Easily Create Dynamic Charts And Easily Create Dynamic Charts And Tiles.
Tiles
</h5> </h5>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-4 col-sm-6 mt48" <div class="col-md-4 col-sm-6 mt48" style="padding-bottom: 10px">
style="padding-bottom: 10px">
<div class="bg-white shadow card" <div class="bg-white shadow card"
style="border-radius:10px; padding:5px; height: 100%"> style="border-radius:10px; padding:5px; height: 100%">
<div class="text-center d-flex align-items-center card-body" <div class="text-center d-flex align-items-center card-body"
@ -592,15 +561,14 @@
<h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize" <h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize"
class="mb2 text-left mb-0"> class="mb2 text-left mb-0">
Multiple way to arrange your dashboard Available Dark Mode And Light Mode.
</h5> </h5>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-4 col-sm-6 mt48" <div class="col-md-4 col-sm-6 mt48" style="padding-bottom: 10px">
style="padding-bottom: 10px">
<div class="bg-white shadow card" <div class="bg-white shadow card"
style="border-radius:10px; padding:5px; height: 100%"> style="border-radius:10px; padding:5px; height: 100%">
<div class="text-center d-flex align-items-center card-body" <div class="text-center d-flex align-items-center card-body"
@ -613,16 +581,14 @@
<h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize" <h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize"
class="mb2 text-left mb-0"> class="mb2 text-left mb-0">
Create a Dynamic Create A Odoo Dynamic Dashboard Menu In Any Model.
Dashboard Menu In Any Model
</h5> </h5>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-4 col-sm-6 mt48" <div class="col-md-4 col-sm-6 mt48" style="padding-bottom: 10px">
style="padding-bottom: 10px">
<div class="bg-white shadow card" <div class="bg-white shadow card"
style="border-radius:10px; padding:5px; height: 100%"> style="border-radius:10px; padding:5px; height: 100%">
<div class="text-center d-flex align-items-center card-body" <div class="text-center d-flex align-items-center card-body"
@ -635,15 +601,14 @@
<h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize" <h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize"
class="mb2 text-left mb-0"> class="mb2 text-left mb-0">
Charts Configuration Charts Can Export Into Image, PDF,XLSX And CSV.
</h5> </h5>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-4 col-sm-6 mt48" <div class="col-md-4 col-sm-6 mt48" style="padding-bottom: 10px">
style="padding-bottom: 10px">
<div class="bg-white shadow card" <div class="bg-white shadow card"
style="border-radius:10px; padding:5px; height: 100%"> style="border-radius:10px; padding:5px; height: 100%">
<div class="text-center d-flex align-items-center card-body" <div class="text-center d-flex align-items-center card-body"
@ -656,7 +621,7 @@
<h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize" <h5 style="margin-bottom: 0px !important; color:#0e1e40; font-size: 18px; text-transform:capitalize"
class="mb2 text-left mb-0"> class="mb2 text-left mb-0">
Tiles Configuration Drag And Resize The Chart And Tile.
</h5> </h5>
</div> </div>
</div> </div>
@ -668,10 +633,8 @@
</section> </section>
</div> </div>
<div class="tab-pane fade " id="ReleaseNotes" role="tabpanel" <div class="tab-pane fade " id="ReleaseNotes" role="tabpanel" aria-labelledby="video-1">
aria-labelledby="video-1"> <div class="row" style="padding:4rem 2.5rem 0 !important; background-color:#fff !important">
<div class="row"
style="padding:4rem 2.5rem 0 !important; background-color:#fff !important">
<div class="col-lg-12 d-flex flex-column align-items-center"> <div class="col-lg-12 d-flex flex-column align-items-center">
<h1 class="text-center text-uppercase" <h1 class="text-center text-uppercase"
style="font-family:Montserrat, 'sans-serif' !important; font-size:40px !important; color:#791d97; font-weight:900 !important"> style="font-family:Montserrat, 'sans-serif' !important; font-size:40px !important; color:#791d97; font-weight:900 !important">
@ -686,9 +649,8 @@
<div class="relese-note w-100 pt-3 pb-2 pl-3 mb-4 pr-3 float-left" <div class="relese-note w-100 pt-3 pb-2 pl-3 mb-4 pr-3 float-left"
style="border:1px solid #ccc; box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75);"> style="border:1px solid #ccc; box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75);">
<p><b> Version 17.0.1 I<span <p><b> Version 17.0.1 I<span style="color:#3363c1; font-size: 14px;"> Released
style="color:#3363c1; font-size: 14px;"> Released on : 18th May 2024</span></b>
on : 11th May 2024</span></b>
</p> </p>
<p> Initial commit for <p> Initial commit for
odoo_dynamic_dashboard </p> odoo_dynamic_dashboard </p>
@ -705,7 +667,6 @@
</div> </div>
</div> </div>
<!-- New section --> <!-- New section -->
<section style="margin-top:140px;"> <section style="margin-top:140px;">
@ -720,71 +681,57 @@
<!-- The slideshow --> <!-- The slideshow -->
<div class="carousel-inner" style="padding:30px"> <div class="carousel-inner" style="padding:30px">
<div class="carousel-item" style="min-height:198.656px"> <div class="carousel-item" style="min-height:198.656px">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
style="float:left">
<a href="https://apps.odoo.com/apps/modules/17.0/base_accounting_kit/" <a href="https://apps.odoo.com/apps/modules/17.0/base_accounting_kit/"
target="_blank"> target="_blank">
<div style="border-radius:10px"> <div style="border-radius:10px">
<img class="img img-responsive center-block" <img class="img img-responsive center-block" style="border-radius:0px"
style="border-radius:0px"
src="assets/modules/1.gif"> src="assets/modules/1.gif">
</div> </div>
</a> </a>
</div> </div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
style="float:left">
<a href="https://apps.odoo.com/apps/modules/17.0/employee_dynamic_fields/" <a href="https://apps.odoo.com/apps/modules/17.0/employee_dynamic_fields/"
target="_blank"> target="_blank">
<div style="border-radius:10px"> <div style="border-radius:10px">
<img class="img img-responsive center-block" <img class="img img-responsive center-block" style="border-radius:0px"
style="border-radius:0px"
src="assets/modules/2.jpg"> src="assets/modules/2.jpg">
</div> </div>
</a> </a>
</div> </div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
style="float:left"> <a href="https://apps.odoo.com/apps/modules/17.0/product_barcode/" target="_blank">
<a href="https://apps.odoo.com/apps/modules/17.0/product_barcode/"
target="_blank">
<div style="border-radius:10px"> <div style="border-radius:10px">
<img class="img img-responsive center-block" <img class="img img-responsive center-block" style="border-radius:0px"
style="border-radius:0px"
src="assets/modules/5.png"> src="assets/modules/5.png">
</div> </div>
</a> </a>
</div> </div>
</div> </div>
<div class="carousel-item active" <div class="carousel-item active" style="min-height:198.656px">
style="min-height:198.656px"> <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/17.0/invoice_format_editor/" <a href="https://apps.odoo.com/apps/modules/17.0/invoice_format_editor/"
target="_blank"> target="_blank">
<div style="border-radius:10px"> <div style="border-radius:10px">
<img class="img img-responsive center-block" <img class="img img-responsive center-block" style="border-radius:0px"
style="border-radius:0px"
src="assets/modules/3.png"> src="assets/modules/3.png">
</div> </div>
</a> </a>
</div> </div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
style="float:left">
<a href="https://apps.odoo.com/apps/modules/17.0/login_user_detail/" <a href="https://apps.odoo.com/apps/modules/17.0/login_user_detail/"
target="_blank"> target="_blank">
<div style="border-radius:10px"> <div style="border-radius:10px">
<img class="img img-responsive center-block" <img class="img img-responsive center-block" style="border-radius:0px"
style="border-radius:0px"
src="assets/modules/4.png"> src="assets/modules/4.png">
</div> </div>
</a> </a>
</div> </div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
style="float:left">
<a href="https://apps.odoo.com/apps/modules/17.0/whatsapp_redirect/" <a href="https://apps.odoo.com/apps/modules/17.0/whatsapp_redirect/"
target="_blank"> target="_blank">
<div style="border-radius:10px"> <div style="border-radius:10px">
<img class="img img-responsive center-block" <img class="img img-responsive center-block" style="border-radius:0px"
style="border-radius:0px"
src="assets/modules/6.jpg"> src="assets/modules/6.jpg">
</div> </div>
</a> </a>
@ -792,16 +739,12 @@
</div> </div>
</div> </div>
<!-- Left and right controls --> <!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo1" <a class="carousel-control-prev" href="#demo1" data-slide="prev" style="width:35px; color:#000">
data-slide="prev" style="width:35px; color:#000"> <span class="carousel-control-prev-icon"><i class="fa fa-chevron-left"
<span class="carousel-control-prev-icon"><i
class="fa fa-chevron-left"
style="font-size:24px"></i></span> style="font-size:24px"></i></span>
</a> <a class="carousel-control-next" href="#demo1" </a> <a class="carousel-control-next" href="#demo1" data-slide="next"
data-slide="next"
style="width:35px; color:#000"> style="width:35px; color:#000">
<span class="carousel-control-next-icon"><i <span class="carousel-control-next-icon"><i class="fa fa-chevron-right"
class="fa fa-chevron-right"
style="font-size:24px"></i></span> style="font-size:24px"></i></span>
</a> </a>
</div> </div>
@ -812,14 +755,12 @@
<section> <section>
<div class="d-flex align-items-center" <div class="d-flex align-items-center" style="border-bottom:2px solid #714B67; padding:15px 0px">
style="border-bottom:2px solid #714B67; padding:15px 0px">
<div class="d-flex justify-content-center align-items-center mr-2" <div class="d-flex justify-content-center align-items-center mr-2"
style="background-color:#F5F5F5; border-radius:0px; width:40px; height:40px"> style="background-color:#F5F5F5; border-radius:0px; width:40px; height:40px">
<img src="//apps.odoocdn.com/apps/assets/16.0/export_stockinfo_xls/assets/misc/star.png?aedef96"> <img src="//apps.odoocdn.com/apps/assets/16.0/export_stockinfo_xls/assets/misc/star.png?aedef96">
</div> </div>
<h2 class="mt-2" <h2 class="mt-2" style="font-family:'Montserrat', sans-serif; font-size:24px; font-weight:bold">
style="font-family:'Montserrat', sans-serif; font-size:24px; font-weight:bold">
Our Services Our Services
</h2> </h2>
</div> </div>
@ -1071,12 +1012,10 @@
<hr> <hr>
</div> </div>
<div class="col-sm-12 col-md-4"> <div class="col-sm-12 col-md-4">
<div class="d-flex align-items-center" <div class="d-flex align-items-center" style="background-color:#F6F8F9; padding:22px">
style="background-color:#F6F8F9; padding:22px">
<div class="mr-4 d-flex justify-content-center align-items-center" <div class="mr-4 d-flex justify-content-center align-items-center"
style="background-color:#714B67; height:70px; width:70px"> style="background-color:#714B67; height:70px; width:70px">
<img src="assets/icons/support.png" height="48" <img src="assets/icons/support.png" height="48" width="48" style="width:42px; height:42px">
width="48" style="width:42px; height:42px">
</div> </div>
<div> <div>
<h4>Need Help?</h4> <h4>Need Help?</h4>
@ -1091,12 +1030,10 @@
</div> </div>
</div> </div>
<div class="col-sm-12 col-md-4"> <div class="col-sm-12 col-md-4">
<div class="d-flex align-items-center" <div class="d-flex align-items-center" style="background-color:#F6F8F9; padding:30px">
style="background-color:#F6F8F9; padding:30px">
<div class="mr-4 d-flex justify-content-center align-items-center" <div class="mr-4 d-flex justify-content-center align-items-center"
style="background-color:#2AC44D; height:70px; width:70px"> style="background-color:#2AC44D; height:70px; width:70px">
<img src="assets/icons/whatsapp.png" height="52" <img src="assets/icons/whatsapp.png" height="52" width="52" style="width:52px; height:52px">
width="52" style="width:52px; height:52px">
</div> </div>
<div> <div>
<h4>WhatsApp</h4> <h4>WhatsApp</h4>
@ -1113,12 +1050,10 @@
<div class="col-sm-12 col-md-4"> <div class="col-sm-12 col-md-4">
<div class="d-flex align-items-center" <div class="d-flex align-items-center" style="background-color:#F6F8F9; padding:30px">
style="background-color:#F6F8F9; padding:30px">
<div class="mr-4 d-flex justify-content-center align-items-center" <div class="mr-4 d-flex justify-content-center align-items-center"
style="background-color:#03a9f4; height:70px; width:70px"> style="background-color:#03a9f4; height:70px; width:70px">
<img src="assets/icons/skype.png" height="52" width="52" <img src="assets/icons/skype.png" height="52" width="52" style="width:52px; height:52px">
style="width:52px; height:52px">
</div> </div>
<div> <div>
<h4>Skype</h4> <h4>Skype</h4>
@ -1136,15 +1071,11 @@
</div> </div>
<!-- Footer --> <!-- Footer -->
<section class="oe_container" <section class="oe_container" style="padding: 2rem 3rem 1rem; background-color: #fff !important;">
style="padding: 2rem 3rem 1rem; background-color: #fff !important;"> <div class="row" style="max-width:1540px; margin: 0 auto; margin-right: 3rem; ">
<div class="row"
style="max-width:1540px; margin: 0 auto; margin-right: 3rem; ">
<!-- Logo --> <!-- Logo -->
<div class="col-lg-12 d-flex justify-content-center align-items-center" <div class="col-lg-12 d-flex justify-content-center align-items-center" style="margin-top: 3rem;">
style="margin-top: 3rem;"> <img src="https://www.cybrosys.com/images/logo.png" width="200px" height="auto" />
<img src="https://www.cybrosys.com/images/logo.png"
width="200px" height="auto"/>
</div> </div>
<!-- End of Logo --> <!-- End of Logo -->
</div> </div>

4
odoo_dynamic_dashboard/static/lib/js/interactjs.js

File diff suppressed because one or more lines are too long

344
odoo_dynamic_dashboard/static/src/css/dynamic_dashboard.css

@ -0,0 +1,344 @@
.row {
margin: 1rem;
}
.resize-drag {
border-radius: 8px;
padding: 20px;
margin: 1rem;
color: white;
font-size: 20px;
font-family: sans-serif;
touch-action: none;
box-sizing: border-box;
}
.theme {
position: absolute;
right: 60.5em;
padding-top:inherit;
}
.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;
}
.dashboard_pdf{
position: absolute;
right: 26.5em;
padding-top:inherit;
}
.dashboard_mail{
position: absolute;
right: 24.5em;
padding-top:inherit;
}
.resize-drag {
position: relative !important;
font-size: 100%;
overflow:hidden;
}
h2, h3 {
font-size: 150% !important;
}
.container {
max-width: 100% !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;
}
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;
}
.modal-header .btn-close {
display: none;
}
.modal-title{
margin-inline-end: auto;
}
.theme-text {
font-family: Arial, sans-serif;
font-size: 1em;
opacity: 70%;
}
.search-group {
position: absolute;
right: 1.7em;
}
.block_setting {
position: absolute;
top: 7px;
left: 10px;
}
.block_delete {
position: absolute;
top: 7px;
right: 10px;
}
.tile_edit {
color: #d2d2d2;
}
.block_edit {
color: #3f3f3f;
}
.block_setting, .block_delete, .block_image, .block_pdf, .block_xlsx, .block_csv, .block_export {
display: none;
}
.resize-drag:hover .block_setting,
.resize-drag:hover .block_xlsx,
.resize-drag:hover .block_csv,
.resize-drag:hover .block_image,
.resize-drag:hover .block_pdf,
.resize-drag:hover .block_export,
.resize-drag:hover .block_delete {
display: block;
}
.chart-edit {
position: absolute;
top: 0px;
left: 3px;
font-size: 16px;
}
.chart-image {
position: absolute;
top: 3px;
right: 106px;
font-size: 16px;
}
.chart-pdf {
position: absolute;
top: 3px;
right: 82px;
font-size: 16px;
}
.chart-csv {
position: absolute;
top: 3px;
right: 58px;
font-size: 16px;
}
.chart-xlsx {
position: absolute;
top: 6px;
right: 34px;
font-size: 15px;
}
.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 {
top:10px;
padding: 1.2rem 0 2.6rem 0 !important;
border-bottom: 1px solid #3f3f3f1a !important;
color: #444444;
background-color: #ffffff !important;
}
.theme_icon:hover {
text-shadow: 0px 0px 5px #9388ff;
}
.navbar-style {
margin-top: 1.2em;
border-radius: 0.5em;
}
.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;
}
.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;
}
.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;
}
}

54
odoo_dynamic_dashboard/static/src/js/DynamicDashboard.js

@ -1,54 +0,0 @@
/** @odoo-module */
import { registry} from '@web/core/registry';
import { DynamicDashboardTile} from './DynamicDashboardTile'
import { DynamicDashboardChart} from './DynamicDashboardChart'
import { useService } from "@web/core/utils/hooks";
const { Component, mount} = owl
export class DynamicDashboard extends Component {
setup(){
this.action = useService("action");
this.rpc = this.env.services.rpc
this.renderDashboard()
}
async renderDashboard() {
const action = this.action
const rpc = this.rpc
await this.rpc('/get/values', {'action_id': this.props.actionId}).then(function(response){
if ($('.o_dynamic_dashboard')[0]){
for (let i = 0; i < response.length; i++) {
if (response[i].type === 'tile'){
mount(DynamicDashboardTile, $('.o_dynamic_tile')[0], { props: {
widget: response[i], doAction: action
}});
}
else{
mount(DynamicDashboardChart, $('.o_dynamic_graph')[0], { props: {
widget: response[i], doAction: action, rpc: rpc
}});
}
}
}
})
}
async _onClick_add_block(e){
var self = this;
var self_props = this.props;
var self_env = self.env;
var type = $(e.target).attr('data-type');
await this.rpc('/create/tile',{'type' : type, 'action_id': self.props.actionId}).then(function(response){
if(response['type'] == 'tile'){
mount(DynamicDashboardTile, $('.o_dynamic_tile')[0], { props: {
widget: response, doAction: self.action
}});
}
else{
mount(DynamicDashboardChart, $('.o_dynamic_graph')[0], { props: {
widget: response, doAction: self.action
}});
}
})
}
}
DynamicDashboard.template = "owl.dynamic_dashboard"
registry.category("actions").add("owl.dynamic_dashboard", DynamicDashboard)

82
odoo_dynamic_dashboard/static/src/js/DynamicDashboardChart.js

@ -1,82 +0,0 @@
/** @odoo-module */
import { registry} from '@web/core/registry';
import { loadJS} from '@web/core/assets';
import { getColor } from "@web/core/colors/colors";
const { Component, xml, onWillStart, useRef, onMounted } = owl
export class DynamicDashboardChart extends Component {
setup() {
this.doAction = this.props.doAction.doAction
this.chartRef = useRef("chart")
onWillStart(async () => {
await loadJS("https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js")
})
onMounted(()=> this.renderChart())
}
renderChart(){
if (this.props.widget.graph_type){
const x_axis = this.props.widget.x_axis
const y_axis = this.props.widget.y_axis
const data = []
for (let i = 0; i < x_axis.length; i++) {
const value = { key: x_axis[i], value: y_axis[i] }
data.push(value);
}
new Chart(
this.chartRef.el,
{
type: this.props.widget.graph_type || 'bar',
data: {
labels: data.map(row => row.key),
datasets: [
{
label: this.props.widget.measured_field,
data: data.map(row => row.value),
backgroundColor: data.map((_, index) => getColor(index)),
hoverOffset : 4
}
]
},
}
);
}
}
async getConfiguration(){
var id = this.props.widget.id
await this.doAction({
type: 'ir.actions.act_window',
res_model: 'dashboard.block',
res_id: id,
view_mode: 'form',
views: [[false, "form"]]
});
}
}
DynamicDashboardChart.template = xml `
<div style="padding-bottom:30px" t-att-class="this.props.widget.cols +' col-4 block'" t-att-data-id="this.props.widget.id">
<div class="card">
<div class="card-header">
<div class="row">
<div class="col">
<h3><t t-esc="this.props.widget.name"/></h3>
</div>
<div class="col">
<div style="float:right;"><i title="Configuration" class="fa fa-cog block_setting fa-2x cursor-pointer" t-on-click="getConfiguration"/></div>
</div>
</div>
</div>
<div class="card-body" id="in_ex_body_hide">
<div class="row">
<div class="col-md-12 chart_canvas">
<div id="chart_canvas">
<canvas t-ref="chart"/>
</div>
</div>
</div>
</div>
</div>
</div>
`

52
odoo_dynamic_dashboard/static/src/js/DynamicDashboardTile.js

@ -1,52 +0,0 @@
/** @odoo-module */
import { registry} from '@web/core/registry';
const { Component, xml } = owl
export class DynamicDashboardTile extends Component {
setup() {
super.setup(...arguments);
this.action = this.props.doAction
}
async getRecord() {
var model_name = this.props.widget.model_name
if (model_name){
await this.action.doAction({
type: 'ir.actions.act_window',
res_model: model_name,
view_mode: 'tree',
views: [[false, "tree"]],
domain: this.props.widget.domain,
});
}
}
async getConfiguration() {
var id = this.props.widget.id
await this.action.doAction({
type: 'ir.actions.act_window',
res_model: 'dashboard.block',
res_id: id,
view_mode: 'form',
views: [[false, "form"]]
});
}
}
DynamicDashboardTile.template = xml`<div class="col-sm-12 col-md-12 col-lg-3 tile block"
t-att-data-id="this.props.widget.id">
<div draggable="true" t-att-style="'background: ' + this.props.widget.tile_color " class="tile-container d-flex justify-content-around align-items-center position-relative w-100 h-auto my-3">
<a t-on-click="getConfiguration" class="block_setting position-absolute tile-container__setting-icon cursor-pointer">
<i class="fa fa-cog"></i>
</a>
<div t-on-click="getRecord" class="d-flex cursor-pointer">
<div t-att-style="'color: ' + this.props.widget.icon_color " class="tile-container__icon-container bg-white d-flex justify-content-center align-items-center">
<i t-att-class="this.props.widget.icon" aria-hidden="true"></i>
</div>
<div class="tile-container__status-container" t-att-style="'color: ' + this.props.widget.text_color ">
<h2 class="status-container__title" t-att-style="'color: ' + this.props.widget.text_color "><t t-esc="this.props.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="'color: ' + this.props.widget.text_color "><t t-esc="this.props.widget.value"/></h3>
</div>
</div>
</div>
</div>
</div>`

391
odoo_dynamic_dashboard/static/src/js/dynamic_dashboard.js

@ -0,0 +1,391 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { loadJS } from '@web/core/assets';
import { DynamicDashboardTile} from './dynamic_dashboard_tile';
import { DynamicDashboardChart} from './dynamic_dashboard_chart';
import { useService } from "@web/core/utils/hooks";
const { Component, useRef, mount, onWillStart, onMounted} = owl;
export class OdooDynamicDashboard extends Component {
// Setup function to run when the template of the class OdooDynamicDashboard renders
setup() {
this.ThemeSelector = useRef('ThemeSelector');
this.action = useService("action");
this.orm = useService("orm");
this.dialog = useService("dialog");
this.actionId = this.props.actionId
this.rpc = useService("rpc");
onWillStart(async () => {
await loadJS("https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js")
await loadJS("https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js")
await loadJS("https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.9.3/html2pdf.bundle.min.js")
})
onMounted(()=>{
this.renderDashboard();
})
}
onChangeTheme(){
/* Function for changing color of the theme of the dashboard */
$(".container").attr('style', this.ThemeSelector.el.value + 'min-height:-webkit-fill-available;')
}
ResizeDrag() {
/* Function for resizing and dragging the div resize-drag */
$('.items .resize-drag').each(function(index, element) {
interact(element).resizable({
edges: { left: true, right: true, bottom: true, top: true },
listeners: {
move (event) {
var target = event.target
var x = (parseFloat(target.getAttribute('data-x')) || 0)
var y = (parseFloat(target.getAttribute('data-y')) || 0)
// update the element's style
target.style.width = event.rect.width + 'px'
target.style.height = event.rect.height + 'px'
// translate when resizing from top or left edges
x += event.deltaRect.left
y += event.deltaRect.top
}
},
modifiers: [
// keep the edges inside the parent
interact.modifiers.restrictEdges({
outer: 'parent'
}),
// minimum size
interact.modifiers.restrictSize({
min: { width: 100, height: 50 }
})
],
inertia: true
}).draggable({
listeners: {move: dragMoveListener},
inertia: true,
modifiers: [
interact.modifiers.restrictRect({
restriction: 'parent',
endOnly: true
})
]
})
function dragMoveListener (event) {
var target = event.target
// keep the dragged position in the data-x/data-y attributes
var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx
var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy
// translate the element
target.style.transform = 'translate(' + x + 'px, ' + y + 'px)'
// update the posiion attributes
target.setAttribute('data-x', x)
target.setAttribute('data-y', y)
}
// this function is used later in the resizing
window.dragMoveListener = dragMoveListener
});
}
async renderDashboard(){
/* Function for rendering the dashboard */
var self = this;
$("#save_layout").hide();
await this.orm.call('dashboard.theme', 'get_records', [[]]).then(function (response) {
response.forEach((ev) => {
const options = document.createElement("option");
options.value = ev.style;
options.text = ev.name;
self.ThemeSelector.el.append(options)
});
})
await this.orm.call("dashboard.block", "get_dashboard_vals", [[], this.actionId]).then( function (response){
for (let i = 0; i < response.length; i++) {
if (response[i].type === 'tile'){
mount(DynamicDashboardTile, $('.items')[0], { props: {
widget: response[i], doAction: self.action, dialog:self.dialog, orm: self.orm
}});
}
else{
mount(DynamicDashboardChart, $('.items')[0], { props: {
widget: response[i], doAction: self.action, rpc: self.rpc, dialog:self.dialog, orm: self.orm
}});
}
}
})
}
editLayout(ev) {
/* Function for editing the layout , it enables resizing and dragging functionality */
$('.items .resize-drag').each(function(index, element) {
interact(element).draggable(true)
interact(element).resizable(true)
});
ev.stopPropagation();
ev.preventDefault();
$("#edit_layout").hide();
$("#save_layout").show();
this.ResizeDrag()
}
saveLayout(ev){
/* Function for saving the layout */
var self = this;
ev.stopPropagation();
ev.preventDefault();
$("#edit_layout").show();
$("#save_layout").hide();
var data_list = []
$('.items .resize-drag').each(function(index, element) {
interact(element).draggable(false)
interact(element).resizable(false)
data_list.push({
'id' : element.dataset['id'],
'data-x': element.dataset['x'],
'data-y': element.dataset['y'],
'height': element.clientHeight,
'width': element.clientWidth,
})
});
self.orm.call('dashboard.block','get_save_layout', [[], data_list]).then( function (response){
window.location.reload();
});
}
changeViewMode(ev){
/* Function for changing the mode of the view */
ev.stopPropagation();
ev.preventDefault();
const currentMode = $(".mode").attr("mode");
if (currentMode == "light"){
$('.theme').attr('style','display: none;')
$(".container").attr('style', 'background-color: #383E45;min-height:-webkit-fill-available; !important')
$(".mode").attr("mode", "dark")
$(".bi-moon-stars-fill").attr('class', 'bi bi-cloud-sun-fill view-mode-icon')
$(".bi-cloud-sun-fill").attr('style', 'color:black;margin-left:10px;')
$(".mode").attr('style','display: none !important');
$("#search-input-chart").attr('style', 'background-color: white; !important')
$("#search-button").attr('style', 'background-color: #BB86FC; !important')
$("#dropdownMenuButton").attr('style', 'background-color: #03DAC5;margin-top:-4px; !important')
$("#text_add").attr('style', 'color: black; !important')
$(".date-label").attr('style', 'color: black;font-family:monospace; !important')
$(".block_setting").attr('style', 'color: white; !important')
$(".block_delete").attr('style', 'color: white; !important')
$(".block_image").attr('style', 'color: #03DAC5; !important')
$(".block_pdf").attr('style', 'color: #03DAC5; !important')
$(".block_csv").attr('style', 'color: #03DAC5; !important')
$(".block_xlsx").attr('style', 'color: #03DAC5; !important')
}
else {
$('.theme').attr('style','display: block;')
$(".container").attr('style', this.ThemeSelector.el.value + 'min-height:-webkit-fill-available;')
$(".mode").attr("mode", "light")
$(".bi-cloud-sun-fill").attr('class', 'bi bi-moon-stars-fill view-mode-icon')
$(".view-mode-icon").attr('style', 'color:black;margin-left:10px; !important')
$(".mode").attr('style','display: none !important');
$(".mode").attr('style','color: white !important');
$("#search-input-chart").attr('style', 'background-color: none; !important')
$("#search-button").attr('style', 'background-color: none; !important')
$("#dropdownMenuButton").attr('style', 'background-color: none;margin-top:-4px; !important')
$("#text_add").attr('style', 'color: white; !important')
$(".date-label").attr('style', 'color: black; !important;font-family:monospace; !important')
$(".block_setting").attr('style', 'color: black; !important')
$(".block_delete").attr('style', 'color: black; !important')
$(".block_image").attr('style', 'color: black; !important')
$(".block_pdf").attr('style', 'color: black; !important')
$(".block_csv").attr('style', 'color: black; !important')
$(".block_xlsx").attr('style', 'color: black; !important')
}
}
onClickAdd(event){
/* For enabling the toggle button */
event.stopPropagation();
event.preventDefault();
$(".dropdown-addblock").toggle()
}
onClickAddItem(event){
/* Function for adding tiles and charts */
event.stopPropagation();
event.preventDefault();
self = this;
var type = event.target.getAttribute('data-type');
if (type == 'graph'){
var chart_type = event.target.getAttribute('data-chart_type');
}
if (type == 'tile'){
var randomColor = '#' + ('000000' + Math.floor(Math.random() * 16777216).toString(16)).slice(-6);
this.action.doAction({
type: 'ir.actions.act_window',
res_model: 'dashboard.block',
view_mode: 'form',
views: [[false, 'form']],
context: {
'form_view_initial_mode': 'edit',
'default_name': 'New Tile',
'default_type': type,
'default_height': '155px',
'default_width': '300px',
'default_tile_color': randomColor,
'default_text_color': '#FFFFFF',
'default_val_color': '#F3F3F3',
'default_fa_icon': 'fa fa-bar-chart',
'default_client_action_id': parseInt(self.actionId)
}
})
}
else{
this.action.doAction({
type: 'ir.actions.act_window',
res_model: 'dashboard.block',
view_mode: 'form',
views: [[false, 'form']],
context: {
'form_view_initial_mode': 'edit',
'default_name': 'New ' + chart_type,
'default_type': type,
'default_height': '565px',
'default_width': '588px',
'default_graph_type': chart_type,
'default_fa_icon': 'fa fa-bar-chart',
'default_client_action_id': parseInt(self.actionId)
},
})
}
}
dateFilter(){
/* Function for filtering the data based on the creation date */
$(".items").empty();
var start_date = $("#start-date").val();
var end_date = $("#end-date").val();
var self = this;
if (!start_date){
start_date = "null"
}
if (!end_date){
end_date = "null"
}
this.orm.call("dashboard.block", "get_dashboard_vals", [[], this.actionId, start_date, end_date]).then( function (response){
for (let i = 0; i < response.length; i++) {
if (response[i].type === 'tile'){
mount(DynamicDashboardTile, $('.items')[0], { props: {
widget: response[i], doAction: self.action, dialog:self.dialog, orm: self.orm
}});
}
else{
mount(DynamicDashboardChart, $('.items')[0], { props: {
widget: response[i], doAction: self.action, rpc: self.rpc, dialog:self.dialog, orm: self.orm
}});
}
}
})
}
async clickSearch(){
/* Function for searching the blocks with their names */
var input = $("#search-input-chart").val();
await this.rpc('/custom_dashboard/search_input_chart', {'search_input': input}).then(function (response) {
var blocks = $(".items .resize-drag");
blocks.each(function(index, element){
var dataId = $(element).data('id');
if (response.includes(dataId)){
$(element).css("visibility", "visible");
}
else{
$(element).css("visibility", "hidden");
}
})
})
}
showViewMode(ev){
/* Function for showing the mode text */
const currentMode = $(".mode").attr("mode");
if (currentMode == "light"){
$(".mode").text("Dark Mode")
$(".mode").attr('style','display: inline-block !important; color: black !important');
}
else{
$(".mode").text("Light Mode")
$(".mode").attr('style','display: inline-block !important; color: black !important');
}
}
hideViewMode(ev){
/* Function for hiding the mode text */
$(".mode").fadeOut(2000);
}
clearSearch(){
/* Function for clearing the search input */
$("#search-input-chart").val('');
var blocks = $(".items .resize-drag");
blocks.each(function(index, element){
$(element).css("visibility", "visible");
})
}
async printPdf() {
/* Function for printing whole dashboard in pdf format */
var elements = $('.items .resize-drag')
var newElement = document.createElement('div');
newElement.className = 'pdf';
elements.each(function(index, elem){
newElement.appendChild(elem);
});
for (var x=0; x< $(newElement)[0].children.length; x++){
$($(newElement)[0].children[x])[0].style.transform = ""
}
var opt = {
margin: 0.3,
filename: 'Dashboard.pdf',
image: { type: 'jpeg', quality: 1 },
html2canvas: { scale: 1 },
jsPDF: { unit: 'mm', format: 'a3', orientation: 'portrait' }
};
html2pdf().set(opt).from(newElement).save().then(()=>{
window.location.reload()
})
}
async createPDF(){
/* Function for getting pdf data in string format */
var elements = $('.items .resize-drag')
var newElement = document.createElement('div');
newElement.className = 'pdf';
elements.each(function(index, elem){
newElement.appendChild(elem);
});
for (var x=0; x< $(newElement)[0].children.length; x++){
$($(newElement)[0].children[x])[0].style.transform = ""
}
var opt = {
margin: 0.3,
filename: 'Dashboard.pdf',
image: { type: 'jpeg', quality: 1 },
html2canvas: { scale: 1 },
jsPDF: { unit: 'mm', format: 'a3', orientation: 'portrait' }
};
var pdf = html2pdf().set(opt).from(newElement).toPdf()
var pdfOutput = await pdf.output('datauristring');
console.log(pdfOutput)
return pdfOutput
}
async sendMail(){
/* Function for creating pdf and sending mail to the selected users */
/* This function calls the createPDF() function and returns the pdf datas */
var created_pdf = await this.createPDF();
var base64code = created_pdf.split(',')[1];
this.action.doAction({
type: 'ir.actions.act_window',
name: 'SEND MAIL',
res_model: 'dashboard.mail',
view_mode: 'form',
views: [[false, 'form']],
target: 'new',
context: {
'default_base64code': base64code,
}
})
}
}
OdooDynamicDashboard.template = "owl.OdooDynamicDashboard"
registry.category("actions").add("OdooDynamicDashboard", OdooDynamicDashboard)

215
odoo_dynamic_dashboard/static/src/js/dynamic_dashboard_chart.js

@ -0,0 +1,215 @@
/** @odoo-module **/
import { loadJS } from '@web/core/assets';
import { getColor } from "@web/core/colors/colors";
import { _t } from "@web/core/l10n/translation";
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
const { Component, xml, onWillStart, useRef, onMounted } = owl
export class DynamicDashboardChart extends Component {
// Setup function of the class DynamicDashboardChart
setup() {
this.doAction = this.props.doAction.doAction;
this.chartRef = useRef("chart");
this.dialog = this.props.dialog;
onWillStart(async () => {
await loadJS("https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js")
await loadJS("https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js")
await loadJS("https://cdn.jsdelivr.net/npm/exceljs@4.4.0/dist/exceljs.min.js")
})
onMounted(()=> this.renderChart())
}
// Function to export the chart in pdf, image, xlsx and csv format
exportItem(ev){
ev.stopPropagation();
ev.preventDefault();
var type = $(ev.currentTarget).attr('data-type');
var canvas = $($($(ev.currentTarget)[0].offsetParent)[0].children[0].lastChild).find("#canvas")[0]
var dataTitle = $(canvas).attr('data-title')
var bgCanvas = document.createElement("canvas");
bgCanvas.width = canvas.width;
bgCanvas.height = canvas.height;
var bgCtx = bgCanvas.getContext("2d");
bgCtx.fillStyle = "white";
bgCtx.fillRect(0, 0, canvas.width, canvas.height);
bgCtx.drawImage(canvas, 0, 0);
var imgData = bgCanvas.toDataURL("image/png");
if (type === 'png') {
var downloadLink = document.createElement('a');
downloadLink.href = imgData;
downloadLink.download = `${dataTitle}.png`;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
if (type === 'pdf') {
var pdf = new jsPDF();
pdf.addImage(imgData, 'PNG', 0, 0);
pdf.save(`${dataTitle}.pdf`);
}
if (type === 'xlsx'){
var rows = [];
var items = $('.resize-drag');
for (let i = 0; i < items.length; i++) {
if ($(items[i]).attr('data-id') === $(ev.currentTarget).attr('data-id')) {
rows.push(this.props.widget.x_axis);
rows.push(this.props.widget.y_axis);
}
}
// Prepare the workbook
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('My Sheet');
for(let i = 0; i < rows.length; i++){
worksheet.addRow(rows[i]);
}
const image = workbook.addImage({
base64: imgData,
extension: 'png',
});
worksheet.addImage(image, {
tl: { col: 0, row: 4 },
ext: { width: canvas.width, height: canvas.height }
});
// Save workbook to a file
workbook.xlsx.writeBuffer()
.then((buffer) => {
// Create a Blob object from the buffer
let blob = new Blob([buffer], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
let link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.setAttribute("download", `${dataTitle}.xlsx`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
})
}
if (type === 'csv') {
var rows = [];
var items = $('.resize-drag')
for (let i = 0; i < items.length; i++) {
if ($(items[i]).attr('data-id') === $(ev.currentTarget).attr('data-id')) {
rows.push(this.props.widget.x_axis);
rows.push(this.props.widget.y_axis);
}
}
let csvContent = "data:text/csv;charset=utf-8,";
rows.forEach(function (rowArray) {
let row = rowArray.join(",");
csvContent += row + "\r\n";
});
var link = document.createElement("a");
link.setAttribute("href", encodeURI(csvContent));
link.setAttribute("download", `${dataTitle}.csv`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
// Function to get the configuration of the chart
async getConfiguration(ev){
ev.stopPropagation();
ev.preventDefault();
var id = this.props.widget.id
await this.doAction({
type: 'ir.actions.act_window',
res_model: 'dashboard.block',
res_id: id,
view_mode: 'form',
views: [[false, "form"]]
});
}
// Function to remove the chart
async removeTile(ev){
ev.stopPropagation();
ev.preventDefault();
this.dialog.add(ConfirmationDialog, {
title: _t("Delete Confirmation"),
body: _t("Are you sure you want to delete this item?"),
confirmLabel: _t("YES, I'M SURE"),
cancelLabel: _t("NO, GO BACK"),
confirm: async () => {
await this.props.orm.unlink("dashboard.block", [this.props.widget.id]);
location.reload();
},
cancel: () => {},
});
}
// Function to render the chart
renderChart(){
if (this.props.widget.graph_type){
const x_axis = this.props.widget.x_axis
const y_axis = this.props.widget.y_axis
const data = []
for (let i = 0; i < x_axis.length; i++) {
const value = { key: x_axis[i], value: y_axis[i] }
data.push(value);
}
new Chart(
this.chartRef.el,
{
type: this.props.widget.graph_type || 'bar',
data: {
labels: data.map(row => row.key),
datasets: [
{
label: this.props.widget.measured_field || 'Data',
data: data.map(row => row.value),
backgroundColor: data.map((_, index) => getColor(index)),
hoverOffset : 4
}
]
},
}
);
}
}
}
DynamicDashboardChart.template = xml`
<div class="resize-drag block card"
t-att-data-x="this.props.widget.data_x"
t-att-data-y="this.props.widget.data_y"
t-att-style="'height:'+this.props.widget.height+'; width:'+ this.props.widget.width+ '; transform: translate('+ this.props.widget.translate_x +', '+ this.props.widget.translate_y +');'"
t-att-data-id="this.props.widget.id">
<div class="card-body mt-1" id="in_ex_body_hide">
<div class="block_edit block_setting" t-on-click="(ev) => this.getConfiguration(ev)">
<i title="Configuration"
class="fa fa-pencil block_setting chart-edit"/>
</div>
<div class="block_edit block_image" data-type="png" t-on-click="(ev) => this.exportItem(ev)">
<i title="Save As Image"
class="bi bi-image block_image chart-image"/>
</div>
<div class="block_edit block_pdf" data-type="pdf" t-on-click="(ev) => this.exportItem(ev)">
<i title="Export to PDF"
class="bi bi-file-earmark-pdf block_pdf chart-pdf"/>
</div>
<div class="block_edit block_csv" t-att-data-id="this.props.widget.id" data-type="csv" t-on-click="(ev) => this.exportItem(ev)">
<i title="Export to CSV"
class="bi bi-filetype-csv block_csv chart-csv"/>
</div>
<div class="block_edit block_xlsx" t-att-data-id="this.props.widget.id" data-type="xlsx" t-on-click="(ev) => this.exportItem(ev)">
<i title="Export to XLSX"
class="fa fa-file-excel-o block_xlsx chart-xlsx"/>
</div>
<div class="block_edit block_delete" t-on-click="(ev) => this.removeTile(ev)">
<i title="Delete"
class="fa fa-times block_delete chart-setting"/>
</div>
<h3 class="chart_title">
<t t-esc="this.props.widget.name"/>
</h3>
<div class="row">
<div class="col-md-12 chart_canvas" id="chart_canvas"
t-att-data-id="this.props.widget.id">
<canvas id="canvas" t-ref="chart" t-att-data-title="this.props.widget.name"/>
</div>
</div>
</div>
</div>
`

89
odoo_dynamic_dashboard/static/src/js/dynamic_dashboard_tile.js

@ -0,0 +1,89 @@
/** @odoo-module **/
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
import { _t } from "@web/core/l10n/translation";
const { Component, xml } = owl;
export class DynamicDashboardTile extends Component {
// Setup function of the class DynamicDashboardTile
setup() {
this.doAction = this.props.doAction.doAction;
this.dialog = this.props.dialog;
this.orm = this.props.orm;
}
// Function to get the configuration of the tile
async getConfiguration(ev){
ev.stopPropagation();
ev.preventDefault();
var id = this.props.widget.id
await this.doAction({
type: 'ir.actions.act_window',
res_model: 'dashboard.block',
res_id: id,
view_mode: 'form',
views: [[false, "form"]]
});
}
// Function to remove the tile
async removeTile(ev){
ev.stopPropagation();
ev.preventDefault();
this.dialog.add(ConfirmationDialog, {
title: _t("Delete Confirmation"),
body: _t("Are you sure you want to delete this item?"),
confirmLabel: _t("YES, I'M SURE"),
cancelLabel: _t("NO, GO BACK"),
confirm: async () => {
await this.orm.unlink("dashboard.block", [this.props.widget.id]);
location.reload();
},
cancel: () => {},
});
}
// Function for getting records by double click
async getRecords(){
var model_name = this.props.widget.model_name;
if (model_name){
await this.doAction({
type: 'ir.actions.act_window',
res_model: model_name,
view_mode: 'tree',
views: [[false, "tree"]],
domain: this.props.widget.domain,
});
}
}
}
DynamicDashboardTile.template = xml `
<div class="resize-drag tile"
t-on-dblclick="getRecords"
t-att-data-id="this.props.widget.id"
t-att-data-x="this.props.widget.data_x"
t-att-data-y="this.props.widget.data_y"
t-att-style="this.props.widget.color+this.props.widget.text_color+ 'height:'+this.props.widget.height+';width:'+this.props.widget.width + '; transform: translate('+ this.props.widget.translate_x +', '+ this.props.widget.translate_y +');'">
<div t-att-style="this.props.widget.color+this.props.widget.text_color"
class="d-flex align-items-center w-100 my-3">
<a class="block_setting tile_edit tile-container__setting-icon" style="color:black;" t-on-click="(ev) => this.getConfiguration(ev)" >
<i class="fa fa-edit"/>
</a>
<a class="block_delete tile_edit tile-container__delete-icon" style="color:black;" t-on-click="(ev) => this.removeTile(ev)">
<i class="fa fa-times"/>
</a>
<div t-att-style="this.props.widget.icon_color"
class="tile-container__icon-container bg-white d-flex justify-content-center align-items-center">
<i t-att-class="this.props.widget.icon"/>
</div>
<div t-att-style="this.props.widget.text_color"
class="tile-container__status-container">
<h2 t-att-style="this.props.widget.text_color"
class="status-container__title">
<t t-esc="this.props.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="this.props.widget.val_color">
<t t-esc="this.props.widget.value"/>
</h3>
</div>
</div>
</div>
</div>`

208
odoo_dynamic_dashboard/static/src/scss/dynamic_dashboard.scss

@ -0,0 +1,208 @@
:root {
/* Colors */
--green: #00C689;
--blue: #3DA5F4;
--red: #F1536E;
--yellow: #FDA006;
/*Fonts*/
--primary-font: 'Roboto', sans-serif;
}
html .o_web_client > .o_action_manager {
overflow: auto;
}
.bg-green {
background-color: var(--green);
}
.bg-blue {
background-color: var(--blue);
}
.bg-red {
background-color: var(--red);
}
.bg-yellow {
background-color: var(--yellow);
}
.text-color-yellow {
color: var(--yellow);
}
.text-color-green {
color: var(--green);
}
.text-color-blue {
color: var(--blue);
}
.text-color-red {
color: var(--red);
}
.text-color-yellow {
color: var(--yellow);
}
.tile-container__icon-container {
border-radius: 50%;
width: 4.75rem;
height: 4.75rem;
font-size: 28px;
margin-left: 15px;
}
.tile-container__status-container {
margin-left: 2em;
}
.status-container__title {
font-family: var(--primary-font);
font-weight: 500;
font-size: 1.5rem;
line-height: 1.5rem;
}
.status-container__figures {
font-family: var(--primary-font);
}
.status-container__figures > h3 {
font-weight: 700;
font-size: 1.5rem;
line-height: 1.813rem;
}
.tile-container__setting-icon {
top: 0.638rem;
}
.dark-theme {
/* Add dark theme styles here */
.block_edit {
color: #b7b7b7 !important;
}
.theme_icon:hover {
text-shadow: 0px 0px 5px #9388ff;
}
.add_block{
color: #dfdfdf;
}
#ExportMenu {
color: #626262;
}
.chart_title {
color: #dfdfdf;
margin: -0.5em 0 2em 0;
}
.btn-search_edit, {
color: #313131;
border: none;
background-color: #979797 !important;
}
#search-input-chart {
color: #ffffff;
border: 1px solid #4e4e4e;
background-color: #2A2A2A !important;
}
#search-clear {
color: #1f1f1f !important;
}
div.navbar {
color: #909090;
background-color: #2A2A2A !important;
}
.navbar-collapse {
margin-bottom: 12px !important;
}
h3 {
color: #909090;
}
div.dropdown-addblock {
color: #909090;
background-color: #2A2A2A !important;
}
#dropdownMenuButton {
color: #313131;
background-color: #979797 !important;
}
div.card-body {
border-radius: 0.5em !important;
background-color: #2a2a2a !important;
}
.o_kanban_record {
background-color: #e8e8e8 !important;
}
.o_kanban_renderer {
background-color: #e8e8e8 !important;
}
.oe_module_name {
background-color: #ffffff !important;
}
background-color: #1C1B1B;
@media (max-width: 767px) {
.navbar-light .navbar-toggler {
color: #A7A7A72D;
border-color: #F6F6F621;
}
}
}
.btn-align-items{
width: 134px;
font-size: small;
border-radius: revert;
height: 33px;
}
#edit_layout{
background-color: #0c8444;
}
#save_layout{
background-color: #b53c5d;
width: 134px;
height: 33px;
font-size: small;
border-radius: revert;
}
#search-button{
width: 69px;
margin-left: 5px;
}
#search-input-chart{
width: 206px;
height: 34px;
border: 1px solid black;
}
.search-clear{
margin-left: -80px;
}
label input{
appearance: none;
}
.mode{
padding-left: 7px;
font-family: 'odoo_ui_icons';
display: none;
}
.view-mode-icon{
font-size: x-large;
}

109
odoo_dynamic_dashboard/static/src/scss/style.scss

@ -1,109 +0,0 @@
.card{
border: none;
border-radius: 0px;
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
-webkit-box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
-moz-box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
}
.card-header{
background-color: transparent;
border: none;
}
:root {
/* Colors */
--green: #00C689;
--blue: #3DA5F4;
--red: #F1536E;
--yellow: #FDA006;
/*Fonts*/
--primary-font: 'Roboto', sans-serif;
}
html .o_web_client > .o_action_manager {
overflow:auto;
}
.bg-green {
background-color: var(--green);
}
.bg-blue {
background-color: var(--blue);
}
.bg-red {
background-color: var(--red);
}
.bg-yellow {
background-color: var(--yellow);
}
.text-color-yellow {
color: var(--yellow);
}
.text-color-green {
color: var(--green);
}
.text-color-blue {
color: var(--blue);
}
.text-color-red {
color: var(--red);
}
.text-color-yellow {
color: var(--yellow);
}
.tile-container {
padding: 3.2rem 1.5rem;
border-radius: 2rem;
}
.tile-container__icon-container {
border-radius: 50%;
width: 4.75rem;
height: 4.75rem;
font-size: 28px;
}
.status-container__title {
font-family: var(--primary-font);
font-weight: 500;
font-size: 1.5rem;
line-height: 1.5rem;
}
.status-container__figures {
font-family: var(--primary-font);
}
.status-container__figures>h3 {
font-weight: 700;
font-size: 1.5rem;
line-height: 1.813rem;
}
.tile-container__setting-icon {
top: 0.638rem;
right: 1rem;
}
// Main Navbar Dropdown menu location override
.o_menu_systray > .o_user_menu > .o-dropdown--menu{
left: auto !important;
right: 0px !important;
}
.button-container{
padding-top: 5px;
}
.add_block{
margin-left: 6px;
}
.tile-container__status-container{
padding-left: 7px;
}

128
odoo_dynamic_dashboard/static/src/xml/dynamic_dashboard_template.xml

@ -1,21 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve"> <templates id="template" xml:space="preserve">
<t t-name="owl.dynamic_dashboard" owl="1"> <!--DASHBOARD VIEW WITH NAVIGATION-BAR, INTERACTJS TEMPLATE-->
<div class="container"> <t t-name="owl.OdooDynamicDashboard" owl="1">
<div class="button-container"> <div class="container" style="min-height:-webkit-fill-available;">
<button class="btn btn-primary" data-type="tile" <div class="navbar navbar-expand-md navbar-light mb-4 navbar-style border-bottom"
type="button" t-on-click="_onClick_add_block">Add Block 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> </button>
<button class="btn btn-primary add_block" data-type="graph" <div class="collapse navbar-collapse"
type="button" t-on-click="_onClick_add_block">Add Graph aria-labelledby="dropdownNavbar">
<ul class="navbar-nav mr-auto">
<label class="navbar-items dropdown drop-down-add">
<button class="btn btn-align-items dropdown-add-items dropdown-toggle"
style="margin-top:-4px;"
type="selection" id="dropdownMenuButton"
data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false"
t-on-click="onClickAdd">
<i class="fa fa-plus-circle"/>
<span id="text_add">⠀Add Items</span>
</button> </button>
<div class="dropdown-menu dropdown-addblock"
aria-labelledby="dropdownMenuButton">
<a class="dropdown-item add_block"
data-type="tile"
t-on-click="(ev) => this.onClickAddItem(ev)">Tile</a>
<a class="dropdown-item add_block"
data-type="graph" data-chart_type="bar"
t-on-click="(ev) => this.onClickAddItem(ev)">Bar Chart</a>
<a class="dropdown-item add_block"
data-type="graph" data-chart_type="doughnut"
t-on-click="(ev) => this.onClickAddItem(ev)">Doughnut Chart</a>
<a class="dropdown-item add_block"
data-type="graph"
data-chart_type="line"
t-on-click="(ev) => this.onClickAddItem(ev)">Line Chart</a>
<a class="dropdown-item add_block"
data-type="graph" data-chart_type="pie"
t-on-click="(ev) => this.onClickAddItem(ev)">Pie Chart</a>
<a class="dropdown-item add_block"
data-type="graph"
data-chart_type="polarArea"
t-on-click="(ev) => this.onClickAddItem(ev)">Polar Area Chart</a>
<a class="dropdown-item add_block"
data-type="graph"
data-chart_type="radar"
t-on-click="(ev) => this.onClickAddItem(ev)">Radar Chart</a>
</div> </div>
<div class="o_dynamic_dashboard row"> </label>
<div class="o_dynamic_tile row"> </ul>
</div> </div>
<div class="o_dynamic_graph w3-container row"> <label class="navbar-items layout-switch"
style="padding-top:20px;"
id="edit-layout-label">
<button class="navbar-items btn-search_edit btn-align-items btn btn-primary my-2 mx-2 my-sm-0"
type="button"
id="edit_layout"
t-on-click="(ev) => this.editLayout(ev)">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"
t-on-click="(ev) => this.saveLayout(ev)">Save Layout</button>
<label for="view_mode"
t-on-mouseover="(ev) => this.showViewMode(ev)"
t-on-mouseout="(ev) => this.hideViewMode(ev)"
t-on-click="(ev) => this.changeViewMode(ev)"
style="margin-left: 6px;">
<input type="checkbox" id="view_mode"/>
<span><i class="bi bi-moon-stars-fill view-mode-icon" style="margin-left:10px"/></span>
</label>
<span class="mode" mode="light">Dark mode</span>
</label>
<div class="search-group" style="margin-right: 30px;padding-top:20px;">
<!-- Search Bar -->
<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" t-on-click="clearSearch">
<i class="fa fa-times search-clear"
style="margin-left:-25px;margin-top:9px;"/>
</span>
</div> </div>
<button class="btn btn-outline-success my-2 my-sm-0"
t-on-click="clickSearch"
style="margin-left:10px;"
type="button">Search
</button>
</div>
<!-- Date Inputs -->
<div class="date-inputs"
style="position: absolute; right: 34.5em; font-size: smaller;font-family: monospace; padding-top:inherit; ">
<label for="start-date" class="date-label"
style="color: black;">Start Date:</label>
<input type="date" id="start-date" name="start-date"
t-on-change="dateFilter"
style="color: black; border: 1px solid #4e4e4e; background-color: white; padding: 5px 10px; border-radius: 5px;"/>
<label for="end-date" class="date-label"
style="color: black; margin-left: 10px;">End Date:</label>
<input type="date" id="end-date" name="end-date"
t-on-change="dateFilter"
style="color: black; border: 1px solid #4e4e4e; background-color: white; padding: 5px 10px; border-radius: 5px;"/>
</div>
<div class="o-dropdown dropdown theme">
<select class="form-select"
t-ref="ThemeSelector"
t-on-change="onChangeTheme">
<option value="0">Select Theme</option>
</select>
</div>
<div class="dashboard_pdf" t-on-click="printPdf">
<i class="bi bi-filetype-pdf" style="font-size:24px;"/>
</div>
<div class="dashboard_mail" t-on-click="sendMail">
<i class="bi bi-envelope-fill" style="font-size:24px;"/>
</div>
</div>
<div class="all_items" style="display:grid;">
<div class="items"/>
</div> </div>
<!--CONTAINER FOR CONTENT GENERATION :TILE & CHART(FROM DynamicDashboardTile & DynamicDashboardChart-->
</div> </div>
</t> </t>
</templates> </templates>

46
odoo_dynamic_dashboard/views/dashboard_menu_view.xml

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Form view of the model 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"/>
<field name="parent_id"/>
<field name="group_ids" widget="many2many_tags" invisible="1"/>
<field name="client_action_id" invisible="1"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<!-- Tree view of the model dashboard menu -->
<record id="dashboard_menu_view_tree" model="ir.ui.view">
<field name="name">dashboard.menu.view.tree</field>
<field name="model">dashboard.menu</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="parent_id"/>
</tree>
</field>
</record>
<!-- Action for the model dashboard menu -->
<record id="dashboard_menu_action" model="ir.actions.act_window">
<field name="name">Dashboard Menu</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">dashboard.menu</field>
<field name="view_mode">tree,form</field>
</record>
<!-- Menu item to show the configuration in module dynamic dashboard-->
<menuitem name="Configuration" id="menu_dynamic_dashboard_configuration" parent="odoo_dynamic_dashboard.menu_dashboard"
sequence="3"/>
<!-- Menu item to show the dynamic menus in module dynamic dashboard-->
<menuitem name="Dashboards" id="menu_dynamic_dashboard_menu" parent="odoo_dynamic_dashboard.menu_dynamic_dashboard_configuration"
sequence="3" action="dashboard_menu_action"/>
</odoo>

66
odoo_dynamic_dashboard/views/dashboard_menu_views.xml

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--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">
<center>
<h3 class="my-2 ms-3">
Name:
<field name="name"/>
</h3>
</center>
<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>
<!-- Action specified for the dashboard menu-->
<record id="dashboard_menu_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>
<!--Menu Item of the model Dashboard Menu-->
<menuitem name="Dashboard Menu" id="dashboard_menu_view_action"
parent="odoo_dynamic_dashboard.menu_dashboard"
sequence="10" action="dashboard_menu_action"/>
</odoo>

56
odoo_dynamic_dashboard/views/dashboard_theme_views.xml

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--Form view of the dashboard block theme-->
<record id="dashboard_theme_view_form" model="ir.ui.view">
<field name="name">dashboard.theme.view.form</field>
<field name="model">dashboard.theme</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<div>
<field name="name" class="oe_inline"
style="font-size: 30px;"
placeholder="Theme Name" required="1"/>
</div>
</group>
<group>
<field name="color_x" widget="color"/>
<field name="color_y" widget="color"/>
<field name="color_z" widget="color"/>
</group>
<notebook>
<page string="Color Gradient">
<field name="body" type="html" readonly="1"/>
<field name="style" invisible="1"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<!--Tree view of the dashboard block theme-->
<record id="dashboard_theme_view_tree" model="ir.ui.view">
<field name="name">dashboard.theme.view.tree</field>
<field name="model">dashboard.theme</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
</tree>
</field>
</record>
<!-- Action specified for the dashboard block theme -->
<record id="dashboard_theme_action" model="ir.actions.act_window">
<field name="name">Dashboard Theme</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">dashboard.theme</field>
<field name="view_mode">tree,form</field>
</record>
<!--Menu Item for the model Dashboard Blocks-->
<menuitem name="Configuration" id="odoo_dynamic_configuration"
parent="odoo_dynamic_dashboard.menu_dashboard"/>
<menuitem name="Dashboard Theme" id="dashboard_theme_menu"
parent="odoo_dynamic_dashboard.odoo_dynamic_configuration"
sequence="45" action="dashboard_theme_action"/>
</odoo>

13
odoo_dynamic_dashboard/views/dashboard_view.xml

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Action to show the dashboard-->
<record id="dynamic_dashboard_action" model="ir.actions.client">
<field name="name">Dashboard</field>
<field name="tag">owl.dynamic_dashboard</field>
</record>
<!-- Menu Item to show the dashboard module-->
<menuitem name="Dashboard" id="menu_dashboard" sequence="0" web_icon="odoo_dynamic_dashboard,static/description/icon.png"/>
<!-- Menu Item to show the dashboards menu in the dashboard module-->
<menuitem name="Dashboards" id="menu_dynamic_dashboard" parent="odoo_dynamic_dashboard.menu_dashboard"
sequence="1" action="dynamic_dashboard_action"/>
</odoo>

14
odoo_dynamic_dashboard/views/dashboard_views.xml

@ -0,0 +1,14 @@
<?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">OdooDynamicDashboard</field>
</record>
<menuitem name="Dashboards" id="menu_dashboard" sequence="-1"
web_icon="odoo_dynamic_dashboard,static/description/icon.png"/>
<menuitem name="Dashboard" action="dashboard_view_action"
id="dashboard_root_menu"
parent="odoo_dynamic_dashboard.menu_dashboard"
sequence="0"/>
</odoo>

56
odoo_dynamic_dashboard/views/dynamic_block_view.xml → odoo_dynamic_dashboard/views/dynamic_block_views.xml

@ -1,50 +1,58 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<!-- form view of the model dashboard block--> <!--Form view of the dashboard block-->
<record id="dashboard_block_view_form" model="ir.ui.view"> <record id="dashboard_block_view_form" model="ir.ui.view">
<field name="name">dashboard.block.view.form</field> <field name="name">dashboard.block.view.form</field>
<field name="model">dashboard.block</field> <field name="model">dashboard.block</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form> <form>
<sheet> <sheet>
<div class="oe_title"> <group>
<h1> <group>
<field name="name"/> <div>
</h1> <field name="name" class="oe_inline"
style="font-size: 30px;"
placeholder="Block Name" required="1"/>
</div> </div>
</group>
</group>
<group> <group>
<group> <group>
<field name="model_id" <field name="model_id"
required="[('edit_mode','=', True)]"/> required="[('edit_mode','=', True)]"
<field name="client_action" invisible="1"/> options="{'no_create_edit':True,'no_create': True}"/>
<field name="client_action_id" invisible="1"/>
<field name="model_name" invisible="1"/> <field name="model_name" invisible="1"/>
<field name="edit_mode" invisible="1"/> <field name="edit_mode" invisible="1"/>
<field name="operation" <field name="operation"
required="[('edit_mode','=', True)]"/> required="[('edit_mode','=', True)]"/>
<field name="measured_field" <field name="measured_field_id"
domain="[('model_id','=',model_id), ('ttype','in',['float','integer','monetary']), ('store', '=', True)]" required="[('edit_mode','=', True)]"
required="[('edit_mode','=', True)]"/> options="{'no_create_edit':True, 'no_create': True }"
domain="[('model_id','=',model_id), ('ttype','in',['float','integer','monetary']), ('store', '=', True)]"/>
<field name="filter" widget="domain" <field name="filter" widget="domain"
options="{'model': 'model_name'}"/> options="{'model': 'model_name'}"/>
</group> </group>
</group> </group>
<group string="Block Information"> <group string="Block Information">
<group> <group>
<field name="sequence" invisible="1"/>
<field name="type" required="1"/> <field name="type" required="1"/>
<field name="graph_type" <field name="graph_type"
invisible="type == 'tile'"/> invisible="type == 'tile'"/>
<field name="graph_size" <field name="fa_icon"
invisible="type == 'tile'"/> invisible="type == 'graph'"/>
<field name="fa_icon" invisible="type == 'graph'"/> <field name="group_by_id" invisible="type == 'tile'"
<field name="fa_color" invisible="type == 'graph'" options="{'no_create_edit':True, 'no_create': True}"
widget="color"/>
<field name="group_by" invisible="type == 'tile'"
required="[('edit_mode','=', True),('type','=','graph')]" required="[('edit_mode','=', True),('type','=','graph')]"
domain="[('model_id','=',model_id), ('ttype','!=','one2many'), ('store', '=', True)]"/> domain="[('model_id','=',model_id), ('ttype','!=','one2many'), ('store', '=', True)]"/>
<field name="tile_color" invisible="type == 'graph'" <field name="tile_color"
invisible="type == 'graph'"
widget="color"/>
<field name="val_color"
invisible="type == 'graph'"
widget="color"/> widget="color"/>
<field name="text_color" invisible="type == 'graph'" <field name="text_color"
invisible="type == 'graph'"
widget="color"/> widget="color"/>
</group> </group>
</group> </group>
@ -52,7 +60,7 @@
</form> </form>
</field> </field>
</record> </record>
<!-- tree view of the model dashboard block--> <!--Tree view of the dashboard block-->
<record id="dashboard_block_view_tree" model="ir.ui.view"> <record id="dashboard_block_view_tree" model="ir.ui.view">
<field name="name">dashboard.block.view.tree</field> <field name="name">dashboard.block.view.tree</field>
<field name="model">dashboard.block</field> <field name="model">dashboard.block</field>
@ -64,7 +72,7 @@
</tree> </tree>
</field> </field>
</record> </record>
<!-- Action of the model dashboard block--> <!-- Action specified for the dashboard block-->
<record id="dashboard_block_action" model="ir.actions.act_window"> <record id="dashboard_block_action" model="ir.actions.act_window">
<field name="name">Dashboard Block</field> <field name="name">Dashboard Block</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
@ -72,8 +80,8 @@
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="context">{'default_edit_mode' : True}</field> <field name="context">{'default_edit_mode' : True}</field>
</record> </record>
<!-- Menu item of the model dashboard block--> <!--Menu Item for the model Dashboard Blocks-->
<menuitem name="Blocks" id="menu_dynamic_dashboard_blocks" <menuitem name="Dashboard Blocks" id="dashboard_block_menu"
parent="odoo_dynamic_dashboard.menu_dashboard" parent="odoo_dynamic_dashboard.menu_dashboard"
sequence="1" action="dashboard_block_action"/> sequence="5" action="dashboard_block_action"/>
</odoo> </odoo>

13
odoo_dynamic_dashboard/models/dashboard_block_line.py → odoo_dynamic_dashboard/wizard/__init__.py

@ -19,15 +19,4 @@
# If not, see <http://www.gnu.org/licenses/>. # If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################# #############################################################################
from odoo import fields, models from . import dashboard_mail
class DashboardBlockLine(models.Model):
""" Creates the model Dashboard Block Line"""
_name = "dashboard.block.line"
_description = "Dashboard Block Line"
sequence = fields.Integer(string="Sequence",
help="Sequence of the block lines")
block_size = fields.Integer(string="Block size",
help="Block size of the dashboard block line")

75
odoo_dynamic_dashboard/wizard/dashboard_mail.py

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.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 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, <br/> '
'I hope this mail finds you well. I am pleased to share the <b>Dashboard Report</b> with you.<br/>'
'Please find the attachment<br/>') % 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',
}

22
odoo_dynamic_dashboard/wizard/dashboard_mail_views.xml

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Sending dashboard pdf to users -->
<record id="dashboard_mail_view_form" model="ir.ui.view">
<field name="name">dashboard.mail.view.form</field>
<field name="model">dashboard.mail</field>
<field name="arch" type="xml">
<form string="Sent Mail">
<group>
<field name="user_ids" widget="many2many_tags"/>
<field name="base64code" invisible="1"/>
</group>
<footer>
<button name="send_mail" string="SEND"
class="btn-primary" type="object"/>
<button string="Cancel" class="btn-secondary"
name="cancel_mail" type="object"/>
</footer>
</form>
</field>
</record>
</odoo>
Loading…
Cancel
Save