From 2acf5f8966a40c1c9a97680c8dc263ee3f1ad3d1 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Wed, 2 Jul 2025 00:45:49 +0000 Subject: (대표님/최겸) 20250702 변경사항 업데이트 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/dashboard/dashboard-overview-chart.tsx | 325 +++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 lib/dashboard/dashboard-overview-chart.tsx (limited to 'lib/dashboard/dashboard-overview-chart.tsx') diff --git a/lib/dashboard/dashboard-overview-chart.tsx b/lib/dashboard/dashboard-overview-chart.tsx new file mode 100644 index 00000000..ca5c0006 --- /dev/null +++ b/lib/dashboard/dashboard-overview-chart.tsx @@ -0,0 +1,325 @@ +"use client"; + +import { TrendingUp, BarChart3, PieChart } from "lucide-react"; +import { Bar, BarChart, CartesianGrid, XAxis, YAxis, Pie, PieChart as RechartsPieChart, Cell, ResponsiveContainer, LabelList } from "recharts"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart"; +import { DashboardStats } from "@/lib/dashboard/service"; + +interface DashboardOverviewChartProps { + data: DashboardStats[]; + title: string; + description?: string; +} + +// 차트 설정 +const chartConfig = { + pending: { + label: "대기", + color: "hsl(var(--chart-1))", // 회색 계열 + }, + inProgress: { + label: "진행중", + color: "hsl(var(--chart-2))", // 파란색 계열 + }, + completed: { + label: "완료", + color: "hsl(var(--chart-3))", // 초록색 계열 + }, +} satisfies ChartConfig; + +// 파이 차트용 색상 +const PIE_COLORS = { + pending: "#6b7280", + inProgress: "#3b82f6", + completed: "#10b981" +}; + +export function DashboardOverviewChart({ data, title, description }: DashboardOverviewChartProps) { + // 바 차트용 데이터 변환 + const barChartData = data.map(item => ({ + name: item.displayName.length > 10 ? + item.displayName.substring(0, 10) + "..." : + item.displayName, + fullName: item.displayName, + pending: item.pending, + inProgress: item.inProgress, + completed: item.completed, + total: item.total + })); + + // 파이 차트용 데이터 (전체 요약) + const totalPending = data.reduce((sum, item) => sum + item.pending, 0); + const totalInProgress = data.reduce((sum, item) => sum + item.inProgress, 0); + const totalCompleted = data.reduce((sum, item) => sum + item.completed, 0); + const totalTasks = totalPending + totalInProgress + totalCompleted; + + const pieChartData = [ + { name: "대기", value: totalPending, color: PIE_COLORS.pending }, + { name: "진행중", value: totalInProgress, color: PIE_COLORS.inProgress }, + { name: "완료", value: totalCompleted, color: PIE_COLORS.completed } + ].filter(item => item.value > 0); + + // 완료율 계산 + const completionRate = totalTasks > 0 ? Math.round((totalCompleted / totalTasks) * 100) : 0; + const isImproving = completionRate > 50; // 50% 이상이면 개선으로 간주 + + return ( +
+ {/* 바 차트 - 2/3 너비 */} + + + + + {title} - 업무별 현황 + + {description && {description}} + + + + + + + + { + const item = barChartData.find(d => d.name === label); + return item?.fullName || label; + }} + />} + /> + + + + value > 0 ? value : ''} + /> + + + + + +
+ {totalTasks > 0 && ( + <> + + 완료율 {completionRate}% - {isImproving ? '순조롭게 진행중' : '진행 필요'} + + )} +
+
+ 총 {totalTasks}건의 업무 현황 +
+
+
+ + {/* 파이 차트 - 1/3 너비 */} + + + + + 업무 분포 + + 상태별 비율 + + + + + [ + `${value}건 (${Math.round((Number(value) / totalTasks) * 100)}%)`, + name + ]} + />} + /> + + percent > 0.1 ? `${(percent * 100).toFixed(0)}%` : '' + } + outerRadius={70} // 작은 공간에 맞게 더 작게 조정 + fill="#8884d8" + dataKey="value" + > + {pieChartData.map((entry, index) => ( + + ))} + + + + + +
+
+
+ 대기 + {totalPending} +
+
+ 진행 + {totalInProgress} +
+
+ 완료 + {totalCompleted} +
+
+
+
+
+
+ ); +} + +// 더 컴팩트한 버전 (높이를 더 많이 줄이고 싶은 경우) +export function CompactDashboardChart({ data, title, description }: DashboardOverviewChartProps) { + const totalPending = data.reduce((sum, item) => sum + item.pending, 0); + const totalInProgress = data.reduce((sum, item) => sum + item.inProgress, 0); + const totalCompleted = data.reduce((sum, item) => sum + item.completed, 0); + const totalTasks = totalPending + totalInProgress + totalCompleted; + + const pieChartData = [ + { name: "대기", value: totalPending, color: PIE_COLORS.pending }, + { name: "진행중", value: totalInProgress, color: PIE_COLORS.inProgress }, + { name: "완료", value: totalCompleted, color: PIE_COLORS.completed } + ].filter(item => item.value > 0); + + const completionRate = totalTasks > 0 ? Math.round((totalCompleted / totalTasks) * 100) : 0; + + return ( +
+ {/* 요약 통계 */} + +
+

완료율

+
{completionRate}%
+

총 {totalTasks}건

+
+ 50 ? 'text-green-600' : 'text-orange-600'}`} /> +
+ + {/* 컴팩트 파이 차트 */} + + + {title} + + +
+ {/* 작은 파이 차트 */} +
+ + + [`${value}건`, name]} + />} + /> + + {pieChartData.map((entry, index) => ( + + ))} + + + +
+ + {/* 통계 목록 */} +
+
+
+ 대기: {totalPending}건 +
+
+
+ 진행중: {totalInProgress}건 +
+
+
+ 완료: {totalCompleted}건 +
+
+
+
+
+
+ ); +} + -- cgit v1.2.3