Charts & Data Visualization
Charts & Data Visualization
UltraViolet Pro includes comprehensive charting capabilities using ApexCharts and Chart.js for creating interactive, responsive data visualizations.
Overview
The dashboard supports two powerful charting libraries:
- ApexCharts: Modern, interactive charts with advanced features
- Chart.js: Lightweight, flexible charting library
Both libraries are fully integrated and styled to match the UltraViolet theme.
ApexCharts
ApexCharts is the primary charting library used throughout UltraViolet, offering advanced features and beautiful animations.
Route: /admin/charts?page=apex
Line Charts
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Revenue Trend</h5>
</div>
<div class="card-body">
<div id="lineChart"></div>
</div>
</div>
<script>
var options = {
series: [{
name: 'Revenue',
data: [30, 40, 35, 50, 49, 60, 70, 91, 125]
}],
chart: {
type: 'line',
height: 350,
toolbar: {
show: false
}
},
colors: ['#6366f1'],
stroke: {
curve: 'smooth',
width: 3
},
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep']
},
yaxis: {
title: {
text: 'Revenue ($)'
}
},
tooltip: {
theme: 'dark'
}
};
var chart = new ApexCharts(document.querySelector("#lineChart"), options);
chart.render();
</script>
Area Charts
var options = {
series: [{
name: 'Sales',
data: [30, 40, 35, 50, 49, 60, 70, 91, 125]
}],
chart: {
type: 'area',
height: 350,
toolbar: {
show: false
}
},
colors: ['#6366f1'],
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth'
},
fill: {
type: 'gradient',
gradient: {
shadeIntensity: 1,
opacityFrom: 0.7,
opacityTo: 0.3,
stops: [0, 90, 100]
}
},
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep']
}
};
var chart = new ApexCharts(document.querySelector("#areaChart"), options);
chart.render();
Bar Charts
var options = {
series: [{
name: 'Sales',
data: [44, 55, 57, 56, 61, 58, 63, 60, 66]
}],
chart: {
type: 'bar',
height: 350,
toolbar: {
show: false
}
},
colors: ['#6366f1'],
plotOptions: {
bar: {
horizontal: false,
columnWidth: '55%',
borderRadius: 4
}
},
dataLabels: {
enabled: false
},
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep']
},
yaxis: {
title: {
text: 'Sales'
}
},
fill: {
opacity: 1
}
};
var chart = new ApexCharts(document.querySelector("#barChart"), options);
chart.render();
Pie Charts
var options = {
series: [44, 55, 13, 43, 22],
chart: {
type: 'pie',
height: 350
},
labels: ['Team A', 'Team B', 'Team C', 'Team D', 'Team E'],
colors: ['#6366f1', '#8b5cf6', '#10b981', '#f59e0b', '#ef4444'],
legend: {
position: 'bottom'
},
responsive: [{
breakpoint: 480,
options: {
chart: {
width: 300
},
legend: {
position: 'bottom'
}
}
}]
};
var chart = new ApexCharts(document.querySelector("#pieChart"), options);
chart.render();
Donut Charts
var options = {
series: [44, 55, 41, 17],
chart: {
type: 'donut',
height: 350
},
labels: ['Desktop', 'Mobile', 'Tablet', 'Other'],
colors: ['#6366f1', '#8b5cf6', '#10b981', '#f59e0b'],
legend: {
position: 'bottom'
},
plotOptions: {
pie: {
donut: {
size: '65%',
labels: {
show: true,
total: {
show: true,
label: 'Total',
formatter: function (w) {
return w.globals.seriesTotals.reduce((a, b) => a + b, 0);
}
}
}
}
}
}
};
var chart = new ApexCharts(document.querySelector("#donutChart"), options);
chart.render();
Radial Bar Charts
var options = {
series: [76, 67, 61, 90],
chart: {
type: 'radialBar',
height: 350
},
plotOptions: {
radialBar: {
dataLabels: {
name: {
fontSize: '16px'
},
value: {
fontSize: '14px'
},
total: {
show: true,
label: 'Total',
formatter: function (w) {
return '73%';
}
}
}
}
},
labels: ['Apples', 'Oranges', 'Bananas', 'Berries'],
colors: ['#6366f1', '#8b5cf6', '#10b981', '#f59e0b']
};
var chart = new ApexCharts(document.querySelector("#radialChart"), options);
chart.render();
Mixed Charts
var options = {
series: [{
name: 'Revenue',
type: 'column',
data: [440, 505, 414, 671, 227, 413, 201, 352, 752, 320, 257, 160]
}, {
name: 'Sales',
type: 'line',
data: [23, 42, 35, 27, 43, 22, 17, 31, 22, 22, 12, 16]
}],
chart: {
height: 350,
type: 'line',
toolbar: {
show: false
}
},
stroke: {
width: [0, 4]
},
colors: ['#6366f1', '#10b981'],
dataLabels: {
enabled: true,
enabledOnSeries: [1]
},
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
xaxis: {
type: 'category'
},
yaxis: [{
title: {
text: 'Revenue'
}
}, {
opposite: true,
title: {
text: 'Sales'
}
}]
};
var chart = new ApexCharts(document.querySelector("#mixedChart"), options);
chart.render();
Heatmap Charts
var options = {
series: [{
name: 'Metric1',
data: generateData(20, {
min: 0,
max: 90
})
}, {
name: 'Metric2',
data: generateData(20, {
min: 0,
max: 90
})
}],
chart: {
height: 350,
type: 'heatmap',
toolbar: {
show: false
}
},
dataLabels: {
enabled: false
},
colors: ["#6366f1"],
xaxis: {
type: 'category'
}
};
var chart = new ApexCharts(document.querySelector("#heatmapChart"), options);
chart.render();
function generateData(count, range) {
var data = [];
for (var i = 0; i < count; i++) {
data.push({
x: 'w' + (i + 1),
y: Math.floor(Math.random() * (range.max - range.min + 1)) + range.min
});
}
return data;
}
Candlestick Charts
var options = {
series: [{
data: [
{x: new Date(1538778600000), y: [6629.81, 6650.5, 6623.04, 6633.33]},
{x: new Date(1538780400000), y: [6632.01, 6643.59, 6620, 6630.11]},
{x: new Date(1538782200000), y: [6630.71, 6648.95, 6623.34, 6635.65]},
// More data points
]
}],
chart: {
type: 'candlestick',
height: 350,
toolbar: {
show: false
}
},
xaxis: {
type: 'datetime'
},
yaxis: {
tooltip: {
enabled: true
}
}
};
var chart = new ApexCharts(document.querySelector("#candlestickChart"), options);
chart.render();
Chart.js
Chart.js provides a lightweight alternative with excellent browser support.
Route: /admin/charts?page=chartjs
Line Chart
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Sales Trend</h5>
</div>
<div class="card-body">
<canvas id="lineChartJs"></canvas>
</div>
</div>
<script>
const ctx = document.getElementById('lineChartJs').getContext('2d');
const lineChart = new Chart(ctx, {
type: 'line',
data: {
labels: ['January', 'February', 'March', 'April', 'May', 'June'],
datasets: [{
label: 'Sales',
data: [12, 19, 3, 5, 2, 3],
borderColor: '#6366f1',
backgroundColor: 'rgba(99, 102, 241, 0.1)',
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top'
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
Bar Chart
const ctx = document.getElementById('barChartJs').getContext('2d');
const barChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
label: 'Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'#ef4444',
'#3b82f6',
'#eab308',
'#10b981',
'#8b5cf6',
'#f97316'
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
Pie Chart
const ctx = document.getElementById('pieChartJs').getContext('2d');
const pieChart = new Chart(ctx, {
type: 'pie',
data: {
labels: ['Red', 'Blue', 'Yellow'],
datasets: [{
data: [300, 50, 100],
backgroundColor: [
'#ef4444',
'#3b82f6',
'#eab308'
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
Doughnut Chart
const ctx = document.getElementById('doughnutChartJs').getContext('2d');
const doughnutChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Direct', 'Organic', 'Referral', 'Social'],
datasets: [{
data: [45, 25, 20, 10],
backgroundColor: [
'#6366f1',
'#10b981',
'#f59e0b',
'#8b5cf6'
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom'
}
},
cutout: '70%'
}
});
Real-time Charts with Livewire
Create live-updating charts with Laravel Livewire:
// Livewire Component
namespace App\Livewire;
use Livewire\Component;
class LiveChart extends Component
{
public $data = [];
public $labels = [];
public function mount()
{
$this->refreshData();
}
public function refreshData()
{
$this->labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
$this->data = Order::selectRaw('DATE(created_at) as date, COUNT(*) as count')
->whereBetween('created_at', [now()->subDays(7), now()])
->groupBy('date')
->pluck('count')
->toArray();
$this->dispatch('chart-updated', [
'labels' => $this->labels,
'data' => $this->data
]);
}
public function render()
{
return view('livewire.live-chart');
}
}
<!-- livewire/live-chart.blade.php -->
<div wire:poll.10s="refreshData">
<canvas id="liveChart"></canvas>
</div>
<script>
let liveChart = null;
document.addEventListener('DOMContentLoaded', function() {
const ctx = document.getElementById('liveChart').getContext('2d');
liveChart = new Chart(ctx, {
type: 'line',
data: {
labels: @json($labels),
datasets: [{
label: 'Orders',
data: @json($data),
borderColor: '#6366f1',
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
});
Livewire.on('chart-updated', (event) => {
if (liveChart) {
liveChart.data.labels = event.labels;
liveChart.data.datasets[0].data = event.data;
liveChart.update();
}
});
</script>
Chart Customization
Theme Integration
Both charting libraries are configured to match the UltraViolet theme:
// ApexCharts Theme
const apexTheme = {
theme: {
mode: 'dark',
palette: 'palette1'
},
colors: ['#6366f1', '#8b5cf6', '#10b981', '#f59e0b', '#ef4444'],
chart: {
background: 'transparent',
foreColor: '#9ca3af'
},
grid: {
borderColor: '#374151'
},
tooltip: {
theme: 'dark'
}
};
// Chart.js Theme
Chart.defaults.color = '#9ca3af';
Chart.defaults.borderColor = '#374151';
Responsive Charts
// Responsive configuration
const responsiveOptions = {
responsive: true,
maintainAspectRatio: false,
aspectRatio: 2
};
Export Charts
// Export chart as image
function exportChart(chartElement) {
const canvas = chartElement.querySelector('canvas');
const image = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.download = 'chart.png';
link.href = image;
link.click();
}
Performance Optimization
Lazy Loading
// Load charts only when visible
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const chartId = entry.target.id;
initChart(chartId);
observer.unobserve(entry.target);
}
});
});
document.querySelectorAll('.chart-container').forEach(el => {
observer.observe(el);
});
Data Decimation
// Reduce data points for better performance
const options = {
plugins: {
decimation: {
enabled: true,
algorithm: 'lttb',
samples: 50
}
}
};
Best Practices
- Choose the Right Chart: Select chart types that best represent your data
- Performance: Limit data points for large datasets
- Responsiveness: Ensure charts work on all screen sizes
- Accessibility: Include proper labels and ARIA attributes
- Color Contrast: Use colors with sufficient contrast
- Tooltips: Provide helpful tooltips for data context
- Loading States: Show loaders while data is fetching
Production Usage
Charts work in both the static HTML and Laravel Livewire editions. For real-time charts in the static HTML version, you can provide static snapshots or mock data.
// Chart initialization
if (typeof ApexCharts !== 'undefined') {
// Initialize charts with ApexCharts
initializeChart();
} else {
console.warn('ApexCharts library not loaded');
}