@kodeme-io/next-core-analytics
Version:
Analytics, charts, dashboards, and reporting for Next.js applications
925 lines (766 loc) โข 22.4 kB
Markdown
Analytics, charts, dashboards, and reporting components for Next.js applications with TypeScript support and real-time data visualization.
- **๐ KPI Cards** - Display key metrics with trends and comparisons
- **๐ Rich Charts** - Line, bar, pie, donut, area, scatter, radar charts using Recharts
- **๐ฏ Dashboard Builder** - Create custom, responsive dashboards
- **๐ Export Functionality** - PDF, Excel, CSV, JSON export with real library implementations
- **๐ Data Aggregation** - Sum, avg, min, max, count with grouping
- **โฐ Time Series Analysis** - Time-based data processing and visualization
- **๐ Real-time Updates** - Live data streaming and WebSocket support
- **โฟ Accessible** - WCAG compliant with keyboard navigation
- **๐ง TypeScript Native** - Full type safety and IntelliSense support
- **๐ Internationalization** - Built-in support for multiple locales and currencies
- **โ
Data Validation** - Zod-powered validation for all data inputs
- **๐ก๏ธ Error Boundaries** - Graceful error handling and recovery
- **โก Performance Optimized** - React.memo and useMemo optimizations
```bash
npm install @kodeme-io/next-core-analytics
yarn add @kodeme-io/next-core-analytics
pnpm add @kodeme-io/next-core-analytics
```
```bash
npm install react react-dom recharts date-fns
```
The package includes the following dependencies:
- `recharts` - Chart rendering
- `date-fns` - Date manipulation
- `jspdf` - PDF export
- `xlsx` - Excel export
- `zod` - Data validation
```tsx
'use client'
import {
KPICard,
ChartContainer,
Dashboard,
useAnalytics,
type KPIMetric,
type ChartConfig,
type DashboardLayout
} from '@kodeme-io/next-core-analytics'
const salesMetrics: KPIMetric[] = [
{
id: 'revenue',
label: 'Total Revenue',
value: 2500000,
previousValue: 2100000,
format: 'currency',
trend: 'up',
trendValue: 19.0,
icon: <div>๐ฐ</div>,
color: 'text-green-600'
},
{
id: 'orders',
label: 'Total Orders',
value: 1250,
previousValue: 1100,
format: 'number',
trend: 'up',
trendValue: 13.6,
icon: <div>๐ฆ</div>,
color: 'text-blue-600'
},
{
id: 'conversion',
label: 'Conversion Rate',
value: 3.2,
previousValue: 2.8,
format: 'percentage',
trend: 'up',
trendValue: 14.3,
icon: <div>๐</div>,
color: 'text-purple-600'
}
]
const salesChartConfig: ChartConfig = {
type: 'line',
data: [
{ name: 'Jan', value: 1800000 },
{ name: 'Feb', value: 2100000 },
{ name: 'Mar', value: 2500000 },
{ name: 'Apr', value: 2300000 },
{ name: 'May', value: 2800000 },
{ name: 'Jun', value: 2500000 }
],
xKey: 'name',
yKey: 'value',
colors: ['#3b82f6'],
title: 'Monthly Revenue Trend',
subtitle: 'Revenue performance over the last 6 months'
}
const salesDashboard: DashboardLayout = {
id: 'sales-dashboard',
name: 'Sales Dashboard',
widgets: [
{
id: 'kpi-row',
type: 'kpi',
title: 'Key Metrics',
config: { metrics: salesMetrics },
span: { cols: 12, rows: 2 }
},
{
id: 'revenue-chart',
type: 'chart',
title: 'Revenue Trend',
config: salesChartConfig,
span: { cols: 8, rows: 4 }
}
],
refreshInterval: 300
}
export default function SalesDashboard() {
const { data, loading, error, refresh } = useAnalytics({
endpoint: '/api/analytics/sales',
params: { period: '6m' },
refreshInterval: 30000 // 30 seconds
})
if (loading) return <div>Loading dashboard...</div>
if (error) return <div>Error loading dashboard</div>
return (
<div className="p-6">
<div className="flex justify-between items-center mb-6">
<h1 className="text-3xl font-bold">Sales Analytics</h1>
<button
onClick={refresh}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Refresh
</button>
</div>
<Dashboard layout={salesDashboard} data={data} />
</div>
)
}
```
```typescript
interface KPIMetric {
id: string // Unique metric identifier
label: string // Display label
value: number | string // Current value
previousValue?: number // Previous period value
format?: 'number' | 'currency' | 'percentage' | 'duration' // Value formatting
trend?: 'up' | 'down' | 'neutral' // Trend direction
trendValue?: number // Trend percentage
icon?: React.ReactNode // Custom icon
color?: string // Custom color class
prefix?: string // Value prefix
suffix?: string // Value suffix
// Internationalization options
locale?: string // Locale for number formatting (e.g., 'en-US', 'de-DE')
currency?: string // Currency code (e.g., 'USD', 'EUR', 'IDR')
numberFormatOptions?: Intl.NumberFormatOptions // Custom formatting options
}
```
```typescript
interface ChartConfig {
type: ChartType // Chart type (line, bar, area, pie, donut, scatter, radar)
data: ChartDataPoint[] // Chart data
xKey?: string // X-axis data key
yKey?: string | string[] // Y-axis data key(s)
colors?: string[] // Custom colors
legend?: boolean // Show legend
grid?: boolean // Show grid
tooltip?: boolean // Show tooltip
title?: string // Chart title
subtitle?: string // Chart subtitle
}
```
```typescript
interface DashboardLayout {
id: string // Dashboard identifier
name: string // Dashboard name
widgets: DashboardWidget[] // Dashboard widgets
refreshInterval?: number // Auto-refresh interval (seconds)
filters?: DashboardFilter[] // Dashboard filters
}
interface DashboardWidget {
id: string // Widget identifier
type: 'chart' | 'kpi' | 'table' | 'custom' // Widget type
title: string // Widget title
description?: string // Widget description
config: ChartConfig | KPICardConfig | any // Widget configuration
span?: { // Grid span
cols?: number // Column span (1-12)
rows?: number // Row span (1-6)
}
}
```
Display key performance metrics with optional trends and comparisons:
```tsx
<KPICard
metric={{
id: 'revenue',
label: 'Total Revenue',
value: 1500000,
previousValue: 1200000,
format: 'currency',
trend: 'up',
trendValue: 25,
icon: <RevenueIcon />,
color: 'text-green-600'
}}
variant="default" // 'default' | 'compact' | 'detailed'
className="custom-kpi-card"
/>
```
**Variants:**
- `default` - Standard card with trend and value
- `compact` - Minimal design for dense layouts
- `detailed` - Extended view with previous values
**Formats:**
- `number` - Formatted with thousand separators
- `currency` - IDR currency format (default)
- `percentage` - Percentage with decimal precision
- `duration` - Hours and minutes format
### ChartContainer
Responsive chart wrapper with loading and error states:
```tsx
<ChartContainer
config={{
type: 'line',
data: salesData,
xKey: 'month',
yKey: 'revenue',
colors: ['#3b82f6'],
title: 'Monthly Revenue',
grid: true,
tooltip: true
}}
loading={false}
error={null}
className="chart-container"
height={400}
/>
```
**Chart Types:**
- `line` - Line charts for trends
- `bar` - Bar charts for comparisons
- `area` - Area charts for cumulative data
- `pie` - Pie charts for proportions
- `donut` - Donut charts with center space
- `scatter` - Scatter plots for correlations
- `radar` - Radar charts for multi-dimensional data
Full dashboard layout with grid system and widgets:
```tsx
<Dashboard
layout={dashboardLayout}
data={analyticsData}
onWidgetClick={(widgetId) => console.log('Widget clicked:', widgetId)}
onFilterChange={(filters) => console.log('Filters changed:', filters)}
className="analytics-dashboard"
loading={false}
error={null}
/>
```
Export dashboard or chart data in multiple formats:
```tsx
<ExportButton
config={{
format: 'pdf',
filename: 'sales-report',
data: chartData,
title: 'Sales Report',
includeCharts: true
}}
onExportComplete={(filename) => console.log('Exported:', filename)}
className="export-button"
/>
```
**Export Formats:**
- `pdf` - PDF reports with charts and tables
- `excel` - Excel spreadsheets with multiple sheets
- `csv` - CSV files for data analysis
- `json` - JSON data for integration
```tsx
import { aggregateData } from '@kodeme-io/next-core-analytics'
// Aggregate sales data by region
const aggregatedSales = aggregateData(salesData, {
field: 'revenue',
type: 'sum',
groupBy: 'region'
})
// Calculate average order value
const avgOrderValue = aggregateData(orderData, {
field: 'orderValue',
type: 'avg'
})
```
**Aggregation Types:**
- `sum` - Sum of values
- `avg` - Average of values
- `min` - Minimum value
- `max` - Maximum value
- `count` - Count of records
```tsx
import { processTimeSeries } from '@kodeme-io/next-core-analytics'
// Process daily sales data into weekly aggregates
const weeklyData = processTimeSeries(dailySales, {
dateField: 'date',
valueField: 'revenue',
granularity: 'week',
fillGaps: true
})
```
**Time Granularities:**
- `hour` - Hourly aggregation
- `day` - Daily aggregation
- `week` - Weekly aggregation
- `month` - Monthly aggregation
- `quarter` - Quarterly aggregation
- `year` - Yearly aggregation
```tsx
import { calculateComparison } from '@kodeme-io/next-core-analytics'
// Calculate period-over-period growth
const comparison = calculateComparison({
currentPeriod: { start: new Date('2024-01-01'), end: new Date('2024-01-31') },
previousPeriod: { start: new Date('2023-12-01'), end: new Date('2023-12-31') },
metric: 'revenue',
data: salesData
})
console.log(`Revenue ${comparison.trend === 'up' ? 'increased' : 'decreased'} by ${comparison.percentChange}%`)
```
```tsx
function RealTimeDashboard() {
const { data, subscribe, unsubscribe } = useAnalytics({
endpoint: '/api/analytics/realtime',
realtime: true
})
useEffect(() => {
const handleRealtimeUpdate = (newData) => {
console.log('Real-time data update:', newData)
}
subscribe('data-updated', handleRealtimeUpdate)
return () => {
unsubscribe('data-updated')
}
}, [subscribe, unsubscribe])
return (
<Dashboard layout={realtimeLayout} data={data} />
)
}
```
```tsx
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'
function CustomSalesChart({ data }: { data: any[] }) {
return (
<ResponsiveContainer width="100%" height={400}>
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Line
type="monotone"
dataKey="revenue"
stroke="#3b82f6"
strokeWidth={2}
dot={{ fill: '#3b82f6', strokeWidth: 2, r: 4 }}
activeDot={{ r: 6 }}
/>
</LineChart>
</ResponsiveContainer>
)
}
```
```tsx
const multiSeriesConfig: ChartConfig = {
type: 'line',
data: [
{ month: 'Jan', revenue: 1000000, profit: 200000, expenses: 800000 },
{ month: 'Feb', revenue: 1200000, profit: 250000, expenses: 950000 },
{ month: 'Mar', revenue: 1100000, profit: 180000, expenses: 920000 }
],
xKey: 'month',
yKey: ['revenue', 'profit', 'expenses'],
colors: ['#3b82f6', '#10b981', '#ef4444'],
title: 'Financial Overview',
legend: true
}
```
```tsx
const dashboardWithFilters: DashboardLayout = {
id: 'filtered-dashboard',
name: 'Filtered Dashboard',
widgets: [/* ... */],
filters: [
{
id: 'date-range',
type: 'date',
label: 'Date Range',
value: { start: '2024-01-01', end: '2024-12-31' }
},
{
id: 'region',
type: 'multiselect',
label: 'Regions',
value: ['all'],
options: [
{ label: 'All Regions', value: 'all' },
{ label: 'North America', value: 'na' },
{ label: 'Europe', value: 'eu' },
{ label: 'Asia Pacific', value: 'apac' }
]
},
{
id: 'product-category',
type: 'select',
label: 'Product Category',
value: 'all',
options: [
{ label: 'All Categories', value: 'all' },
{ label: 'Electronics', value: 'electronics' },
{ label: 'Clothing', value: 'clothing' },
{ label: 'Food', value: 'food' }
]
}
]
}
```
```tsx
const customTheme = {
primary: '#6366f1',
secondary: '#8b5cf6',
success: '#10b981',
warning: '#f59e0b',
error: '#ef4444',
neutral: '#6b7280'
}
const themedChartConfig: ChartConfig = {
type: 'bar',
data: salesData,
xKey: 'month',
yKey: 'revenue',
colors: [customTheme.primary, customTheme.secondary],
title: 'Sales by Month'
}
```
```tsx
// Responsive grid layout
const responsiveDashboard: DashboardLayout = {
id: 'responsive-dashboard',
name: 'Responsive Dashboard',
widgets: [
{
id: 'kpi-cards',
type: 'kpi',
title: 'Metrics',
config: { metrics: kpiMetrics },
span: { cols: 12, rows: 2 } // Full width on desktop
},
{
id: 'main-chart',
type: 'chart',
title: 'Trends',
config: chartConfig,
span: { cols: 8, rows: 4 } // 8/12 columns
},
{
id: 'secondary-chart',
type: 'chart',
title: 'Breakdown',
config: pieChartConfig,
span: { cols: 4, rows: 4 } // 4/12 columns
}
]
}
```
The package includes comprehensive test coverage:
```bash
npm test
npm run test:coverage
npm run test:watch
```
```tsx
import { render, screen } from '@testing-library/react'
import { KPICard } from '@kodeme-io/next-core-analytics'
test('KPI card renders correctly', () => {
const metric = {
id: 'test-metric',
label: 'Test Metric',
value: 1000,
format: 'number' as const,
trend: 'up' as const,
trendValue: 10
}
render(<KPICard metric={metric} />)
expect(screen.getByText('Test Metric')).toBeInTheDocument()
expect(screen.getByText('1.000')).toBeInTheDocument()
expect(screen.getByText('10%')).toBeInTheDocument()
})
```
The analytics package now supports internationalization out of the box:
```tsx
import { KPICard, currencyFormatters } from '@kodeme-io/next-core-analytics'
// Indonesian Rupiah
<KPICard
metric={{
id: 'revenue',
label: 'Total Revenue',
value: 1500000,
format: 'currency',
locale: 'id-ID',
currency: 'IDR'
}}
/>
// Euro with German locale
<KPICard
metric={{
id: 'revenue-eur',
label: 'Revenue (EUR)',
value: 1500,
format: 'currency',
locale: 'de-DE',
currency: 'EUR'
}}
/>
// Or use pre-configured formatters
const formattedValue = currencyFormatters.EUR(1500)
```
```tsx
import { createFormatter, formatValue } from '@kodeme-io/next-core-analytics'
const customFormatter = createFormatter({
format: 'currency',
currency: 'GBP',
locale: 'en-GB'
})
```
All components include automatic data validation using Zod schemas:
```tsx
import { KPICard, validateKPI, KPIMetricSchema } from '@kodeme-io/next-core-analytics'
// Manual validation
const isValidKPI = validateKPI(kpiData)
// Safe validation with error handling
import { safeValidate } from '@kodeme-io/next-core-analytics'
const result = safeValidate(KPIMetricSchema, metricData)
if (!result.success) {
console.error('Validation errors:', result.errors)
}
```
```tsx
import { validationMonitor } from '@kodeme-io/next-core-analytics'
// Get validation statistics
const stats = validationMonitor.getStats()
console.log('Average validation time:', stats.averageValidationTime)
console.log('Success rate:', stats.successRate)
```
```tsx
import {
AnalyticsErrorBoundary,
KPIErrorBoundary,
ChartErrorBoundary,
DashboardErrorBoundary
} from '@kodeme-io/next-core-analytics'
// Wrap your entire dashboard
<DashboardErrorBoundary>
<Dashboard layout={dashboardLayout} />
</DashboardErrorBoundary>
// Or specific components
<KPIErrorBoundary>
<KPICard metric={metric} />
</KPIErrorBoundary>
<ChartErrorBoundary>
<ChartContainer config={chartConfig} />
</ChartErrorBoundary>
```
```tsx
import { useErrorHandler } from '@kodeme-io/next-core-analytics'
function MyComponent() {
const { error, captureError, resetError } = useErrorHandler()
if (error) {
return (
<div>
<p>Error: {error.message}</p>
<button onClick={resetError}>Retry</button>
</div>
)
}
// Your component logic
}
```
```typescript
// Main analytics hook (enhanced with real API support)
export function useAnalytics(options?: {
endpoint?: string
params?: Record<string, any>
refreshInterval?: number
realtime?: boolean
method?: 'GET' | 'POST'
headers?: Record<string, string>
cache?: RequestCache
}): {
data: any
loading: boolean
error: Error | null
refresh: () => void
subscribe: (event: string, callback: Function) => void
unsubscribe: (event: string) => void
}
// Error handling hook
export function useErrorHandler(): {
error: Error | null
captureError: (error: Error) => void
resetError: () => void
}
```
```typescript
// Data aggregation
export function aggregateData(data: any[], config: AggregationConfig): any[]
// Time series processing
export function processTimeSeries(data: any[], config: TimeSeriesConfig): any[]
// Comparison analysis
export function calculateComparison(config: ComparisonConfig): ComparisonData
// Formatting utilities
export function formatValue(value: number | string, options: FormatValueOptions): string
export function formatTrendValue(value: number, format?: 'percentage' | 'number'): string
export function formatDuration(seconds: number): string
// Validation utilities
export function validateKPI(metric: unknown): KPIMetric
export function validateChartData(data: unknown[]): ChartDataPoint[]
export function validateDashboardLayout(layout: unknown): DashboardLayout
export function safeValidate<T>(schema: z.ZodSchema<T>, data: unknown): ValidationResult<T>
```
### Component Props
```typescript
// KPICard props
export interface KPICardProps {
metric: KPIMetric
variant?: 'default' | 'compact' | 'detailed'
className?: string
}
// ChartContainer props
export interface ChartContainerProps {
config: ChartConfig
loading?: boolean
error?: string | null
className?: string
height?: number
}
// Dashboard props
export interface DashboardProps {
layout: DashboardLayout
data?: any
onWidgetClick?: (widgetId: string) => void
onFilterChange?: (filters: Record<string, any>) => void
className?: string
loading?: boolean
error?: string | null
}
```
1. **Charts Not Rendering**
- Check if data is properly formatted
- Verify xKey and yKey match data structure
- Ensure recharts is installed as a dependency
2. **KPI Values Not Formatting**
- Verify format type matches value type
- Check if value is number for numeric formats
- Ensure locale settings are correct
3. **Export Not Working**
- Check if export libraries are installed
- Verify data structure matches export requirements
- Ensure proper file permissions
Enable debug logging:
```tsx
const analytics = useAnalytics({
endpoint: '/api/analytics',
debug: true
})
```
1. **Data Caching** - Cache API responses to reduce network requests
2. **Virtualization** - Use virtual scrolling for large datasets
3. **Lazy Loading** - Load charts and data on demand
4. **Debounced Updates** - Debounce real-time data updates
5. **Component Memoization** - Components are optimized with React.memo
6. **Value Memoization** - Expensive calculations are memoized
```tsx
// Caching example
const cachedAnalytics = useMemo(() => {
return processAnalyticsData(rawData)
}, [rawData])
// Debounced refresh
const debouncedRefresh = useMemo(
() => debounce(refresh, 1000),
[]
)
// Components are already optimized - no manual memoization needed
<KPICard metric={metric} variant="compact" />
<ChartContainer config={chartConfig} />
```
The package includes built-in performance monitoring:
```tsx
import { validationMonitor } from '@kodeme-io/next-core-analytics'
// Get real-time performance metrics
const stats = validationMonitor.getStats()
console.log({
totalValidations: stats.totalValidations,
averageTime: stats.averageValidationTime,
successRate: stats.successRate
})
```
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests for new functionality
5. Submit a pull request
MIT License - see LICENSE file for details
- [@kodeme-io/next-core-forms](../forms/README.md) - Form components and validation
- [@kodeme-io/next-core-workflow](../workflow/README.md) - Workflow management
- [@kodeme-io/next-core-ui](../ui/README.md) - UI components
- Documentation: [Full documentation](https://docs.abc-food.com/next-core/analytics)
- Issues: [GitHub Issues](https://github.com/abc-food/next-core/issues)
- Discord: [Community Discord](https://discord.gg/abc-food)