RESOURCES

How to Create Dashboard in React

Explore the intricacies of creating a powerful dashboard using React with our comprehensive guide

At Databrain, we've helped several dozen companies ship 1000+ customer-facing dashboards from fast growing startups that want to ship dashboards quickly to large cloud 100 enterprises that want robust, multi-tenant dashboards across various geographies globally. Here's what we've learned about building them.

React remains the dominant framework for building interactive dashboards in 2026. But the ecosystem has changed dramatically — React 19 introduced Server Components and the React Compiler, shadcn/ui replaced Material UI as the default component library, and TanStack Query became the standard for server state management.

If you're evaluating whether to build a dashboard from scratch or use a pre-built solution, start with our embedded analytics overview to understand the trade-offs.

This guide covers two paths:

  1. Build a custom React dashboard from scratch using modern 2026 patterns
  2. Embed production-ready dashboards in minutes using Databrain's web components

What you'll build:

  1. A React 19 dashboard with Vite and TypeScript
  2. Data fetching with TanStack Query and caching
  3. Authentication with JWT and role-based access control
  4. Real-time data updates
  5. Integration with Databrain for production analytics

Prerequisites:

  1. Node.js 20+ (Vite 8 requires 20.19+ or 22.12+) and npm 10+
  2. Basic React and TypeScript knowledge

Choosing Your Approach: Build vs. Embed vs. Template

Before writing code, the first decision is how to build your dashboard. Here's an honest comparison:

Approach Time to Ship Cost Flexibility Maintenance

Approach Time to Ship Cost Flexibility Maintenance
Custom build (this guide) 2-6 weeks Developer time only Full control You own it all
Template (TailAdmin, MUI) 2-3 days $0-300 one-time Limited to template Fork maintenance
Framework (Refine, AdminJS) 1-2 weeks $0-200/mo Opinionated Framework updates
Embedded analytics (Databrain) 1-2 days $99-499/mo 48 chart types, RBAC, multi-tenant Fully managed

When to build custom: You need a unique UI, you're learning React, or your dashboard is simple enough that a library adds unnecessary complexity.

When to use embedded analytics: You're building customer-facing dashboards for a SaaS product, need multi-tenancy, and can't afford 6 months of engineering on charts, drill-downs, and data pipelines. See our detailed embedded analytics vs. BI comparison to understand the differences.

90% of our customers tried building custom first, or using an open source solution like Apache Superset or Metabase, then switched to DataBrain. The maintenance cost of 20+ chart types, with complex drill-down and interactive dashboard behavior was the breaking point.

Step 1: Set Up React 19 with Vite and TypeScript

Vite is the standard build tool for React in 2026 — the React team officially recommends it as the primary alternative after deprecating Create React App in February 2025. Vite 8 (released March 2026) ships with Rolldown — a Rust-based bundler that replaced Rollup — delivering significantly faster builds.

Bash
npm create vite@latest my-dashboard -- --template react-ts
cd my-dashboard
npm install

Why React 19 Matters for Dashboards

React 19 shipped features specifically useful for data-heavy applications like dashboards:

  1. React Compiler Stable since October 2025, the compiler automatically memoizes components and hooks — eliminating the need for manual useMemo/useCallback. New Vite, Next.js, and Expo projects ship with it enabled by default. Meta reports up to 12% faster initial loads and 2.5x faster interactions in production. For dashboards with dozens of components re-rendering on filter changes, this is a significant win.
  2. use() API — Read promises and context directly in render, simplifying data-fetching patterns.
  3. Server Components — Render data-heavy components on the server, reducing JavaScript shipped to the client. Server Components work best with frameworks like Next.js — for client-side Vite apps, you won't use these directly.
  4. Actions and useOptimistic — Handle form submissions (filter changes, date range selections) with automatic pending states.

For client-side dashboards built with Vite, you'll benefit from the React Compiler (enabled by default in new projects), the use() API, and Actions. Server Components require a framework like Next.js.

Step 2: Install Your Component Library and Data Layer

shadcn/ui for Dashboard Components

shadcn/ui has become the fastest-growing component library in React. The State of React 2024 survey showed it doubling from 20% to 42% usage in a single year, with 80% positivity — the highest of any library. By 2025, it was on the verge of overtaking MUI for the top spot. Its popularity is partly driven by AI coding tools (Cursor, v0, Lovable) that default to generating shadcn/ui code. It provides composable components you own and can customize.

Bash
npx shadcn@latest init
npx shadcn@latest add card button table tabs separator skeleton badge

TanStack Query for Server State

Every dashboard fetches data from APIs. TanStack Query (React Query) handles caching, background refetching, stale-while-revalidate, and error states — problems you'd otherwise solve manually with useState + useEffect.

Bash
npm install @tanstack/react-query

Zustand for Client State

For client-side state like sidebar open/closed, selected filters, and dark mode preference, Zustand provides a minimal, performant store without the boilerplate of Redux.

Bash
npm install zustand

Recharts for Data Visualization

For custom-built charts, Recharts is the most popular React charting library. It's composable, responsive, and works well with TypeScript.

Bash
npm install recharts

Step 3: Build the Dashboard Layout

Create a responsive dashboard shell with a sidebar, header, and main content area using CSS Grid and shadcn/ui components.

src/components/DashboardLayout.tsx:

TSX
import { useState } from 'react';import { Button } from '@/components/ui/button';import { cn } from '@/lib/utils';‍interface DashboardLayoutProps {children: React.ReactNode;}‍const navItems = [{ id: 'overview', label: 'Overview', icon: '📊' },{ id: 'analytics', label: 'Analytics', icon: '📈' },{ id: 'customers', label: 'Customers', icon: '👥' },{ id: 'settings', label: 'Settings', icon: '⚙️' },];‍export function DashboardLayout({ children }: DashboardLayoutProps) {const [activeItem, setActiveItem] = useState('overview');const [sidebarOpen, setSidebarOpen] = useState(true);‍return (<div className="grid min-h-screen grid-cols-[auto_1fr]"><asideclassName={cn('border-r bg-card transition-all duration-300',sidebarOpen ? 'w-60' : 'w-16')}><div className="flex h-14 items-center border-b px-4"><Buttonvariant="ghost"size="sm"onClick={() => setSidebarOpen(!sidebarOpen)}>{sidebarOpen ? '◀' : '▶'}</Button>{sidebarOpen && (<span className="ml-2 font-semibold">Dashboard</span>)}</div><nav className="flex flex-col gap-1 p-2">{navItems.map((item) => (<Buttonkey={item.id}variant={activeItem === item.id ? 'secondary' : 'ghost'}className="justify-start"onClick={() => setActiveItem(item.id)}><span className="text-lg">{item.icon}</span>{sidebarOpen && <span className="ml-2">{item.label}</span>}</Button>))}</nav></aside><main className="flex flex-col"><header className="flex h-14 items-center justify-between border-b px-6"><h1 className="text-lg font-semibold capitalize">{activeItem}</h1></header><div className="flex-1 p-6">{children}</div></main></div>);}

Step 4: Fetch and Display Data with TanStack Query

This is where dashboards differ from typical React apps. Dashboard data is server state — it comes from APIs, changes frequently, and multiple components often need the same data.

src/hooks/useDashboardData.ts:

TS
import { useQuery } from '@tanstack/react-query';

interface DashboardMetrics {
totalRevenue: number;
activeUsers: number;
conversionRate: number;
avgOrderValue: number;
revenueByMonth: { month: string; revenue: number }[];
}

async function fetchDashboardMetrics(): Promise<DashboardMetrics> {
const response = await fetch('/api/dashboard/metrics');
if (!response.ok) throw new Error('Failed to fetch metrics');
return response.json();
}

export function useDashboardMetrics() {
return useQuery({
queryKey: ['dashboard', 'metrics'],
queryFn: fetchDashboardMetrics,
staleTime: 30_000, // Data is fresh for 30 seconds
refetchInterval: 60_000, // Background refetch every 60 seconds
});
}

src/components/MetricCards.tsx:

TSX
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Skeleton } from '@/components/ui/skeleton';
import { useDashboardMetrics } from '@/hooks/useDashboardData';

export function MetricCards() {
const { data, isLoading, error } = useDashboardMetrics();

if (error) return <div className="text-destructive">Failed to load metrics</div>;

const metrics = [
{ title: 'Total Revenue', value: data?.totalRevenue, format: (v: number) => `$${v.toLocaleString()}` },
{ title: 'Active Users', value: data?.activeUsers, format: (v: number) => v.toLocaleString() },
{ title: 'Conversion Rate', value: data?.conversionRate, format: (v: number) => `${v}%` },
{ title: 'Avg Order Value', value: data?.avgOrderValue, format: (v: number) => `$${v.toFixed(2)}` },
];

return (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{metrics.map((metric) => (
<Card key={metric.title}>
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">
{metric.title}
</CardTitle>
</CardHeader>
<CardContent>
{isLoading ? (
<Skeleton className="h-8 w-24" />
) : (
<p className="text-2xl font-bold">
{metric.format(metric.value!)}
</p>
)}
</CardContent>
</Card>
))}
</div>
);
}

Notice that MetricCards and the RevenueChart below both call useDashboardMetrics(). TanStack Query deduplicates these - only one network request is made, and both components share the same cached data. This is the key architectural pattern: colocate data fetching with the component that needs it, and let the query layer handle deduplication and caching.

Also note the use of <Skeleton> instead of a spinner. Skeleton screens reduce perceived load time and prevent layout shift - both important for Core Web Vitals.

Step 5: Add Charts with Recharts

src/components/RevenueChart.tsx:

TSX
import {
LineChart, Line, XAxis, YAxis, CartesianGrid,
Tooltip, ResponsiveContainer,
} from 'recharts';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useDashboardMetrics } from '@/hooks/useDashboardData';

export function RevenueChart() {
const { data, isLoading } = useDashboardMetrics();

return (
<Card>
<CardHeader>
<CardTitle>Revenue Over Time</CardTitle>
</CardHeader>
<CardContent>
{isLoading ? (
<div className="h-[300px] animate-pulse rounded bg-muted" />
) : (
<ResponsiveContainer width="100%" height={300}>
<LineChart data={data?.revenueByMonth}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Line
type="monotone"
dataKey="revenue"
stroke="hsl(var(--primary))"
strokeWidth={2}
/>
</LineChart>
</ResponsiveContainer>
)}
</CardContent>
</Card>
);
}

When Recharts Isn't Enough

Recharts handles common chart types well — line, bar, pie, area, scatter. But production dashboards often need more: Sankey diagrams, geo maps, Gantt charts, gauges, waterfall charts, pivot tables. For a broader comparison of options, see our guides on React chart libraries and JavaScript chart libraries.

At Databrain, we built a visualization engine spanning 48 chart types using ECharts for the 80% of charts where built-in interactivity saves months, and D3 for the 20% where pixel-level control matters. If your dashboard needs more than 5-6 chart types, building each one from scratch becomes the dominant cost — and it's where embedded analytics tools pay for themselves.

Step 6: Authentication and Role-Based Access Control

Production dashboards need authentication. Here's a minimal JWT pattern with React Router.

src/hooks/useAuth.ts:

TS
import { create } from 'zustand';

interface User {
id: string;
email: string;
role: 'admin' | 'manager' | 'viewer';
}

interface AuthState {
user: User | null;
token: string | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}

export const useAuth = create<AuthState>((set) => ({
user: null,
token: null,
login: async (email, password) => {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const data = await res.json();
set({ user: data.user, token: data.token });
},
logout: () => set({ user: null, token: null }),
}));

src/components/PermissionGate.tsx:

TSX
import { useAuth } from '@/hooks/useAuth';

interface PermissionGateProps {
allowedRoles: string[];
children: React.ReactNode;
fallback?: React.ReactNode;
}

export function PermissionGate({
allowedRoles,
children,
fallback = null,
}: PermissionGateProps) {
const { user } = useAuth();
if (!user || !allowedRoles.includes(user.role)) return <>{fallback}</>;
return <>{children}</>;
}
Usage:
<PermissionGate allowedRoles={['admin', 'manager']}>
<DangerousSettingsPanel />
</PermissionGate>

For embedded dashboards, Databrain handles authentication through guest tokens with built-in row-level security — the token scopes what data a user can see at the database level, so you don't need to implement RBAC yourself.

Step 7: Performance Optimization

Dashboards are the most performance-sensitive pages in any application. Multiple charts, real-time data, and complex layouts compound to create performance challenges.

Code Splitting by Route

Lazy-load dashboard pages so users only download the JavaScript for the page they're viewing:

TSX
import { lazy, Suspense } from 'react';

const OverviewPage = lazy(() => import('./pages/Overview'));
const AnalyticsPage = lazy(() => import('./pages/Analytics'));
const CustomersPage = lazy(() => import('./pages/Customers'));

function App() {
return (
<Suspense fallback={<DashboardSkeleton />}>
<Routes>
<Route path="/" element={<OverviewPage />} />
<Route path="/analytics" element={<AnalyticsPage />} />
<Route path="/customers" element={<CustomersPage />} />
</Routes>
</Suspense>
);
}

Core Web Vitals Targets

Metric Target Why It Matters

Metric Target Why It Matters
LCP (Largest Contentful Paint) < 2.5s Google ranking signal; users perceive >2.5s as slow
INP (Interaction to Next Paint) < 200ms Filter changes and drill-downs must feel instant
CLS (Cumulative Layout Shift) < 0.1 Charts loading should not push content around

Practical tips for dashboards:

  1. Use <Skeleton> components to reserve space before data loads (prevents CLS)
  2. Set explicit height on chart containers (prevents layout shift)
  3. Use TanStack Query's staleTime to serve cached data instantly while refetching in the background
  4. The React Compiler handles memoization automatically — if you're on an older project without it, profile and add useMemo/useCallback where needed

Step 8: Real-Time Data Updates

For dashboards that need live data (operations dashboards, trading screens, monitoring), add WebSocket support:

TS
import { useEffect } from 'react';
import { useQueryClient } from '@tanstack/react-query';

export function useRealtimeMetrics() {
const queryClient = useQueryClient();

useEffect(() => {
const ws = new WebSocket('wss://your-api.com/ws/metrics');

ws.onmessage = (event) => {
const update = JSON.parse(event.data);
queryClient.setQueryData(['dashboard', 'metrics'], (old: any) => ({
...old,
...update,
}));
};

return () => ws.close();
}, [queryClient]);
}

This pattern updates TanStack Query's cache directly, so every component using useDashboardMetrics() re-renders with the new data automatically — no prop drilling, no manual state management.

Step 9: Putting It All Together

src/App.tsx:

TSX
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { DashboardLayout } from '@/components/DashboardLayout';
import { MetricCards } from '@/components/MetricCards';
import { RevenueChart } from '@/components/RevenueChart';

const queryClient = new QueryClient();

function App() {
return (
<QueryClientProvider client={queryClient}>
<DashboardLayout>
<div className="space-y-6">
<MetricCards />
<div className="grid gap-6 lg:grid-cols-2">
<RevenueChart />
{/* Add more chart components here */}
</div>
</div>
</DashboardLayout>
</QueryClientProvider>
);
}

export default App;

Dashboard with React

Run it:

npm run dev

Visit http://localhost:5173 to see your dashboard.

Production Alternative: Embed Dashboards with Databrain

Building a custom dashboard teaches you React fundamentals. But production dashboards for SaaS products need significantly more than what's covered above: 48 chart types, drill-downs with cross-dashboard filtering, pivot tables, scheduled email reports, multi-tenant row-level security, CSV/PDF export, and theming that matches your brand.

BerryBox, an insure-tech startup, tried embedding Power BI into their SaaS app and hit roadblocks with multi-source data integration, risky deployments, and a steep DAX learning curve. They switched to Databrain and went from kickoff to production in 3 weeks — saving $250K and freeing up 6 months of engineering. Similarly, Freightify tried building analytics in-house before realizing the cost, then deployed Databrain in 1 week.

Databrain ships as two HTML custom elements — <dbn-dashboard> and <dbn-metric> — that work in React, Vue, Angular, or plain HTML. They render inside Shadow DOM for style isolation, so they won't conflict with your existing CSS. Under the hood, they're a full React analytics application bridged to Web Components using r2wc (React to Web Component).

Install the Plugin

Bash
npm install @databrainhq/plugin

Note: As of v0.16, the plugin's peer dependencies specify React 18. It works with React 19 in practice, but npm may show a peer dependency warning. You can use --legacy-peer-deps or an overrides field in package.json if needed.

Backend: Generate a Guest Token

Guest tokens scope what data a user can see. Your backend generates them — the token is never exposed to the client directly.

Node.js / Express:

JavaScript
import express from 'express';
const router = express.Router();

router.post('/api/guest-token', async (req, res) => {
const { clientId } = req.body;

const response = await fetch(
`${process.env.DATABRAIN_API_URL}/api/v2/guest-token/create`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DATABRAIN_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
clientId,
dataAppName: process.env.DATABRAIN_DATA_APP_NAME,
permissions: {
isEnableManageMetrics: false,
isEnableCustomizeLayout: false,
isEnableUnderlyingData: false,
isEnableDownloadMetrics: true,
isShowDashboardName: true,
},
}),
}
);

const data = await response.json();
if (response.ok && data.token) {
res.json({ token: data.token });
} else {
res.status(400).json({ error: data?.error?.message || 'Token creation failed' });
}
});

The permissions object controls what embedded users can do - create metrics, customize layouts, download data, see the sidebar. This is how you implement RBAC without building it yourself.

Frontend: React Component

TSX
import { useEffect, useState, useCallback } from 'react';
import '@databrainhq/plugin/web';

declare global {
namespace JSX {
interface IntrinsicElements {
'dbn-dashboard': any;
}
}
}

export function AnalyticsDashboard({ dashboardId }: { dashboardId: string }) {
const [token, setToken] = useState<string | null>(null);

const fetchToken = useCallback(async () => {
const res = await fetch('/api/guest-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ clientId: 'your-tenant-id' }),
});
const data = await res.json();
setToken(data.token);
}, []);

useEffect(() => {
fetchToken();
const interval = setInterval(fetchToken, 50 * 60 * 1000);
return () => clearInterval(interval);
}, [fetchToken]);

if (!token) return <div>Loading analytics...</div>;

return (
<dbn-dashboard
token={token}
dashboard-id={dashboardId}
enable-download-csv
enable-email-csv
/>
);
}

That's it. The <dbn-dashboard> element handles all 48 chart types, drill-downs with cross-dashboard filtering, theming, and real-time data — configured in Databrain's UI, not in your code.

Next.js (App Router)

For Next.js, the web component needs to run on the client. Create an API route for token generation and a client component for the embed:

app/api/guest-token/route.ts:

TS
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
const { clientId } = await request.json();

const response = await fetch(
`${process.env.DATABRAIN_API_URL}/api/v2/guest-token/create`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.DATABRAIN_API_TOKEN}`,
},
body: JSON.stringify({
clientId,
dataAppName: process.env.DATABRAIN_DATA_APP_NAME,
}),
}
);

const data = await response.json();
return NextResponse.json(data);
}

app/components/DatabrainEmbed.tsx:

TSX
'use client';

import { useEffect, useState } from 'react';
import '@databrainhq/plugin/web';

export default function DatabrainEmbed({ dashboardId }: { dashboardId: string }) {
const [token, setToken] = useState<string | null>(null);

useEffect(() => {
fetch('/api/guest-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ clientId: 'your-client-id' }),
})
.then((res) => res.json())
.then((data) => setToken(data.token));
}, []);

if (!token) return <div>Loading...</div>;

return <dbn-dashboard token={token} dashboard-id={dashboardId} />;
}

The 'use client' directive is required because @databrainhq/plugin/web registers custom elements, which requires the DOM.

Customize the Theme

Match the embedded dashboard to your brand using the theme prop:

For full theming control including chart palettes, breakpoints, and metric layout columns, see the component API reference.

What You Get Out of the Box

Feature Custom Build With Databrain

Capability Custom Build Databrain
Chart types Build each one 48 built-in (bar, line, pie, Sankey, gauge, geo maps, pivot, Gantt, and more)
Drill-downs Build from scratch Declarative config, cross-dashboard filtering
Data sources Custom API integration 18 connectors (PostgreSQL, MySQL, MongoDB, Snowflake, BigQuery, ClickHouse, Databricks, Redshift, Elasticsearch, Athena, Trino, and more)
Multi-tenancy Build RLS layer Built-in row-level security via guest tokens
Export Build PDF/CSV generation One-click CSV, PDF, scheduled email reports
Style isolation CSS Modules or Tailwind scoping Shadow DOM with injected CSS
Time to production 2-6 months 1-2 days

Procuremrnt Management dashboard

React Dashboard Visualization Libraries Compared

If you're building charts from scratch, here's how the major libraries compare:

Library Bundle Size (gzipped) Chart Types TypeScript Best For

Library Bundle Size Chart Types TypeScript Support Best For
Recharts ~135 KB (full), ~50 KB with selective imports 11 Yes Simple dashboards, React-first API
Chart.js ~67 KB 8 Via @types/chart.js Lightweight, canvas-based charts
Apache ECharts ~273 KB (full), tree-shakeable to ~100 KB 30+ Yes Complex visualizations, geo maps
D3 ~80 KB (full), modular imports can reduce to ~30 KB Unlimited Via @types/d3 Full control, custom visualizations
Databrain N/A (web component) 48 Yes Production embedded analytics

For more on how embedded visualization fits into your product, see our embedded visualization guide.

Deployment Checklist

Before shipping your dashboard to production:

  1. Replace demo API endpoints with production URLs
  2. Store tokens and API keys in environment variables (never commit .env)
  3. Enable code splitting for each dashboard page
  4. Set explicit heights on chart containers (prevents CLS)
  5. Test on mobile devices (responsive sidebar, touch interactions)
  6. Verify Core Web Vitals: LCP < 2.5s, INP < 200ms, CLS < 0.1
  7. Add error boundaries around chart components
  8. If using Databrain: whitelist your domain in Embed Settings

Next Steps

  1. Try Databrain free: Start building
  2. What is embedded analytics?: Complete overview
  3. Customer stories: See how BerryBox, Freightify, and SpotDraft ship dashboards
  4. Compare alternatives: Power BI Embedded alternatives
  5. npm package: @databrainhq/plugin
  6. Developer docs: docs.usedatabrain.com

Databrain is a purpose-built embedded analytics platform for SaaS companies. Start building or read the docs.

FAQs

Should I use Create React App in 2026?

No. Create React App was officially deprecated in February 2025. Use Vite for client-side apps or Next.js for server-rendered applications.

Do I need Next.js for a dashboard?

Not necessarily. If your dashboard is client-side only (no SSR, no SEO requirements), Vite + React is simpler and faster. Use Next.js if you need Server Components for initial data loading or if your dashboard is part of a larger Next.js application.

What state management should I use for dashboards?

Use TanStack Query for server state (API data) and Zustand for client state (UI state like filters, sidebar, dark mode). Avoid Redux unless you have a specific need for its middleware ecosystem.

How do I handle real-time data in a React dashboard?

Yes. Use the App Router pattern shown above: an API route for guest token generation and a 'use client' component for the embed. The @databrainhq/plugin/web import must run client-side.

Is Databrain suitable for large enterprises?

Yes. Databrain is built for enterprise use, with SOC 2 Type II and ISO 27001 certification, GDPR support, HIPAA support for self-hosted deployments, SSO/SAML, RBAC, row-level security, encryption, audit logs, self-hosted deployment options, and a 99.9% uptime SLA on cloud plans.

Make analytics your competitive advantage

Get it touch with us and see how Databrain can take your customer-facing analytics to the next level.

Interactive analytics dashboard with revenue insights, sales stats, and active deals powered by Databrain