Di era digital saat ini, kemampuan untuk memvisualisasikan dan menganalisis data secara real-time menjadi kebutuhan vital bagi bisnis dan organisasi. Dashboard analitik real-time memungkinkan pengambilan keputusan yang lebih cepat dan tepat berdasarkan informasi terkini. Artikel ini akan memandu Anda langkah demi langkah dalam membuat dashboard analitik data real-time yang interaktif menggunakan React.
Prasyarat
Sebelum memulai, pastikan Anda memiliki pengetahuan dasar tentang:
- JavaScript dan React.js
- HTML dan CSS (Tailwind CSS akan digunakan dalam tutorial ini)
- Konsep dasar API dan pengambilan data
- Node.js dan npm terinstal di komputer Anda
Apa yang Akan Kita Buat?
Kita akan membuat dashboard analitik data real-time dengan fitur-fitur berikut:
- Tampilan metrik utama dalam bentuk kartu statistik
- Grafik garis untuk menampilkan tren dari waktu ke waktu
- Grafik batang untuk perbandingan kategori
- Peta panas (heatmap) untuk visualisasi data kompleks
- Tabel data interaktif dengan fitur pencarian dan pengurutan
- Pembaruan data secara real-time menggunakan websocket
- Responsif di berbagai ukuran perangkat
Langkah 1: Menyiapkan Proyek React
Pertama, mari kita buat proyek React baru menggunakan Create React App, yang merupakan cara termudah untuk memulai proyek React:
npx create-react-app dashboard-analitik-realtime
cd dashboard-analitik-realtime
Setelah proyek dibuat, kita perlu menginstal beberapa dependensi untuk membuat dashboard kita:
npm install recharts socket.io-client tailwindcss @headlessui/react date-fns
Mari konfigurasi Tailwind CSS:
npx tailwindcss init
Buat file CSS utama (src/index.css) dengan konten berikut:
@tailwind base;
@tailwind components;
@tailwind utilities;
Langkah 2: Struktur Proyek
Berikut adalah struktur folder yang direkomendasikan untuk proyek dashboard kita:
src/
├── components/
│ ├── Dashboard.js
│ ├── Header.js
│ ├── Sidebar.js
│ ├── charts/
│ │ ├── AreaChart.js
│ │ ├── BarChart.js
│ │ ├── HeatMap.js
│ │ └── LineChart.js
│ ├── ui/
│ │ ├── Card.js
│ │ ├── Table.js
│ │ └── Loading.js
├── services/
│ ├── api.js
│ └── socket.js
├── hooks/
│ └── useWebSocket.js
├── utils/
│ ├── formatters.js
│ └── colors.js
├── App.js
├── index.js
└── index.css
Langkah 3: Mengatur Layanan WebSocket
Untuk data real-time, kita akan menggunakan Socket.IO. Mari buat file services/socket.js
:
import { io } from 'socket.io-client';
const SOCKET_URL = 'http://localhost:5000';
export const socket = io(SOCKET_URL, {
autoConnect: false,
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000
});
export const connectSocket = () => {
if (!socket.connected) {
socket.connect();
}
return socket;
};
export const disconnectSocket = () => {
if (socket.connected) {
socket.disconnect();
}
};
Selanjutnya, buat custom hook untuk menggunakan WebSocket (hooks/useWebSocket.js
):
import { useState, useEffect } from 'react';
import { connectSocket, disconnectSocket, socket } from '../services/socket';
export const useWebSocket = (eventName) => {
const [data, setData] = useState(null);
const [isConnected, setIsConnected] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const socketInstance = connectSocket();
const onConnect = () => {
setIsConnected(true);
setError(null);
};
const onDisconnect = () => {
setIsConnected(false);
};
const onError = (err) => {
setError(err);
};
const onData = (receivedData) => {
setData(receivedData);
};
socketInstance.on('connect', onConnect);
socketInstance.on('disconnect', onDisconnect);
socketInstance.on('error', onError);
socketInstance.on(eventName, onData);
return () => {
socketInstance.off('connect', onConnect);
socketInstance.off('disconnect', onDisconnect);
socketInstance.off('error', onError);
socketInstance.off(eventName, onData);
disconnectSocket();
};
}, [eventName]);
return { data, isConnected, error };
};
Catatan Penting
Dalam implementasi nyata, Anda perlu menyiapkan server Socket.IO terpisah yang akan mengirimkan data real-time ke klien. Untuk tujuan tutorial ini, kita akan fokus pada sisi klien dan mengasumsikan server sudah tersedia.
Langkah 4: Membuat Komponen UI Dasar
Card.js
Komponen Card akan digunakan untuk menampilkan metrik statistik:
import React from 'react';
const Card = ({ title, value, icon, trend, trendValue, color = 'blue' }) => {
const colorClasses = {
blue: 'bg-blue-50 text-blue-700 border-blue-200',
green: 'bg-green-50 text-green-700 border-green-200',
red: 'bg-red-50 text-red-700 border-red-200',
yellow: 'bg-yellow-50 text-yellow-700 border-yellow-200',
purple: 'bg-purple-50 text-purple-700 border-purple-200',
};
const trendClasses = trend === 'up'
? 'text-green-600'
: trend === 'down'
? 'text-red-600'
: 'text-gray-600';
return (
{icon && {icon}}
{title}
{value && {value}
}{trend && trendValue && ( {trend === 'up' ? (
) : trend === 'down' ? (
) : null}
{trendValue} )} );
};
export default Card;
Table.js
Komponen tabel untuk menampilkan data:
import React, { useState } from 'react';
const Table = ({ data, columns }) => {
const [sortConfig, setSortConfig] = useState({ key: null, direction: 'ascending' });
const [searchTerm, setSearchTerm] = useState('');
// Logic for sorting
const requestSort = (key) => {
let direction = 'ascending';
if (sortConfig.key === key && sortConfig.direction === 'ascending') {
direction = 'descending';
}
setSortConfig({ key, direction });
};
// Apply sorting
const sortedData = React.useMemo(() => {
if (!sortConfig.key) return data;
return [...data].sort((a, b) => {
if (a[sortConfig.key] < b[sortConfig.key]) {
return sortConfig.direction === 'ascending' ? -1 : 1;
}
if (a[sortConfig.key] > b[sortConfig.key]) {
return sortConfig.direction === 'ascending' ? 1 : -1;
}
return 0;
});
}, [data, sortConfig]);
// Apply search filtering
const filteredData = sortedData.filter(item => {
return Object.values(item).some(
value =>
value &&
value.toString().toLowerCase().includes(searchTerm.toLowerCase())
);
});
return (
{}
{}
{}
type="text"
placeholder="Cari..."
className="w-full p-2 border rounded-lg pl-10"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>{}
{}
{}
{}
{columns.map((column) => (
key={column.key}
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
onClick={() => requestSort(column.key)}
>{}
{}
{column.label}{}
{sortConfig.key === column.key && (
{}
{sortConfig.direction === 'ascending' ? '↑' : '↓'}{}
)}
{}
))}
{}
{}
{}
{filteredData.length > 0 ? (
filteredData.map((row, rowIndex) => (
{}
{columns.map((column) => (
key={`${rowIndex}-${column.key}`}
className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"
>{}
{column.render
? column.render(row[column.key], row)
: row[column.key]}
))}
))}
) : (
{}
colSpan={columns.length}
className="px-6 py-4 text-center text-sm text-gray-500"
>{}
Tidak ada data yang ditemukan
{}
)}
{}
{}
{}
{}
);
};
export default Table;
Langkah 5: Membuat Komponen Grafik
Kita akan menggunakan Recharts untuk membuat grafik interaktif. Mari mulai dengan LineChart:
LineChart.js
import React from 'react';
import {
LineChart as RechartsLineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer
} from 'recharts';
const LineChart = ({ data, title, xKey, series, height = 400 }) => {
const colors = ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6'];
return (
{}
{title && {title}
}{}
{}
data={data}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>{}
{}
{}
{}
{}
{}
{}
);
};
export default LineChart;
BarChart.js
import React from 'react';
import {
BarChart as RechartsBarChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer
} from 'recharts';
const BarChart = ({ data, title, xKey, series, height = 400, layout = 'vertical' }) => {
const colors = ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6'];
return (
{}
{title && {title}
}{}
{}
data={data}
layout={layout}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>{}
{}
type={layout === 'vertical' ? 'number' : 'category'}
dataKey={layout === 'vertical' ? null : xKey}
/>{}
dataKey={layout === 'vertical' ? xKey : null}
type={layout === 'vertical' ? 'category' : 'number'}
/>{}
{}
{}
{}
);
};
export default BarChart;
Langkah 6: Membuat Komponen Dashboard Utama
Sekarang, mari kita gabungkan semua komponen untuk membuat dashboard utama:
import React, { useState, useEffect } from 'react';
import Card from './ui/Card';
import LineChart from './charts/LineChart';
import BarChart from './charts/BarChart';
import Table from './ui/Table';
import { useWebSocket } from '../hooks/useWebSocket';
import { format, parseISO, subDays } from 'date-fns';
import { id } from 'date-fns/locale';
// Icons
const UsersIcon = () => (
{}
{}
);
const RevenueIcon = () => (
{}
{}
);
const OrdersIcon = () => (
{}
{}
);
const ConversionIcon = () => (
{}
{}
);
// Sample data until websocket delivers real data
const generateSampleData = () => {
// Generate sample time series data
const timeSeriesData = Array(30)
.fill()
.map((_, i) => {
const date = subDays(new Date(), 29 - i);
return {
date: format(date, 'yyyy-MM-dd'),
users: Math.floor(Math.random() * 1000) + 500,
revenue: Math.floor(Math.random() * 10000) + 5000,
orders: Math.floor(Math.random() * 500) + 200,
conversionRate: (Math.random() * 5 + 1).toFixed(2)
};
});
// Sample table data
const tableData = Array(20)
.fill()
.map((_, i) => ({
id: i + 1,
product: `Produk ${i + 1}`,
category: ['Elektronik', 'Pakaian', 'Makanan', 'Rumah Tangga', 'Lainnya'][Math.floor(Math.random() * 5)],
price: Math.floor(Math.random() * 1000000) + 100000,
stock: Math.floor(Math.random() * 100),
sales: Math.floor(Math.random() * 500)
}));
return {
metrics: {
users: {
value: '2,547',
trend: 'up',
trendValue: '+12.5%'
},
revenue: {
value: 'Rp 8.342.500',
trend: 'up',
trendValue: '+23.1%'
},
orders: {
value: '847',
trend: 'down',
trendValue: '-5.2%'
},
conversion: {
value: '3.6%',
trend: 'up',
trendValue: '+0.8%'
}
},
timeSeriesData,
tableData,
categoryData: [
{ name: 'Elektronik', value: 35 },
{ name: 'Pakaian', value: 25 },
{ name: 'Makanan', value: 20 },
{ name: 'Rumah Tangga', value: 15 },
{ name: 'Lainnya', value: 5 }
]
};
};
const Dashboard = () => {
const [dashboardData, setDashboardData] = useState(generateSampleData());
const { data: realTimeData, isConnected } = useWebSocket('dashboard-updates');
// Update dashboard when real-time data is received
useEffect(() => {
if (realTimeData) {
setDashboardData(prevData => ({
...prevData,
...realTimeData
}));
}
}, [realTimeData]);
const tableColumns = [
{ key: 'id', label: 'ID' },
{ key: 'product', label: 'Produk' },
{ key: 'category', label: 'Kategori' },
{
key: 'price',
label: 'Harga',
render: (value) => `Rp ${value.toLocaleString('id-ID')}`
},
{ key: 'stock', label: 'Stok' },
{
key: 'sales',
label: 'Penjualan',
render: (value, row) => (
{value}
)
}
];
return (
{/* Connection Status */}
{isConnected ? 'Terhubung ke data real-time' : 'Tidak terhubung ke data real-time'}
{/* Metrics */}
}
trend={dashboardData.metrics.users.trend}
trendValue={dashboardData.metrics.users.trendValue}
color="blue"
/>
}
trend={dashboardData.metrics.revenue.trend}
trendValue={dashboardData.metrics.revenue.trendValue}
color="green"
/>
}
trend={dashboardData.metrics.orders.trend}
trendValue={dashboardData.metrics.orders.trendValue}
color="yellow"
/>
}
trend={dashboardData.metrics.conversion.trend}
trendValue={dashboardData.metrics.conversion.trendValue}
color="purple"
/>
{/* Charts */}
{/* Table */}
Produk Terlaris
);
};
export default Dashboard;