Sisense Community logo
    • Community Feedback
    • Chapters
    • Events
    • Forums
      • Help and How To
      • Product Feedback Forum
      • Strategy & Use Cases
    • Blogs
    • KB Docs
      • KB Docs
      • Add-Ons & Plug-Ins
      • APIs
      • Best Practices
      • Blox
      • CDT
      • Cloud Managed Service
      • Data Models
      • Data Sources
      • Embedding Analytics
      • How-Tos & FAQs
      • Onboarding
      • PySisense
      • Security
      • Sisense Administration
      • Sisense Intelligence & AI
      • Troubleshooting
      • Widget & Dashboard Scripts
    • Support
    • Learning
      • Sisense Academy: Free Courses and Certifications
      • Official Developer Documentation
      • Official Product Documentation
      • Official Sisense Youtube Channel
      • Sisense Compose SDK Playground
      • Official Sisense Discord
    • Use Case Gallery
    Discussions
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    Discussions
    • TagsChevronRightIcon
    Iframe
    • Blog banner
      • Widget & Dashboard ScriptsChevronRightIcon

      Redirect users to different dashboards based on dashboard filters

                                                                                                                               

      Redirect users to different dashboards based on dashboard filters This article  discusses and shares the full code of a  dashboard script  that redirects users to a different dashboard ID based on the user's  filter selections or initial loaded filter state. In the particular example shared in this article, the script checks whether the selected date filter (either from a members filter or a filter date range) includes an earlier date than the earliest date in the current dashboard's data source. If this is the case, the script redirects the user to a specified alternate dashboard, preserving any additional URL segments and query parameters in the URL. Any other type of filter state can also be used to determine when the script should redirect, including non-date filters using similar scripts. Example Use Case Limited vs. Full Dataset : One dashboard data source might serve only recent data, while another dashboard data source stores all historical data. If a user selects a date that pre-dates one dataset, this script will seamlessly redirect them to the alternate dashboard that includes older data. Improved User Experience : Instead of displaying empty or invalid data for older dates, users are smoothly guided to the correct “all data” view dashboard. Full Script     /** * Main function that checks a single-level date filter of day-level granularity * (either 'members' or 'from/to') for a single selected by variable date dimension. The dashboard script determines * whether the selected date is earlier than the earliest date in the current dashboard’s * datasource. If so, the function redirects to another dashboard while preserving the rest * of the URL parameters and path segments. * * Use Case: * - One dashboard has a datasource with only recent data (no older dates). * - Another dashboard has a datasource with all historical data. * - If a user selects a date earlier than the “recent” dataset supports, the script redirects * them to the “all data” dashboard, retaining any query parameters or path info in the URL. */ function checkAndRedirectIfNeeded() { /** * Toggle console logging for clarity or debugging. */ const enableLogging = true; /** * The dimension string for the date filter in the current dashboard's datasource. * Example: "[Commerce.Date (Calendar)]". * This code checks a single-level date filter of day-level granularity for this dimension. Change as needed */ const dateDim = "[Commerce.Date (Calendar)]"; /** * The alternate dashboard ID to which a user is redirected if they select a date * older than what the current datasource includes. This dashboard ID replaces the current * dashboard ID in the URL, preserving any extra path or query parameters. */ const alternateDashboardId = "67bebb6863199f002a7b0906"; /** * Logs to the console only if enableLogging is true. * @param {...any} msgs - Items to log. */ function log(...msgs) { if (enableLogging) { console.log(...msgs); } } /** * Searches the dashboard's filters for a single-level date filter matching dimStr. * Returns the filter object if found, otherwise null. * @param {string} dimStr - The date dimension to look for. */ function findDateFilter(dimStr) { if (!dashboard.filters || !dashboard.filters.$$items) { log("No filters accessible on this dashboard."); return null; } for (const filter of dashboard.filters.$$items) { if (filter.jaql && filter.jaql.dim === dimStr) { log("Found date filter for dimension:", dimStr); return filter; } } return null; } /** * Builds a minimal JAQL query to fetch the earliest date (sorted ascending) from the specified dimension. * Uses the current dashboard's datasource, and returns only 1 row (count: 1). * * This ensures we get the earliest available date in the dataset for the dimension in question, * typically returned as an ISO-like string in the response data. * * @param {string} dimStr - The dimension string representing the date field. */ function buildEarliestDateQuery(dimStr) { return { datasource: dashboard.datasource, metadata: [ { jaql: { dim: dimStr, datatype: "datetime", level: "days", sort: "asc" } } ], count: 1 }; } /** * Runs the JAQL query using Sisense's internal HTTP service if available, otherwise jQuery.ajax. * The request is made synchronous (async: false). If the internal service doesn't exist, fallback is standard AJAX. * * @param {Object} jaql - The JAQL query object. * @returns {Promise} - Resolves with the server response or rejects on error. */ function runHTTP(jaql) { const $internalHttp = prism.$injector.has("base.factories.internalHttp") ? prism.$injector.get("base.factories.internalHttp") : null; const ajaxConfig = { url: `/api/datasources/${encodeURIComponent(jaql.datasource.title)}/jaql`, method: "POST", data: JSON.stringify(jaql), contentType: "application/json", dataType: "json", async: false, xhrFields: { withCredentials: true } }; return $internalHttp ? $internalHttp(ajaxConfig, false) : $.ajax(ajaxConfig); } /** * Replaces only the dashboard ID in the current URL with alternateDashboardId, * preserving the rest of the URL (query parameters, path segments, etc.). * If the current dashboard ID is not found, the redirect is canceled. */ function redirectToAlternateDashboard() { const currentDashId = dashboard.oid; if (!currentDashId) { log("Cannot determine current dashboard ID. Redirect canceled."); return; } const currentUrl = window.location.href; if (!currentUrl.includes(currentDashId)) { log("Current URL does not contain the expected dashboard ID. Redirect canceled."); return; } const newUrl = currentUrl.replace(currentDashId, alternateDashboardId); log("Redirecting to alternate dashboard:", newUrl); // Perform the actual redirect window.location.href = newUrl; } /** * Main logic flow: * 1) Fetch the earliest date in the dataset for dateDim by building and running a JAQL query. * 2) Locate the single-level date filter for dateDim in the dashboard filters. * 3) Check the user's selected date: * - If 'members' type, use the first member. * - If 'from/to' type, use 'from'. * 4) Compare the selected date (JS Date) to the earliest date (JS Date). * 5) If selected date is earlier, redirect to the alternate dashboard; otherwise, do nothing. */ const jaqlQuery = buildEarliestDateQuery(dateDim); runHTTP(jaqlQuery) .then(response => { if (!response?.data?.values?.length) { log("No data returned for earliest date query. No redirect needed."); return; } // The earliest date is stored in .data, typically an ISO-like date string (e.g. "2023-01-05T00:00:00"). const earliestDateString = response.data.values[0][0].data; log("Earliest date in the dataset:", earliestDateString); // Convert the earliest date string to a JS Date object for comparison const earliestDate = new Date(earliestDateString); if (isNaN(earliestDate.valueOf())) { log("Earliest date is invalid or unrecognized:", earliestDateString); return; } // Find the single-level date filter on the dashboard const dateFilter = findDateFilter(dateDim); if (!dateFilter || !dateFilter.jaql.filter) { log("Date filter not found or missing filter object. Skipping redirect check."); return; } let selectedDateStr = null; // If it's a 'members' type filter, use the first member, this is the earliest if ( Array.isArray(dateFilter.jaql.filter.members) && dateFilter.jaql.filter.members.length > 0 ) { selectedDateStr = dateFilter.jaql.filter.members[0]; log("Filter type: 'members'. Selected date string:", selectedDateStr); // If it has 'from' (and possibly 'to'), use 'from' } else if (dateFilter.jaql.filter.from) { selectedDateStr = dateFilter.jaql.filter.from; log("Filter type: 'from/to'. Selected 'from' date string:", selectedDateStr); } else { log("Filter is not recognized or has no selected date. Skipping redirect."); return; } // Convert the filter's date string to a JS Date const selectedDate = new Date(selectedDateStr); if (isNaN(selectedDate.valueOf())) { log("Selected date is invalid or unrecognized:", selectedDateStr); return; } // Compare for earliest date if (selectedDate < earliestDate) { log("Selected date is earlier than the earliest dataset date. Redirecting..."); redirectToAlternateDashboard(); } else { log("Selected date is within or after earliest dataset date. No redirect needed."); } }) .catch(error => { log("Error retrieving earliest date or comparing filter date:", error); }); } /** * Calls the main logic function after the dashboard is initialized. */ dashboard.on("initialized", function () { checkAndRedirectIfNeeded(); }); /** * Also calls the main logic function whenever filters change. */ dashboard.on("filterschanged", function () { checkAndRedirectIfNeeded(); });     How It Works Earliest Date Lookup The script first builds a small custom secondary JAQL query (sorted in ascending order, limited to one result) to determine the earliest date in the data source. It executes this query using Sisense’s internal service function, using the native Sisense cookies. This type of scripting functionality has been discussed in more detail in previous knowledge base articles, such as this article . Filter Inspection After fetching the earliest date, the script searches for a single-level date filter matching the configured date dimension. If the filter uses a   members   array, the script reads the first member. If the filter uses a   from and to range   structure, it uses the   from   date, as this is always the earliest. Comparison The script converts both the earliest date and the selected date into JavaScript   Date   objects. If the selected date is earlier than the earliest date, the script triggers a redirect. Redirect Logic Only the dashboard ID portion of the current URL is replaced with the specified alternateDashboardId. Path segments, query parameters, and any other pre-existing parts of the URL remain the same. Use Cases Limited vs. Full Dataset When one Sisense dashboard is restricted to recent data (for example, the last 30 days) and another includes all historical data. This script diverts users to the appropriate dashboard seamlessly. Consistent User Experience Avoids confusion when a date filter extends beyond a limited dataset’s timeframe. Instead of showing empty or invalid data, the user is sent to a more comprehensive dashboard. Multi-Dashboard Navigation Ensures the user can continue with the same URL parameters and path segments, simply swapping the restricted dashboard for the alternate “all data” one. Conclusion By employing this or similar scripts based on this form of customization (this example is more advanced in using a custom secondary JAQL request to fetch the first data value, but this is not necessary in many scenarios), it is possible to streamline date-based filter navigation or other filter-based navigation between dashboards or data sources, guaranteeing for example that if a date selection falls outside the scope of a limited dataset, the user automatically sees data in a broader data source dashboard. This functionality improves user experience and allows for wide customizability.  Example of a date selection including dates before the first date in dataset [A LT Text: A digital interface showing a table titled "Days in Date." Below the title, there are three dates listed: 11/26/20, 11/25/20, and 11/27/20. The date 11/26/20 is highlighted in yellow, along with a toggle switch on the right side of the image.] Example Output with logging variable enabled [ ALT Text: A screenshot of a computer terminal showing error messages related to date filters for a calendar dimension in a dataset. The messages indicate that a selected date of November 26, 2020, is valid while a date of November 25, 2020, is earlier than the earliest date in the dataset (November 11, 2020). The terminal displays a redirecting notice to an alternate dashboard.]  

      Jeremy Friedel
      Jeremy FriedelPosted 1 year ago
      0
               
    • ramansingh89
      • Help and How-To
               
      ramansingh89
      JWT Token with Iframe
               

      Hi Team I am trying to find out a doc that can explain how JWT token works with iframe? If there is any code snippet to generate JWT token exists somewhere, please let me know as well

      2 years agolast reply 1 year ago
      7
               
      • Embedding AnalyticsChevronRightIcon

      Dynamic Resize For Embedded IFrames

                                       

      If you've ever attempted to dynamically resize your embedded iFrames in your parent application, you may have experienced a  CORS  conflict. Basically, since your parent application and sisense application serve from different domains, your browser restricts HTTP responses for security reasons. Luckily, we have a workaround that leverages the Window.postMessage() function. This solution dynamically resizes your embedded iFrame based on the height of the contents. // Within your SISENSE APPLICATION, apply this script on the dashboard to be embedded via iFrame: dashboard.on('refreshend', function () { var heightValue = 0, columnArr = dashboard.layout.columns, columnHeight = [], targetWindow = window.parent, // replace with your target domain targetOrigin = 'http://main.mytestapp.com:8083'; // iterate through columns for (var i = 0; i < columnArr.length; i++) { var cellsLength = columnArr[i].cells.length, heightTotal = 0; // increment through each column widget for (var j = 0; j < cellsLength; j++) { var height = columnArr[i].cells[j].subcells[0].elements[0].height; heightTotal += height; } columnHeight.push(heightTotal); // assign max aggregate column height heightValue = columnHeight.reduce(function (a, b) { return Math.max(a, b); }); } targetWindow.postMessage({ heightValue: heightValue }, targetOrigin); }); // Within your PARENT APPLICATION: window.addEventListener('message', receiveMessage, false); function receiveMessage(event) { // replace with your sisense application origin var sendingOrigin = 'http://reporting.mytestapp.com:8080'; if (event.origin !== sendingOrigin) { throw new Error('bad origin:', event.origin); } else { try { // replace with your iFrame ID document.getElementById('ifm').height = event.data.heightValue; } catch (err) { throw new Error(err.name + ':', err.message); } } }

      intapiuser
      intapiuserPosted 3 years ago • Last reply 2 years ago
      1
               
    • Blog banner
      • BloxChevronRightIcon

      Sync Blox Input Element with Current Filter State

                                                                                                                       

      Sync Blox Input Element with Current Filter State When creating a Blox widget  and altering a filter within a dashboard or widget, it is sometimes advantageous to modify the default behavior, so that upon activating a Blox action , the Blox widget input or inputs retain the recently entered values instead of reverting to the default Blox inputs set in the Blox template. Additionally, when a user has permission to modify the relevant filter directly, bypassing Blox widget actions, and subsequently making changes to the filter that the Blox widget is designed to manipulate, the default Blox input values may no longer match with the current state of the filter. The following widget script offers a solution for this specific requirement by maintaining synchronization between the Blox input and the values of a selected filter, identifying the filter by its title. This approach guarantees that when the Blox widget is used to modify the filter, the input values persist, reflecting the current filter state as potentially altered by the Blox action. This synchronization ensures alignment between the Blox inputs and the filter values.     // Preserve Blox filter inputs after filter change, sync with current filter state widget.on('ready', function () { // Modify to match the relevant filter title let filterModifiedName = "days" filterModifiedName = filterModifiedName.toLowerCase() // Variable containing the Dashboard filter modified by Blox let modifiedFilter = widget.dashboard.filters.$$items.find(function (filterItem) { return !filterItem.isCascading && filterItem.jaql && filterItem.jaql.title && filterItem.jaql.title.toLowerCase() === filterModifiedName; }); // If relevant filter has from and to values selected (change as needed if filter uses members or any other type of filter) if (modifiedFilter && modifiedFilter.jaql && modifiedFilter.jaql.filter && modifiedFilter.jaql.filter.from && modifiedFilter.jaql.filter.to) { // Filter from and to value from dashboard filters let dateFilterFrom = modifiedFilter.jaql.filter.from let dateFilterTo = modifiedFilter.jaql.filter.to // Change filter ID selector as needed, to match ID parameter of input Elements let inputElementFrom = $('[id="filters[0].filterJaql.from"]', element)[0] let inputElementTo = $('[id="filters[0].filterJaql.to"]', element)[0] // Set value of inputs to value of filter inputElementFrom.value = dateFilterFrom inputElementTo.value = dateFilterTo } })     For Blox inputs that involve dates and modifications to date type filters, there is a dedicated Community article that includes a widget script for this particular functionality. Although initially developed for Blox-type widgets, this script can be adapted with minor adjustments to affect other elements on the page or non-Blox widgets. The fundamental principle involves utilizing the dashboard or widget filter object to retrieve filter values or values and then using a dashboard or widget (JavaScript) script to manipulate the HTML content of a text element or input present on the dashboard page. Share your experience in the comments! 

      Jeremy Friedel
      Jeremy FriedelPosted 2 years ago
      0
               
    • Blog banner
      • Widget & Dashboard ScriptsChevronRightIcon

      Animating Sisense Title Elements and Widgets Using CSS Animations

                                                                                                                       

      Animating Sisense Title Elements and Widgets Using CSS Animations Animating Sisense widget elements and dashboard/widget titles is a possible way to enhance the visual appeal and user experience of a Sisense dashboard or highlight a particular part of a dashboard or widget. Sisense widgets and titles are standard HTML elements, making it possible to apply animation using solely standard CSS stylesheets, much like other HTML elements on non-Sisense pages. Animations can include effects like fading widgets in or out and cyclic changes in the text color of widgets or titles. For documentation of CSS animation, you can explore the following pages which include many examples: Mozilla Developer Documentation on CSS Animation CSS Text Animations Examples CSS Text Animations Examples W3Schools CSS3 Animations In Sisense, widget and dashboard scripting can be used to add additional CSS stylesheets to the header of the HTML page using Javascript. CSS selector rules can be specific to a widget OID (all widgets have a unique OID, it is visible in the widget editor URL) in the selector string and not apply to any other widgets. Any valid CSS can be added via widget or dashboard scripting. Here's an example of a widget script that pertains to a text widget and animates the text color :     widget.on('ready', function () { var style = document.createElement('style'); // Any valid CSS will work in styleString and be appended to the CSS, including any CSS animation, modify as needed var styleString = ` [widgetid='${widget.oid}'] .wd div font { animation: color-animation 10s linear infinite; } @keyframes color-animation { 0% { color: blue } 32% { color: red } 45% { color: green } 55% { color: black } 66% { color: blue) } 80% { color: grey } 100% { color: purple } } ` if (style.styleSheet) { style.styleSheet.cssText = styleString; } else { style.appendChild(document.createTextNode(styleString)); } document.getElementsByTagName('head')[0].appendChild(style); });   Here's another example of CSS animation, in this example a widget is faded in and then out by modifying opacity :     widget.on('ready', function () { var style = document.createElement('style'); // Any valid CSS will work in styleString and be appended to the CSS, including any CSS animation, modify as needed var styleString = ` [widgetid='${widget.oid}'] .wd { animation: fadeIn ease 8s infinite; } @keyframes fadeIn { 0% { opacity: 0; } 50% { opacity: 1; } 100% { opacity: 0; } } ` if (style.styleSheet) { style.styleSheet.cssText = styleString; } else { style.appendChild(document.createTextNode(styleString)); } document.getElementsByTagName('head')[0].appendChild(style); });     It is also possible to animate other HTML elements within a Sisense dashboard, using the same principles.   Share in the comments if this is a code you tried and let us know how it went! 

      Jeremy Friedel
      Jeremy FriedelPosted 2 years ago
      0
               
      • Add-ons & Plug-InsChevronRightIcon

      Embedding a Google Doc or Sheet in Your Dashboard

                                       

      The iFrame Widget Plugin is a powerful tool that enables you to embed any web page that you wish into your dashboard, using its URL. A great use of the iFrame widget can also be used to Embed Google Docs or Sheets. The quick access to the document or sheet can be used to store notes and comments derived from a dashboard's results. Furthermore, it can be utilized for reading or even updating the same spreadsheet being used as an Elasticube's data source (write back). Other users with whom the dashboard is shared will be able to view the content of the doc, only if its sharing settings allow it. You can decide if the user must be logged in to Google, and if so, who may view and who may edit. For more information about it, see this Google article . To embed a single fixed doc: 1. Get the required doc's URL. If you want the doc to be editable, use the shareable URL . If you want it to be presented in presentation only mode, use the Published URL . 2. Create a new iFrame widget. 3. In the Widget's edit mode, provide the document's URL to the iFrame.         4. Apply your changes. To Embed a dynamic, filter responsive selection of documents: 1. Create a table that would list all of the relevant documents' description and URL. In the below example, we are using the owner's Email as the description that we will filter by: 2. Import the table into an existing or to a new Elasticube. The table can exist as a stand-alone island, and enable filtering based on its inline dimensions, or it can be integrating into an existing model, to enable interaction with its additional filters.                  3. Create a new iFrame widget based on the same cube. In the widget's URL panel, add the field that holds the documents' URLs 4. Apply changes and inspect your dashboard. You can now choose the required document by choosing the relevant Email:

      intapiuser
      intapiuserPosted 3 years ago
      0