Hide Widget based on grouped by Column Values
Hello I'm looking to hide a specific widget id if a specific value exists within a column value. I started with dashboard.on('widgetrefreshed', function (se, ev) { widgetList = ['682f7ab1602b5d7f74c97e6f', ] if(widgetList.includes(ev.widget.oid)) { $(`widget[widgetid="${ev.widget.oid}"]`).closest('.dashboard-layout-subcell-host').addClass('dontshowme-parent') } }); but I am not sure how to check if the value for a column called test_benchmarks contains 'ctv'73Views0likes8CommentsAdd presets to a Blox date filter widget
We have some dashboards that have widgets as filters. One of these is a Blox widget that functions as a date filter, which I created with help from the community here. I recently added presets to the date filter to make it easier and faster to apply date filtering. Create a Blox Widget Paste the script below in the script editor section. { "style": ".blox-slides button:hover{background-color:#014E66 !important;} .date-input-container { position: relative; } .date-input-container input[type='date'] { cursor: pointer; } .date-input-container::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 1; cursor: pointer; } .date-input-container input::-webkit-calendar-picker-indicator { opacity: 0; position: absolute; right: 10px; width: 20px; height: 20px; cursor: pointer; z-index: 2; }", "title": "", "showCarousel": true, "carouselAnimation": { "showButtons": false }, "script": "setTimeout(function() { const fromInput = document.getElementById('SelectVal_from'); const toInput = document.getElementById('SelectVal_to'); function formatDate(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return year + '-' + month + '-' + day; } function setDates(fromDate, toDate) { if (fromInput) fromInput.value = formatDate(fromDate); if (toInput) toInput.value = formatDate(toDate); } function getDateRanges() { const today = new Date(); const currentYear = today.getFullYear(); const currentMonth = today.getMonth(); const currentQuarter = Math.floor(currentMonth / 3); return { last30days: { from: new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000), to: today }, quarter: { from: new Date(currentYear, currentQuarter * 3, 1), to: new Date(currentYear, (currentQuarter + 1) * 3, 0) }, ytd: { from: new Date(currentYear, 0, 1), to: today }, lastyear: { from: new Date(currentYear - 1, 0, 1), to: new Date(currentYear - 1, 11, 31) } }; } const ranges = getDateRanges(); document.querySelectorAll('[data-filter-type]').forEach(function(btn) { btn.addEventListener('click', function() { const filterType = this.getAttribute('data-filter-type'); if (ranges[filterType]) { setDates(ranges[filterType].from, ranges[filterType].to); } }); }); if (fromInput) { fromInput.addEventListener('click', function(e) { if (e.target.tagName === 'INPUT') { e.target.showPicker ? e.target.showPicker() : e.target.click(); } }); fromInput.style.cursor = 'pointer'; } if (toInput) { toInput.addEventListener('click', function(e) { if (e.target.tagName === 'INPUT') { e.target.showPicker ? e.target.showPicker() : e.target.click(); } }); toInput.style.cursor = 'pointer'; } }, 1000);", "body": [ { "type": "Container", "width": "90%", "style": { "margin": "0 auto" }, "items": [ { "type": "ActionSet", "actions": [ { "type": "date-preset", "title": "Last 30 Days", "style": { "color": "white", "background-color": "#007FAA" }, "data": { "FilterType": "last30days", "FilterFields": [ "[Dm_dates.date_data (Calendar)]" ] } }, { "type": "date-preset", "title": "This Quarter", "style": { "color": "white", "background-color": "#007FAA" }, "data": { "FilterType": "quarter", "FilterFields": [ "[Dm_dates.date_data (Calendar)]" ] } }, { "type": "date-preset", "title": "Year to Date", "style": { "color": "white", "background-color": "#007FAA" }, "data": { "FilterType": "ytd", "FilterFields": [ "[Dm_dates.date_data (Calendar)]" ] } }, { "type": "date-preset", "title": "Last Year", "style": { "color": "white", "background-color": "#007FAA" }, "data": { "FilterType": "lastyear", "FilterFields": [ "[Dm_dates.date_data (Calendar)]" ] } } ] }, { "type": "Container", "style": { "display": "flex", "flexDirection": "row", "justifyContent": "space-between", "marginTop": "20px", "gap": "10px" }, "items": [ { "type": "Container", "style": { "width": "48%" }, "items": [ { "type": "TextBlock", "text": "From", "weight": "lighter", "color": "black" }, { "type": "Container", "style": { "position": "relative" }, "items": [ { "type": "Input.Date", "id": "SelectVal_from", "placeholder": "mm/dd/yyyy", "calendar": true, "style": { "width": "100%", "padding": "14px", "background-color": "#F4F4F8", "border-radius": "8px", "border": "1px solid #ccc", "font-size": "16px", "cursor": "pointer" } } ] } ] }, { "type": "Container", "style": { "width": "48%" }, "items": [ { "type": "TextBlock", "text": "To", "weight": "lighter", "color": "black" }, { "type": "Container", "style": { "position": "relative" }, "items": [ { "type": "Input.Date", "id": "SelectVal_to", "placeholder": "mm/dd/yyyy", "calendar": true, "style": { "width": "100%", "padding": "14px", "background-color": "#F4F4F8", "border-radius": "8px", "border": "1px solid #ccc", "font-size": "16px", "cursor": "pointer" } } ] } ] } ] }, { "type": "ActionSet", "style": { "marginTop": "20px", "text-align": "center" }, "actions": [ { "type": "DateX", "id": "submit_btn", "title": "Apply", "style": { "color": "white", "background-color": "#007FAA" }, "data": { "FilterFields": [ "[Dm_dates.date_data (Calendar)]" ] } }, { "type": "filter-date-clear", "title": "Clear", "style": { "color": "white", "background-color": "#007FAA" }, "data": { "FilterFields": [ "[Dm_dates.date_data (Calendar)]" ] } } ] } ] } ] } Create the necessary actions for the buttons to work: date-preset const filterType = payload.data.FilterType; const filterDims = payload.data.FilterFields; const dash = payload.widget.dashboard; const now = new Date(); const yyyy = now.getFullYear(); const mm = String(now.getMonth() + 1).padStart(2, '0'); const dd = String(now.getDate()).padStart(2, '0'); const today = `${yyyy}-${mm}-${dd}`; let fromDate = ''; let toDate = today; //Year to date if (filterType === 'ytd') { fromDate = `${yyyy}-01-01`; //Quarter } else if (filterType === 'quarter') { const q = Math.floor(now.getMonth() / 3); const startMonth = q * 3 + 1; fromDate = `${yyyy}-${String(startMonth).padStart(2, '0')}-01`; //Last Year } else if (filterType === 'lastyear') { fromDate = `${yyyy - 1}-01-01`; toDate = `${yyyy - 1}-12-31`; // Last 30 days: from 30 days ago to today } else if (filterType === 'last30days') { const pastDate = new Date(now); pastDate.setDate(pastDate.getDate() - 30); const pastY = pastDate.getFullYear(); const pastM = String(pastDate.getMonth() + 1).padStart(2, '0'); const pastD = String(pastDate.getDate()).padStart(2, '0'); fromDate = `${pastY}-${pastM}-${pastD}`; } else { console.log('Unknown FilterType:', filterType); if (typeof sendResponse === 'function') sendResponse(false); return; } let newFilter = {}; $('#SelectVal_from').val(fromDate); $('#SelectVal_to').val(toDate); newFilter = { jaql: { dim: "", filter: { from: fromDate, to: toDate } } }; filterDims.forEach(function(dim) { newFilter.jaql.dim = dim; dash.filters.update(newFilter, { refresh: true, save: true }); }); Datex -- Apply button var today = new Date(); var dd = String(today.getDate()).padStart(2, '0'); var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0! var yyyy = today.getFullYear(); today = yyyy + '-' + mm + '-' + dd; const filVal_from = payload.data.SelectVal_from == '' ? '1800-01-01' : payload.data.SelectVal_from; const filVal_to = payload.data.SelectVal_to == '' ? '2100-01-01' : payload.data.SelectVal_to; const filterDims = payload.data.FilterFields; const dash = payload.widget.dashboard; let newFilter = {}; console.log(filVal_from); console.log(filVal_to); newFilter = { jaql: { dim: "", filter: { from: filVal_from, to: filVal_to } } }; filterDims.forEach(function (dim) { newFilter.jaql.dim = dim; dash.filters.update(newFilter, { refresh: true, save: true }) }) Clear dates var today = new Date(); var dd = String(today.getDate()).padStart(2, '0'); var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0! var yyyy = today.getFullYear(); today = yyyy + '-' + mm + '-' + dd; const filVal_from = "1800-01-01" const filVal_to = '2100-01-01' const filterDims = payload.data.FilterFields; const dash = payload.widget.dashboard; let newFilter = {}; newFilter = { jaql: { dim: "", filter: { from: filVal_from, to: filVal_to } } }; $('#SelectVal_from').val(''); $('#SelectVal_to').val(''); filterDims.forEach(function (dim) { newFilter.jaql.dim = dim; dash.filters.update(newFilter, { refresh: true, save: true }) }) Save everything and try it out.56Views2likes0CommentsHelp with BloX Widget
Hello, I am hoping to get some guidance on how to use BloX to return a message. I have a field in a table that returns a value of Yes or No. I want to the BloX widget to return a message if the value is No. If the value is Yes, I want the BloX widget hidden. I have exhausted my AI effort attempts. It appears this is something the BloX widget should be able to do, but I just can't get it to work at all. Any advise would be helpful.36Views0likes4CommentsCheckBox using Blox without Action Button
Hello everyone, I have created a Blox widget which shows a list of values with checkboxes. I am trying to make the check boxes "actionable", meaning that when a checkbox is ticked, then my dashboard filter adjusts. I have tried to utilize this widget script, but it doesn't seem to work: widget.on('processresult', function(se, ev){ ev.result.unshift(allObject) }) widget.on('ready', function(se, ev){ var filterName = 'Chain Name' var select = document.getElementById(`data.filters[2].filterJaql.members[0]`); dashboardFilter = prism.activeDashboard.filters.$$items.find(el => el.jaql.title == filterName) if(dashboardFilter && dashboardFilter.jaql.filter && dashboardFilter.jaql.filter.members) { select.value = dashboardFilter.jaql.filter.members[0] } select.addEventListener("change", function(e){ if(e.target.value == "All") { filter = { "explicit": false, "multiSelection": true, "all": true } }else { filter = { "explicit": true, "multiSelection": true, "members": [ e.target.value ] } } dashboardFilter = prism.activeDashboard.filters.$$items.find(el => el.jaql.title == filterName) var filterOptions = { save: true, refresh: true, } dashboardFilter.jaql.filter = filter prism.activeDashboard.filters.update(dashboardFilter, filterOptions) }); }) Can anyone help me with this?Solved97Views0likes6CommentsInput Parameters using BloX for What-If Analysis
Input Parameters using BloX for What-If Analysis This is an alternative to the paid or community Input Parameter plugins. This new BloX Input Parameter custom action: supports multiple levers (input parameters) with just one Apply button supports all widget types remembers previous selections even after refreshing/closing the dashboard loops through all widgets so dashboard designers do not have to specify widget IDs in the BloX code. Note: Be mindful of performance when considering this option as this will execute the BloX action on all widgets including those that the input parameters may not necessarily be relevant to. Input Parameter Implementation Instructions: Import the example dashboard attached below. Before importing the file, change the file extension from .txt to .dash. Make sure that you still have the Sample ECommerce Elasticube for this dashboard to properly display the data. Open the BloX Lever Configuration widget, then add the custom action given below to your Sisense environment. Go to the Actions tab, click the three-dot menu, then click Create Action. Copy the custom action code from below and paste it into your BloX custom action editor. Give this name for the action: ParameterSwap_V2_AllWidgets. You can also use a different name, but make sure the name of the action matches with the name referenced in the BloX code. Click Next, then Create. Click the Apply button to close the widget. Enter values to the levers and click Apply. The number on the widgets is now recalculated based on your input. There are three input parameters in this example. To add another input parameter: In the BloX code of the Lever Configuration widget, modify the number of input parameters in the BloX code under the paramsToModify parameter. For example, if you need 4 input parameters (levers), update the value to 4. On the left panel, add the additional swap_levers to the Values panel, i.e. swap_lever4, swap_lever5, etc. You can assign any arbitrary hard-coded number to these swap levers. To add the input box to the widget, copy one of the column elements then add it to the BloX code. Modify the id parameter to selectVal<lever number>, e.g. if this is the fourth lever, the id should be selectVal4. Similarly, modify the value parameter to match the swap_lever name you added in the previous step, e.g. [swap_lever4]. Update the title text as well and give it a meaningful description. Save the widget by clicking the Apply button. Add the additional levers to your widget formulas as needed so that they get recalculated when you enter the input values. Input Parameter BloX action: //initialize variables var swapParam = []; //create an array of swap_levers for (d = 0; d < payload.data.paramsToModify; d++) { swapParam[d] = "swap_lever" + (d + 1); } //loop through each of the specified widgets payload.widget.dashboard.widgets.$$widgets .forEach(function (widget) { //loop through each panel in the widget //exclude the filter panel (last panel) for (p = 0; p < widget.metadata.panels.length - 1; p++) { //loop through each item in the panel for (i = 0; i < widget.metadata.panels[p].items.length; i++) { //check if the panel item contains context (i.e. a formula) if (widget.metadata.panels[p].items[i].jaql.context != undefined) { var queryContext = widget.metadata.panels[p].items[i].jaql.context; //loop through each context in the item for (let [k, v] of Object.entries(queryContext)) { //loop through each swap_lever for (s = 0; s < swapParam.length; s++) { //check if the formula contains the swap_lever if (v.title == swapParam[s]) { var val = 'selectVal' + (s + 1); //update the formula with the swap_lever value that the user entered v.formula = payload.data[val]; } } } } } } //apply and save changes to the widget widget.changesMade('plugin-BloX', ['metadata']) //refresh the widget widget.refresh(); }) Resetting to Default Values The attached dashboard example includes a Reset button to reset the input parameters to pre-set default values. If you do not need this option, you can remove the second action from the BloX code, as shown in the highlighted part of the screenshot below. Instructions to implement the Reset button: Open the BloX Lever Configuration widget, then add the custom action given below to your Sisense environment. Go to the Actions tab, click the three-dot menu, then click Create Action. Copy the custom action code from below and paste it into your BloX custom action editor. Give this name for the action: ParameterSwap_V2_AllWidgets_Reset. You can also use a different name, but make sure the name of the action matches with the name referenced in the BloX code. Click Next, then Create. In the BloX code, update the defaultValues array with your required default values. See screenshot below for reference. There are three input parameters in this example, if you have more/less parameters, update the paramsToModify value to the correct number of parameters that you have. See screenshot below for reference. Click the Apply button to close the widget. Reset Input Parameter BloX action: //initialize variables var leverValues = payload.data.defaultValues; var swapParam = []; //create an array of swap_levers for (d = 0; d < payload.data.paramsToModify; d++) { swapParam[d] = "swap_lever" + (d + 1); } //loop through all widgets in the dashboard payload.widget.dashboard.widgets.$$widgets .forEach(function (widget) { //loop through each panel in the widget //exclude the filter panel (last panel) for (p = 0; p < widget.metadata.panels.length - 1; p++) { //loop through each item in the panel for (i = 0; i < widget.metadata.panels[p].items.length; i++) { //check if the panel item contains context (i.e. a formula) if (widget.metadata.panels[p].items[i].jaql.context != undefined) { var queryContext = widget.metadata.panels[p].items[i].jaql.context; //loop through each context in the item for (let [k, v] of Object.entries(queryContext)) { //loop through each swap_lever for (s = 0; s < swapParam.length; s++) { //check if the formula contains the swap_lever if (v.title == swapParam[s]) { var val = 'selectVal' + (s + 1); //update the formula with the default value v.formula = leverValues[s]; } } } } } } //apply and save changes to the widget widget.changesMade('plugin-BloX', ['metadata']) //refresh the widget widget.refresh(); }) We hope this is a helpful article and would love to hear about your experience in the comments!16KViews4likes12CommentsImprovise, adapt, overcome! How to increase the adoption and user satisfaction of dashboards
Discover the art of creating user-friendly dashboards that truly resonate with your audience. Drawing from six years of experience with Sisense, this article dives into dashboard design. Learn practical tips on improving dashboard readability, integrating advanced filters, and using dynamic widgets. Whether you're a seasoned designer or just starting, this guide offers invaluable insights to ensure your dashboards are not only functional but also a delight to use.1.4KViews4likes1CommentCreating a unique benchmarking visual with Sisense Blox and SVG
In our quest to create a dynamic visual representation of a company’s percentile among its peers, we found the Box and Whisker Plot lacking. Our solution? Designing a custom SVG image that integrates seamlessly with Sisense Blox. By leveraging Adobe XD's plugin to generate the SVG and customizing the code with Sisense formulas, we crafted a Percentile Comparison Chart that dynamically adjusts as data updates. This innovative approach allows for clear visualization of percentile rankings, including the median, 10th, and 90th percentiles, and more, all while adapting to real-time data changes.987Views2likes0CommentsSingle Filter against multiple columns
Hello Experts I wonder if there is way to build a single filter on Dashboard that interacts with 2 different columns in the database? Here is an example 2 columns in table:- - primary City - secondary city On the dashboard level, we would like to build a single filter called 'city' which should filter against both of these columns using OR condition (eg: select sum(sales) from customer_orders where primary_city='Toronto' or secondary_city='Toronto'Solved756Views0likes5CommentsBlox - Multi-Select Dropdown List Filter
Blox - Multi-Select Dropdown List Filter This article will take you step by step on how to create a multi-select dropdown filter using Blox and JavaScript.   ElastiCube: 1. For each field you want to use in multi-select filter, you need to add a custom column. For instance, in our Sample ECommerce ElastiCube, add a custom column to the "Category" table: For Sisense on Windows add the below string in order to create the column: '<li><input type="checkbox" />'+[Category].[Category]+'</li>' For Sisense on Linux: '<li><input type=checkbox>'+[Category].[Category] + '</li>' 2. Change its name to [CategoryHTML]. 3. Do the same for the column [Country] from the table [Country] and name it [CountryHTML]. 3. Perform the 'Changes Only' build. Dashboard: 1. Download the dashboard attached and import it to your application. 2. Create a custom action in BloX and name it MultiBoxSelection: 3. Add the action's code below: var outputFilters = []; var widgetid = payload.widget.oid; var widgetElement = $('[widgetid="' + widgetid + '"]'); widgetElement.find($('blox input:checked')).parent().each(function () { var values = $('.fillmeup').attr('value') + $(this).text(); $('.fillmeup').attr('value', values); }).each((i, lmnt) => { outputFilters.push($(lmnt).text()); }) payload.widget.dashboard.filters.update( { 'jaql': { 'dim': payload.data.dim, 'title': payload.data.title, 'filter': { 'members': outputFilters }, 'datatype': 'text' } }, { 'save': true, 'refresh': true } ) 4. Action's snippet: { "type": "MultiBoxSelection", "title": "Apply", "data": { "dim": "FilterDimension", "title": "FilterTitle" } } 5. Add the widget's script. For each widget you need to change identifiers [CategoryList] and [CategoryItems] - these identifiers should be unique per each widget on a page: widget.on('ready', function() { var checkList = document.getElementById('CategoryList'); var items = document.getElementById('CategoryItems'); checkList.getElementsByClassName('anchor')[0].onclick = function(evt) { if (items.classList.contains('visible')) { items.classList.remove('visible'); items.style.display = "none"; } else { items.classList.add('visible'); items.style.display = "block"; } } items.onblur = function(evt) { items.classList.remove('visible'); } }); widget.on('processresult', function(a, b) { b.result.slice(1, b.result.length).forEach(function(i) { b.result[0][0].Text = b.result[0][0].Text + ' ' + i[0].Text }) }); These identifiers should be the same as you have in the widget in the value of [text]: { "type": "TextBlock", "spacing": "large", "id": "", "class": "", "text": "<div id='CategoryList' class='dropdown-check-list' tabindex='100'> <span class='anchor'>Select Category</span> <ul id='CategoryItems' class='items'>{panel:CategoryHTML}</ul> </div>" } 5. Click Apply on the widget and refresh the dashboard. Important Notes: Make sure you have the Category in the items (The new column was created) and name the Item "Category". Make sure you have a Category Filter (The actual category field and name it "Category") on the dashboard level. Make sure to replace the field and table names with the relevant field/table in the Action, in the editor of the Blox widget in the Blox items and in the dashboard filter. Disclaimer: Please note that this blog post contains one possible custom workaround solution for users with similar use cases. We cannot guarantee that the custom code solution described in this post will work in every scenario or with every Sisense software version. As such, we strongly advise users to test solutions in their environment prior to deploying them to ensure that the solutions proffered function as desired in their environment. For the avoidance of doubt, the content of this blog post is provided to you "as-is" and without warranty of any kind, express, implied, or otherwise, including without limitation any warranty of security and or fitness for a particular purpose. The workaround solution described in this post incorporates custom coding, which is outside the Sisense product development environment and is, therefore, not covered by Sisense warranty and support services.259Views1like0CommentsPassing Filters via URL Parameters for Dashboards with Separate Datasources
Sisense includes a native included feature and format for passing URL filters via URL parameters, as documented here. By default, this functionality copies filters in full, including the datasource parameter of the filter, and includes every filter automatically. It results in very long URL's, and includes many parameters that are not always required, as the full filter object is included. Previous Knowledge Base articles articles have discussed how similar behavior can be recreated customized via scripting for more flexible usage. However, those approaches applied only to member-type filters, excluding other filter types and multi-level dependent filters. The code shared below demonstrates a more flexible filter modification via URL modification approach. It includes both creating URL parameters and reading URL parameters for filter modification, whether this code is in a script or plugin. This method applies to all filter types and can be used to transfer filters between dashboards using different datasources. This code works in both dashboard and widget scripts as well as plugins. If your datasources use different dimension names, this code can be adopted to map and match the aligned dimensions.307Views1like0Comments