AOTP view with enhanced UI layer
A dashboard-as-a-component pattern using Sisense native widgets with added custom UI components to enrich the experience. This includes buttons, tooltips, tabs, or other UI enhancements layered on top of standard visualizations. Provides a streamlined user experience that preserves the power of Sisense widgets while enabling enhanced interactions and bespoke design elements. Check out this public Github repo for detailed steps.62Views0likes0CommentsComposable combo chart for flexible self-Service
A single, dynamic charting component built entirely with the Compose SDK. Users can interactively select dimensions, swap measures, and apply filters - turning a single chart into a flexible, self-contained dashboard experience. Empowers end-users with self-service exploration without requiring multiple dashboards, reducing dashboard sprawl and enhancing agility. Check out this public Github repo for specific guidance.83Views0likes0CommentsJira Automation App with Sisense + GenAI
A fully automated analytics-to-action workflow. Sisense triggers threshold-based alerts using Compose SDK queries, then through a POST request directly to Jira via API. The ticket is auto-populated with narrative insights generated using Sisense’s GenAI capabilities. Closes the loop between insight and action - reducing response times, improving accountability, and eliminating manual ticket creation in operational workflows. Check out this public Github repo for detailed steps.52Views0likes0CommentsBuilding a custom dashboard creation interface with compose SDK & Sisense rest APIs
Overview This use case demonstrates how to build a custom, user-friendly dashboard creation interface that leverages Sisense's powerful analytics engine while providing a tailored user experience. By combining Sisense's robust backend capabilities with a modern React frontend, organizations can create workflows that align perfectly with their specific needs and branding requirements. Value Proposition Why Build a Custom Interface? 🎨 Tailored Designer UX: Create interfaces that match your organization's design language and workflows, rather than being constrained by out-of-the-box UI limitations. 👥 Improved User Experience: Design streamlined workflows that reduce complexity for end users while maintaining full analytical capabilities. 🔒 Maintain Enterprise Controls: Leverage Sisense's built-in role-based access controls, security, and governance features without modification. ⚡ Faster Time-to-Insight: Reduce the steps required for users to create meaningful dashboards and visualizations. 🏢 Brand Consistency: Ensure dashboard creation tools match your organization's visual identity and user experience standards. Technical Stack Frontend Libraries { "@sisense/sdk-ui": "^1.x.x", "@mui/icons-material": "^5.x.x", "@mui/material": "^5.x.x", "react": "^18.x.x", "typescript": "^4.x.x" } Backend Libraries { "express": "^4.18.2", "cors": "^2.8.5", "dotenv": "^16.3.1" } Implementation Guide 1. Backend API Service Setup Create a Node.js/Express service to proxy Sisense API calls and handle authentication: // server.js const express = require('express'); const cors = require('cors'); require('dotenv').config(); const app = express(); app.use(cors()); app.use(express.json()); // Sisense Configuration const SISENSE_CONFIG = { baseUrl: process.env.SISENSE_BASE_URL, apiToken: process.env.SISENSE_API_TOKEN, dataSource: { title: 'Sample ECommerce', id: 'aLOCALHOST_aSAMPLEIAAaECOMMERCE', // ... other datasource config } }; // Helper function for Sisense API requests async function sisenseApiRequest(endpoint, method = 'GET', body = null) { const url = `${SISENSE_CONFIG.baseUrl}/api/v1${endpoint}`; const options = { method, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${SISENSE_CONFIG.apiToken}`, }, }; if (body) options.body = JSON.stringify(body); const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`Sisense API Error: ${response.status} ${errorText}`); } return await response.json(); } 2. Key API Endpoints Dashboard Management // List all dashboards app.get('/api/dashboards', async (req, res) => { try { const dashboards = await sisenseApiRequest('/dashboards'); res.json({ success: true, dashboards: dashboards.map(d => ({ id: d.oid || d._id, title: d.title, created: d.created, lastUpdated: d.lastUpdated })) }); } catch (error) { res.status(500).json({ error: 'Failed to list dashboards', details: error.message }); } }); // Create new dashboard app.post('/api/dashboards', async (req, res) => { try { const { title, description } = req.body; const payload = { title: title.trim(), desc: description || 'Dashboard created from custom UI', datasource: SISENSE_CONFIG.dataSource, layout: { type: 'columnar', columns: [{ width: 100, cells: [] }] } }; const dashboard = await sisenseApiRequest('/dashboards', 'POST', payload); res.json({ success: true, dashboard: { id: dashboard.oid || dashboard._id, title: dashboard.title, created: dashboard.created, } }); } catch (error) { res.status(500).json({ error: 'Failed to create dashboard', details: error.message }); } }); Widget Creation // Add widgets to dashboard app.post('/api/dashboards/:dashboardId/widgets', async (req, res) => { try { const { dashboardId } = req.params; const { widgets } = req.body; const results = []; for (let i = 0; i < widgets.length; i++) { const widget = widgets[i]; if (widget.widgetType !== 'chart') continue; const payload = processWidgetForSisense(widget, i); const savedWidget = await sisenseApiRequest( `/dashboards/${dashboardId}/widgets`, 'POST', payload ); results.push({ id: savedWidget.oid || savedWidget._id, title: savedWidget.title, type: savedWidget.type, }); } res.json({ success: true, saved: results.length, widgets: results }); } catch (error) { res.status(500).json({ error: 'Failed to create widgets', details: error.message }); } }); 3. Widget Data Processing Transform Sisense SDK objects to API-compatible format: function processWidgetForSisense(widget, index = 0) { const chartWidget = widget; // Chart type mapping const chartTypeMapping = { 'column': 'bar', 'bar': 'bar', 'line': 'line', 'pie': 'pie' }; const sisenseChartType = chartTypeMapping[chartWidget.chartType] || 'bar'; // Extract and process field data const category = chartWidget.dataOptions?.category?.[0]; const measure = chartWidget.dataOptions?.value?.[0]; const categoryName = category?._name || category?.name || 'Unknown Category'; const measureName = measure?._name || measure?.name || 'Unknown Measure'; // Format for Sisense API bracket notation const categoryDim = `[Commerce.${categoryName.replace(/\s+/g, ' ')}]`; const measureDim = `[Commerce.${measureName.replace(/\s+/g, ' ').replace('sum ', '')}]`; return { title: chartWidget.title || `Chart Widget ${index + 1}`, desc: 'Widget created from custom UI', datasource: SISENSE_CONFIG.dataSource, type: `chart/${sisenseChartType}`, subtype: sisenseChartType, metadata: { ignore: { ids: [], dimensions: [], all: false }, panels: [ { name: 'categories', items: category ? [{ jaql: { dim: categoryDim, datatype: 'text', title: categoryName, }, field: { id: `[Commerce.${categoryName.replace(/\s+/g, '')}]_1`, index: 0, }, format: {}, }] : [], }, { name: 'values', items: measure ? [{ jaql: { dim: measureDim, datatype: 'numeric', title: measureName, agg: 'sum', }, field: { id: `[Commerce.${measureName.replace(/\s+/g, '').replace('sum', '')}]_1`, index: 0, }, format: {}, }] : [], }, { name: 'break by', items: [] }, ], }, style: { widgetDesign: { widgetSpacing: 'none', widgetBackgroundColor: '#FFFFFF', // ... additional styling }, height: 280, }, }; } 4. Frontend Implementation Dashboard Selection Interface // React component for dashboard selection const DashboardSelector = () => { const [existingDashboards, setExistingDashboards] = useState<DashboardSummary[]>([]); const [isLoading, setIsLoading] = useState(false); const fetchDashboards = async () => { setIsLoading(true); try { const response = await fetch(`${BACKEND_URL}/api/dashboards`); const data = await response.json(); setExistingDashboards(data.dashboards || []); } catch (error) { console.error('Failed to fetch dashboards:', error); } finally { setIsLoading(false); } }; useEffect(() => { fetchDashboards(); }, []); return ( <div> {existingDashboards.map((dashboard) => ( <DashboardCard key={dashboard.id} dashboard={dashboard} onSelect={(id) => selectDashboard(id)} /> ))} </div> ); }; Widget Creation Integration // Integration with Sisense Compose SDK const WidgetBuilder = ({ dashboardId }: { dashboardId: string }) => { const [newWidgets, setNewWidgets] = useState<WidgetProps[]>([]); const { dashboard } = useGetDashboardModel({ dashboardOid: dashboardId, includeFilters: true, includeWidgets: true, }); const saveWidgets = async () => { try { const response = await fetch( `${BACKEND_URL}/api/dashboards/${dashboardId}/widgets`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ widgets: newWidgets }), } ); const result = await response.json(); if (result.success) { setNewWidgets([]); alert(`Saved ${result.saved} widgets successfully!`); } } catch (error) { console.error('Failed to save widgets:', error); } }; return ( <div> {dashboard && ( <Dashboard {...dashboardModelTranslator.toDashboardProps(dashboard)} config={{ filtersPanel: { visible: false } }} /> )} <button onClick={saveWidgets}> Save Widgets ({newWidgets.length}) </button> </div> ); }; Sisense API Endpoints Used Core Endpoints GET /api/v1/dashboards - List all dashboards POST /api/v1/dashboards - Create new dashboard GET /api/v1/dashboards/{id} - Get specific dashboard POST /api/v1/dashboards/{id}/widgets - Add widgets to dashboard Authentication All requests require Bearer token authentication: You can set this up with the SSO too - for the example I just hardcoded token. Authorization: Bearer {your-sisense-api-token} Environment Setup Backend Environment Variables # .env PORT=3001 SISENSE_BASE_URL=https://your-sisense-instance.com SISENSE_API_TOKEN=your_api_token_here Frontend Environment Variables # .env REACT_APP_BACKEND_URL=http://localhost:3001 Key Considerations Licensing Requirements Designer License Required: This solution requires Sisense Designer licenses or above for users who will be creating dashboards Token Management: Implement proper token rotation and security practicesBenefits Realized ✅ Improved User Adoption: Create tailored workflows that align with your product's UX. ✅ Consistent Branding: Custom UI matches organizational design language ✅ Faster Dashboard Creation: Streamlined workflow reduces clicks and complexity ✅ Maintained Security: Full Sisense RBAC and security features preserved Conclusion This approach demonstrates how organizations can leverage Sisense's powerful analytics capabilities while providing users with a custom interface that fits their specific workflow requirements. By maintaining the underlying Sisense security and data governance features while improving the user experience, teams can achieve faster adoption and more efficient dashboard creation processes. The modular architecture of Compose SDK also allows for future enhancements such as custom widget templates, automated dashboard generation, and integration with existing enterprise applications.165Views1like0CommentsAG grid integration with Sisense data
This integration combines Sisense’s Compose SDK with AG Grid's high-performance data tables in a React framework. Queries are powered directly from Sisense, enabling seamless interaction with native data models while preserving built-in security and row-level permissions. Delivers enterprise-grade tabular analytics with powerful filtering, sorting, and exporting features, while maintaining centralized governance and access control. Check out this public Github repo for detailed steps.81Views0likes0Comments