Dashboard Example (iCignal)
분석 대시보드 구현 예제
개요
iCignal 전용 AnalyticsCard 컴포넌트를 활용한 실시간 분석 대시보드 예제입니다.
주요 특징
- ✅ Real-time Analytics Cards
- ✅ Trend Indicators
- ✅ Chart Integration
- ✅ Responsive Grid Layout
- ✅ Data Refresh
전체 코드
"use client";
import { useState, useEffect } from "react";
import { AnalyticsCard } from "@vortex/ui-icignal";
import { Container } from "@/components/ui/container";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@vortex/ui-icignal";
export default function Dashboard() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchDashboardData();
}, []);
const fetchDashboardData = async () => {
setLoading(true);
// API 호출 시뮬레이션
await new Promise((resolve) => setTimeout(resolve, 1000));
setData({
users: { value: "12,345", trend: "+12%", trendUp: true },
revenue: { value: "$45,678", trend: "+8%", trendUp: true },
orders: { value: "1,234", trend: "-3%", trendUp: false },
conversion: { value: "3.2%", trend: "+0.5%", trendUp: true },
});
setLoading(false);
};
if (loading) {
return (
<Container size="xl" className="py-8">
<div className="animate-pulse space-y-6">
<div className="h-8 bg-muted rounded w-64"></div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{[...Array(4)].map((_, i) => (
<div key={i} className="h-32 bg-muted rounded"></div>
))}
</div>
</div>
</Container>
);
}
return (
<Container size="xl" className="py-8 space-y-8">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Analytics Dashboard</h1>
<p className="text-muted-foreground">
실시간 비즈니스 지표를 확인하세요
</p>
</div>
<Button onClick={fetchDashboardData}>Refresh</Button>
</div>
{/* Metrics Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<AnalyticsCard
title="Total Users"
value={data.users.value}
trend={data.users.trend}
trendUp={data.users.trendUp}
description="Active users this month"
/>
<AnalyticsCard
title="Revenue"
value={data.revenue.value}
trend={data.revenue.trend}
trendUp={data.revenue.trendUp}
description="Total revenue this month"
/>
<AnalyticsCard
title="Orders"
value={data.orders.value}
trend={data.orders.trend}
trendUp={data.orders.trendUp}
description="Total orders this month"
/>
<AnalyticsCard
title="Conversion Rate"
value={data.conversion.value}
trend={data.conversion.trend}
trendUp={data.conversion.trendUp}
description="Average conversion rate"
/>
</div>
{/* Tabs for Different Views */}
<Tabs defaultValue="overview" className="space-y-4">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="analytics">Analytics</TabsTrigger>
<TabsTrigger value="reports">Reports</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-4">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle>Recent Activity</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<p className="font-medium">New user registration</p>
<p className="text-sm text-muted-foreground">
2 minutes ago
</p>
</div>
<span className="text-sm text-green-600">+1</span>
</div>
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Order completed</p>
<p className="text-sm text-muted-foreground">
5 minutes ago
</p>
</div>
<span className="text-sm text-green-600">+$234</span>
</div>
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Payment received</p>
<p className="text-sm text-muted-foreground">
12 minutes ago
</p>
</div>
<span className="text-sm text-green-600">+$567</span>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Top Products</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Product A</p>
<p className="text-sm text-muted-foreground">234 sales</p>
</div>
<span className="text-sm font-medium">$12,345</span>
</div>
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Product B</p>
<p className="text-sm text-muted-foreground">189 sales</p>
</div>
<span className="text-sm font-medium">$9,876</span>
</div>
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Product C</p>
<p className="text-sm text-muted-foreground">145 sales</p>
</div>
<span className="text-sm font-medium">$7,654</span>
</div>
</div>
</CardContent>
</Card>
</div>
</TabsContent>
<TabsContent value="analytics">
<Card>
<CardHeader>
<CardTitle>Analytics Overview</CardTitle>
</CardHeader>
<CardContent>
<p className="text-muted-foreground">
차트 및 상세 분석 데이터가 여기에 표시됩니다.
</p>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="reports">
<Card>
<CardHeader>
<CardTitle>Reports</CardTitle>
</CardHeader>
<CardContent>
<p className="text-muted-foreground">
생성된 리포트 목록이 여기에 표시됩니다.
</p>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</Container>
);
}주요 패턴
실시간 데이터 로딩
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchDashboardData();
// 30초마다 자동 새로고침
const interval = setInterval(fetchDashboardData, 30000);
return () => clearInterval(interval);
}, []);AnalyticsCard 사용
<AnalyticsCard
title="Total Users"
value="12,345"
trend="+12%"
trendUp={true}
description="Active users this month"
/>Props:
title: 지표 이름value: 현재 값trend: 증감 비율trendUp: 상승/하락 표시description: 부가 설명
반응형 그리드
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{/* 모바일: 1열, 태블릿: 2열, 데스크톱: 4열 */}
</div>API 통합
데이터 페칭
const fetchDashboardData = async () => {
try {
const response = await fetch("/api/analytics/dashboard");
const data = await response.json();
setData(data);
} catch (error) {
console.error("Failed to fetch dashboard data:", error);
} finally {
setLoading(false);
}
};TanStack Query 사용
import { useQuery } from "@tanstack/react-query";
function Dashboard() {
const { data, isLoading, refetch } = useQuery({
queryKey: ["dashboard"],
queryFn: fetchDashboardData,
refetchInterval: 30000, // 30초마다 자동 새로고침
});
return <Button onClick={() => refetch()}>Refresh</Button>;
}로딩 상태
if (loading) {
return (
<div className="animate-pulse space-y-6">
<div className="h-8 bg-muted rounded w-64"></div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{[...Array(4)].map((_, i) => (
<div key={i} className="h-32 bg-muted rounded"></div>
))}
</div>
</div>
);
}차트 통합
Recharts 예제
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
} from "recharts";
<Card>
<CardHeader>
<CardTitle>Revenue Trend</CardTitle>
</CardHeader>
<CardContent>
<LineChart width={600} height={300} data={chartData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="revenue" stroke="#8884d8" />
</LineChart>
</CardContent>
</Card>;다음 단계
Last updated on