A comprehensive, lightweight business intelligence library built with Svelte 5. Create interactive dashboards with drag-and-drop functionality, real-time data visualization, dynamic filtering, and SQL query management.
- π― Interactive Dashboards: Drag-and-drop block positioning with real-time editing
- π Multiple Chart Types: Line, bar, pie, scatter, area, donut, gauge, and heatmap charts
- π Data Tables: Sortable, filterable tables with pagination support
- π Advanced Filtering: String, date range, numeric range, and list filters
- π Rich Text Blocks: Template-based text with variable interpolation
- ποΈ SQL Query Management: Built-in SQL editor with syntax highlighting and AI assistance
- π¨ Responsive Design: Mobile-first design with touch support
- π Real-time Updates: Automatic data refresh and filter application
- π οΈ TypeScript Support: Full type safety and IntelliSense support
- πͺ Extensible: Easy to extend with custom block types and data sources
- π Dark Mode: Built-in light/dark theme with system preference detection and toggle
npm install @jaspero/mini-bi --save-exactInstall required peer dependencies:
npm install svelte@^5.0.0 --save-exactFor styling support, install Tailwind CSS:
npm install tailwindcss @tailwindcss/forms @tailwindcss/typography --save-exact- Configure Tailwind CSS (if using):
// tailwind.config.js
export default {
content: [
'./src/**/*.{html,js,svelte,ts}',
'./node_modules/@jaspero/mini-bi/**/*.{html,js,svelte,ts}'
],
theme: {
extend: {}
},
plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')]
};- Import styles in your app:
/* app.css */
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';- Create your first dashboard:
<!-- App.svelte -->
<script>
import { DashboardComponent, MockDashboardService } from '@jaspero/mini-bi';
const dashboardService = new MockDashboardService();
let selectedDashboardId = '1'; // Use existing sample dashboard
</script>
<main class="h-screen">
<DashboardComponent {dashboardService} {selectedDashboardId} editable={true} />
</main>Implement the IDashboardService interface for your data source:
import type { IDashboardService, Dashboard, Query, QueryResult } from '@jaspero/mini-bi';
export class CustomDashboardService implements IDashboardService {
async loadDashboards(): Promise<Dashboard[]> {
const response = await fetch('/api/dashboards');
return response.json();
}
async executeQuery(queryId: string, parameters?: Record<string, any>): Promise<QueryResult> {
const response = await fetch(`/api/queries/${queryId}/execute`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ parameters })
});
return response.json();
}
async getDatabaseSchema(): Promise<DatabaseSchema> {
const response = await fetch('/api/schema');
return response.json();
}
// Implement other required methods...
}import type { Dashboard, DashboardLayout } from '@jaspero/mini-bi';
const dashboard: Dashboard = {
id: 'my-dashboard',
name: 'Sales Analytics',
description: 'Monthly sales performance dashboard',
created: new Date(),
lastModified: new Date(),
layout: {
gridSize: 80,
columns: 20,
rows: 15,
gap: 10,
canvasWidth: { type: 'screen' }, // or { type: 'fixed', value: 1600 }
canvasHeight: { type: 'fixed', value: 1000 }
},
blocks: [
{
id: 'sales-chart',
type: 'graph',
title: 'Monthly Sales',
position: { x: 0, y: 0 },
size: { width: 10, height: 6 },
dataSource: {
type: 'query',
queryId: 'sales-data'
},
config: {
chartType: 'line',
series: [
{ name: 'Sales', dataKey: 'sales', color: '#3b82f6' },
{ name: 'Target', dataKey: 'target', color: '#ef4444' }
],
xAxis: { type: 'category', name: 'Month' },
yAxis: { type: 'value', name: 'Amount ($)' },
legend: { show: true, position: 'top', align: 'right' }
}
},
{
id: 'sales-table',
type: 'table',
title: 'Sales Data',
position: { x: 10, y: 0 },
size: { width: 10, height: 6 },
dataSource: {
type: 'query',
queryId: 'sales-data'
},
config: {
columns: [
{ key: 'month', header: 'Month', type: 'string', sortable: true },
{ key: 'sales', header: 'Sales', type: 'number', sortable: true },
{ key: 'target', header: 'Target', type: 'number', sortable: true }
],
pagination: { enabled: true, pageSize: 10 },
sorting: { enabled: true }
}
}
],
filters: [
{
id: 'date-filter',
key: 'date_range',
name: 'Date Range',
type: 'date_range',
active: true,
initialValue: [new Date('2024-01-01'), new Date('2024-12-31')]
}
]
};<!-- CustomBlock.svelte -->
<script lang="ts">
import type { Block, BlockData, IDashboardService } from '@jaspero/mini-bi';
interface Props {
block: Block;
dashboardService: IDashboardService;
filterParams?: Record<string, any>;
showControls?: boolean;
}
let { block, dashboardService, filterParams = {}, showControls = false }: Props = $props();
let data: BlockData | null = $state(null);
let loading = $state(false);
async function loadData() {
loading = true;
try {
data = await dashboardService.loadBlockData(
block.id,
block.type,
block.config,
block.dataSource,
filterParams
);
} catch (error) {
console.error('Failed to load data:', error);
} finally {
loading = false;
}
}
$effect(() => {
loadData();
});
$effect(() => {
if (filterParams) {
loadData();
}
});
</script>
<div class="h-full w-full rounded-lg border border-gray-200 bg-white p-4">
{#if loading}
<div class="flex h-full items-center justify-center">
<div class="h-8 w-8 animate-spin rounded-full border-b-2 border-blue-600"></div>
</div>
{:else if data}
<!-- Your custom visualization here -->
<pre>{JSON.stringify(data, null, 2)}</pre>
{/if}
</div>import type { Filter } from '@jaspero/mini-bi';
const filters: Filter[] = [
{
id: 'region-filter',
key: 'region',
name: 'Sales Region',
type: 'list',
active: true,
initialValue: ['North America'],
options: [
{ label: 'North America', value: 'North America' },
{ label: 'Europe', value: 'Europe' },
{ label: 'Asia Pacific', value: 'Asia Pacific' }
]
},
{
id: 'date-range',
key: 'date_range',
name: 'Date Range',
type: 'date_range',
active: true,
initialValue: [new Date('2024-01-01'), new Date('2024-12-31')]
},
{
id: 'min-sales',
key: 'min_sales',
name: 'Minimum Sales',
type: 'integer',
active: false,
initialValue: 1000,
min: 0,
max: 100000
}
];<!-- Dashboard Manager -->
<script>
import { DashboardManager, MockDashboardService } from '@jaspero/mini-bi';
const service = new MockDashboardService();
</script>
<DashboardManager {service} />
<!-- Query Manager -->
<script>
import { GlobalQueryManager } from '@jaspero/mini-bi';
</script>
<GlobalQueryManager dashboardService={service} />
<!-- Individual Blocks -->
<script>
import { TableBlock, GraphBlock, TextBlock } from '@jaspero/mini-bi';
</script>
<TableBlock {block} {dashboardService} />
<GraphBlock {block} {dashboardService} />
<TextBlock {block} dashboardVariables={variables} />import type { GraphBlockConfig } from '@jaspero/mini-bi';
const chartConfig: GraphBlockConfig = {
chartType: 'line', // 'line' | 'bar' | 'pie' | 'scatter' | 'area' | 'donut' | 'gauge'
series: [
{
name: 'Revenue',
dataKey: 'revenue',
color: '#3b82f6'
}
],
xAxis: {
type: 'category', // 'category' | 'value' | 'time'
name: 'Time Period'
},
yAxis: {
type: 'value',
name: 'Amount ($)',
min: 0
},
legend: {
show: true,
position: 'top', // 'top' | 'bottom' | 'left' | 'right'
align: 'center' // 'left' | 'center' | 'right'
},
colors: ['#3b82f6', '#ef4444', '#10b981'],
animations: {
enabled: true,
duration: 1000,
easing: 'cubicInOut'
}
};import type { TableBlockConfig } from '@jaspero/mini-bi';
const tableConfig: TableBlockConfig = {
columns: [
{
key: 'name',
header: 'Product Name',
type: 'string',
sortable: true,
width: 200
},
{
key: 'price',
header: 'Price',
type: 'number',
sortable: true,
formatter: (value) => `$${value.toLocaleString()}`
}
],
pagination: {
enabled: true,
pageSize: 25,
showSizeChanger: true,
pageSizeOptions: [10, 25, 50, 100]
},
sorting: {
enabled: true,
defaultSort: {
column: 'name',
direction: 'asc'
}
},
filtering: {
enabled: true,
type: 'text'
}
};The library uses Tailwind CSS classes. You can customize the appearance by:
- Override CSS classes:
.mini-bi-dashboard {
@apply border-gray-300 bg-gray-50;
}
.mini-bi-block {
@apply rounded-xl shadow-lg;
}- Custom CSS variables:
:root {
--mini-bi-primary: #3b82f6;
--mini-bi-secondary: #64748b;
--mini-bi-success: #10b981;
--mini-bi-danger: #ef4444;
--mini-bi-warning: #f59e0b;
}The library is fully responsive with touch support:
- Touch-based drag and drop
- Mobile-optimized controls
- Responsive grid system
- Touch-friendly resize handles
Use the included MockDashboardService for testing and development:
import { MockDashboardService } from '@jaspero/mini-bi';
const mockService = new MockDashboardService();
// Test dashboard loading
const dashboards = await mockService.loadDashboards();
console.log('Available dashboards:', dashboards);
// Test query execution
const result = await mockService.executeQuery('sales-query', {
region: ['North America'],
date_range: [new Date('2024-01-01'), new Date('2024-12-31')]
});
console.log('Query result:', result);- DashboardComponent: Main dashboard viewer/editor
- DashboardManager: Dashboard list and management
- GlobalQueryManager: SQL query management
- FilterSidebar: Dynamic filter controls
- TableBlock: Data table visualization
- GraphBlock: Chart visualization
- TextBlock: Rich text with templating
- IDashboardService: Interface for data operations
- MockDashboardService: Built-in mock implementation
All TypeScript types are exported for full type safety in your application.
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
MIT License - see LICENSE file for details.
- π Documentation
- π Issues
- π¬ Discussions
Built with β€οΈ using Svelte 5 and TypeScript