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