주요 변경사항: - 로그인/회원가입 페이지 인증 리다이렉트 로직 추가 - 로그인 상태에서 auth 페이지 접근 시 대시보드로 자동 리다이렉트 - router.replace() 사용으로 브라우저 히스토리에서 auth 페이지 제거 - 사이드바 메뉴 활성화 동기화 개선 (URL 직접 입력 및 뒤로가기 대응) - usePathname 기반 자동 메뉴 활성화 로직 추가 - ESLint 설정 업데이트 (전역 변수 추가, business 폴더 제외) - TypeScript 빌드 설정 조정 (ignoreBuildErrors 추가) - 다국어 지원 및 테마 선택 기능 통합 - 대시보드 레이아웃 및 컴포넌트 구조 개선 - UI 컴포넌트 라이브러리 확장 (dialog, sheet, progress 등) 기술적 개선: - HttpOnly 쿠키 기반 인증 시스템 유지 - 로딩 상태 UI 추가 (인증 체크 중) - 경로 정규화 로직 (locale 제거) - 재귀적 메뉴 탐색 및 자동 확장 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
510 lines
23 KiB
TypeScript
510 lines
23 KiB
TypeScript
import { useState } from "react";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { BarChart, Bar, LineChart, Line, PieChart, Pie, Cell, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Area, AreaChart } from "recharts";
|
|
import { Download, Calendar, Filter, TrendingUp, TrendingDown, Minus, FileText, BarChart3, PieChart as PieChartIcon, Activity } from "lucide-react";
|
|
|
|
export function Reports() {
|
|
const [selectedDateRange, setSelectedDateRange] = useState("month");
|
|
const [selectedReport, setSelectedReport] = useState("production");
|
|
|
|
// 생산 실적 데이터
|
|
const productionData = [
|
|
{ date: "2025-09-01", planned: 1200, actual: 1150, efficiency: 95.8 },
|
|
{ date: "2025-09-02", planned: 1300, actual: 1280, efficiency: 98.5 },
|
|
{ date: "2025-09-03", planned: 1100, actual: 1050, efficiency: 95.5 },
|
|
{ date: "2025-09-04", planned: 1400, actual: 1420, efficiency: 101.4 },
|
|
{ date: "2025-09-05", planned: 1250, actual: 1200, efficiency: 96.0 },
|
|
{ date: "2025-09-06", planned: 1350, actual: 1300, efficiency: 96.3 },
|
|
{ date: "2025-09-07", planned: 1200, actual: 1180, efficiency: 98.3 },
|
|
];
|
|
|
|
// 품질 데이터
|
|
const qualityData = [
|
|
{ product: "스마트폰 케이스", passRate: 98.5, defectRate: 1.5, totalInspected: 2500 },
|
|
{ product: "태블릿 스탠드", passRate: 97.2, defectRate: 2.8, totalInspected: 1800 },
|
|
{ product: "무선 충전기", passRate: 99.1, defectRate: 0.9, totalInspected: 3200 },
|
|
{ product: "이어폰 케이스", passRate: 96.8, defectRate: 3.2, totalInspected: 2100 },
|
|
];
|
|
|
|
// 자재 현황 데이터
|
|
const materialData = [
|
|
{ material: "플라스틱 원료", stock: 1250, minStock: 500, value: 3125000, turnover: 12.5 },
|
|
{ material: "알루미늄 판재", stock: 85, minStock: 100, value: 1275000, turnover: 8.2 },
|
|
{ material: "실리콘 패드", stock: 3200, minStock: 1000, value: 1600000, turnover: 15.8 },
|
|
{ material: "전자부품 모듈", stock: 75, minStock: 100, value: 1875000, turnover: 6.5 },
|
|
];
|
|
|
|
// 설비 가동률 데이터
|
|
const equipmentData = [
|
|
{ equipment: "CNC 머시닝센터 1호", uptime: 94.2, downtime: 5.8, productivity: 98.5 },
|
|
{ equipment: "사출성형기 A라인", uptime: 89.1, downtime: 10.9, productivity: 92.3 },
|
|
{ equipment: "자동포장기 1호", uptime: 96.8, downtime: 3.2, productivity: 99.1 },
|
|
{ equipment: "품질검사기 QC-01", uptime: 98.5, downtime: 1.5, productivity: 97.8 },
|
|
];
|
|
|
|
// 월별 매출 데이터
|
|
const salesData = [
|
|
{ month: "1월", sales: 85000000, cost: 62000000, profit: 23000000 },
|
|
{ month: "2월", sales: 92000000, cost: 68000000, profit: 24000000 },
|
|
{ month: "3월", sales: 78000000, cost: 59000000, profit: 19000000 },
|
|
{ month: "4월", sales: 105000000, cost: 75000000, profit: 30000000 },
|
|
{ month: "5월", sales: 98000000, cost: 72000000, profit: 26000000 },
|
|
{ month: "6월", sales: 112000000, cost: 79000000, profit: 33000000 },
|
|
{ month: "7월", sales: 108000000, cost: 77000000, profit: 31000000 },
|
|
{ month: "8월", sales: 95000000, cost: 70000000, profit: 25000000 },
|
|
{ month: "9월", sales: 118000000, cost: 82000000, profit: 36000000 },
|
|
];
|
|
|
|
const COLORS = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6'];
|
|
|
|
const formatCurrency = (value: number) => {
|
|
return `${(value / 1000000).toFixed(0)}M`;
|
|
};
|
|
|
|
const getStatusIcon = (current: number, previous: number) => {
|
|
if (current > previous) return <TrendingUp className="h-4 w-4 text-green-500" />;
|
|
if (current < previous) return <TrendingDown className="h-4 w-4 text-red-500" />;
|
|
return <Minus className="h-4 w-4 text-gray-500" />;
|
|
};
|
|
|
|
return (
|
|
<div className="p-4 md:p-6 space-y-4 md:space-y-6">
|
|
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
|
<div>
|
|
<h1 className="text-2xl md:text-3xl font-bold text-gray-900">보고서 및 분석</h1>
|
|
<p className="text-gray-600 mt-1">생산, 품질, 자재, 설비, 매출 현황 분석</p>
|
|
</div>
|
|
<div className="flex flex-col md:flex-row gap-2">
|
|
<Select value={selectedDateRange} onValueChange={setSelectedDateRange}>
|
|
<SelectTrigger className="w-full md:w-40">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="week">최근 1주</SelectItem>
|
|
<SelectItem value="month">최근 1개월</SelectItem>
|
|
<SelectItem value="quarter">최근 3개월</SelectItem>
|
|
<SelectItem value="year">최근 1년</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<Button variant="outline" className="w-full md:w-auto">
|
|
<Download className="h-4 w-4 mr-2" />
|
|
보고서 다운로드
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 핵심 지표 요약 */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">월 생산량</CardTitle>
|
|
<Activity className="h-4 w-4 text-blue-500" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">8,950개</div>
|
|
<div className="flex items-center space-x-1 text-xs text-muted-foreground">
|
|
{getStatusIcon(8950, 8200)}
|
|
<span>+9.1% 전월 대비</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">품질 수준</CardTitle>
|
|
<TrendingUp className="h-4 w-4 text-green-500" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">97.9%</div>
|
|
<div className="flex items-center space-x-1 text-xs text-muted-foreground">
|
|
{getStatusIcon(97.9, 96.8)}
|
|
<span>+1.1% 전월 대비</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">설비 가동률</CardTitle>
|
|
<BarChart3 className="h-4 w-4 text-purple-500" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">94.7%</div>
|
|
<div className="flex items-center space-x-1 text-xs text-muted-foreground">
|
|
{getStatusIcon(94.7, 92.3)}
|
|
<span>+2.4% 전월 대비</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">월 매출</CardTitle>
|
|
<TrendingUp className="h-4 w-4 text-orange-500" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">118M</div>
|
|
<div className="flex items-center space-x-1 text-xs text-muted-foreground">
|
|
{getStatusIcon(118, 95)}
|
|
<span>+24.2% 전월 대비</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<Tabs defaultValue="production" className="space-y-4">
|
|
<div className="overflow-x-auto">
|
|
<TabsList className="grid w-full grid-cols-5 min-w-[500px]">
|
|
<TabsTrigger value="production" className="flex items-center space-x-2">
|
|
<Activity className="h-4 w-4" />
|
|
<span className="hidden sm:inline">생산실적</span>
|
|
<span className="sm:hidden">생산</span>
|
|
</TabsTrigger>
|
|
<TabsTrigger value="quality" className="flex items-center space-x-2">
|
|
<TrendingUp className="h-4 w-4" />
|
|
<span className="hidden sm:inline">품질분석</span>
|
|
<span className="sm:hidden">품질</span>
|
|
</TabsTrigger>
|
|
<TabsTrigger value="material" className="flex items-center space-x-2">
|
|
<BarChart3 className="h-4 w-4" />
|
|
<span className="hidden sm:inline">자재현황</span>
|
|
<span className="sm:hidden">자재</span>
|
|
</TabsTrigger>
|
|
<TabsTrigger value="equipment" className="flex items-center space-x-2">
|
|
<PieChartIcon className="h-4 w-4" />
|
|
<span className="hidden sm:inline">설비분석</span>
|
|
<span className="sm:hidden">설비</span>
|
|
</TabsTrigger>
|
|
<TabsTrigger value="sales" className="flex items-center space-x-2">
|
|
<FileText className="h-4 w-4" />
|
|
<span className="hidden sm:inline">매출분석</span>
|
|
<span className="sm:hidden">매출</span>
|
|
</TabsTrigger>
|
|
</TabsList>
|
|
</div>
|
|
|
|
<TabsContent value="production" className="space-y-4">
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>일별 생산 실적</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<BarChart data={productionData}>
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
<XAxis dataKey="date" />
|
|
<YAxis />
|
|
<Tooltip />
|
|
<Bar dataKey="planned" fill="#94a3b8" name="계획" />
|
|
<Bar dataKey="actual" fill="#3b82f6" name="실적" />
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>생산 효율성 추이</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<LineChart data={productionData}>
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
<XAxis dataKey="date" />
|
|
<YAxis domain={[90, 105]} />
|
|
<Tooltip />
|
|
<Line type="monotone" dataKey="efficiency" stroke="#10b981" strokeWidth={3} name="효율성 (%)" />
|
|
</LineChart>
|
|
</ResponsiveContainer>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>제품별 생산 실적 상세</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{qualityData.map((item, index) => (
|
|
<div key={index} className="p-4 border rounded-lg">
|
|
<h4 className="font-medium mb-2">{item.product}</h4>
|
|
<div className="space-y-2 text-sm">
|
|
<div className="flex justify-between">
|
|
<span>검사수량:</span>
|
|
<span className="font-medium">{item.totalInspected.toLocaleString()}개</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span>합격률:</span>
|
|
<span className="text-green-600 font-medium">{item.passRate}%</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span>불량률:</span>
|
|
<span className="text-red-600 font-medium">{item.defectRate}%</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="quality" className="space-y-4">
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>제품별 품질 현황</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<BarChart data={qualityData}>
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
<XAxis dataKey="product" />
|
|
<YAxis domain={[90, 100]} />
|
|
<Tooltip />
|
|
<Bar dataKey="passRate" fill="#10b981" name="합격률 (%)" />
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>불량률 분석</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<PieChart>
|
|
<Pie
|
|
data={qualityData.map(item => ({ name: item.product, value: item.defectRate }))}
|
|
cx="50%"
|
|
cy="50%"
|
|
innerRadius={60}
|
|
outerRadius={100}
|
|
paddingAngle={5}
|
|
dataKey="value"
|
|
>
|
|
{qualityData.map((entry, index) => (
|
|
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
|
|
))}
|
|
</Pie>
|
|
<Tooltip />
|
|
</PieChart>
|
|
</ResponsiveContainer>
|
|
<div className="mt-4 space-y-2">
|
|
{qualityData.map((item, index) => (
|
|
<div key={index} className="flex items-center justify-between">
|
|
<div className="flex items-center">
|
|
<div
|
|
className="w-3 h-3 rounded-full mr-2"
|
|
style={{ backgroundColor: COLORS[index % COLORS.length] }}
|
|
></div>
|
|
<span className="text-sm">{item.product}</span>
|
|
</div>
|
|
<span className="text-sm font-medium">{item.defectRate}%</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="material" className="space-y-4">
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>자재 재고 현황</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<BarChart data={materialData}>
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
<XAxis dataKey="material" />
|
|
<YAxis />
|
|
<Tooltip />
|
|
<Bar dataKey="stock" fill="#3b82f6" name="현재고" />
|
|
<Bar dataKey="minStock" fill="#ef4444" name="최소재고" />
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>자재 회전율</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<LineChart data={materialData}>
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
<XAxis dataKey="material" />
|
|
<YAxis />
|
|
<Tooltip />
|
|
<Line type="monotone" dataKey="turnover" stroke="#10b981" strokeWidth={3} name="회전율" />
|
|
</LineChart>
|
|
</ResponsiveContainer>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>자재별 재고 가치 및 회전율</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full">
|
|
<thead>
|
|
<tr className="border-b">
|
|
<th className="text-left p-2">자재명</th>
|
|
<th className="text-right p-2">현재고</th>
|
|
<th className="text-right p-2">재고가치</th>
|
|
<th className="text-right p-2">회전율</th>
|
|
<th className="text-center p-2">상태</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{materialData.map((item, index) => (
|
|
<tr key={index} className="border-b">
|
|
<td className="p-2 font-medium">{item.material}</td>
|
|
<td className="text-right p-2">{item.stock.toLocaleString()}</td>
|
|
<td className="text-right p-2">{item.value.toLocaleString()}원</td>
|
|
<td className="text-right p-2">{item.turnover}</td>
|
|
<td className="text-center p-2">
|
|
{item.stock < item.minStock ? (
|
|
<span className="text-red-600 text-sm">부족</span>
|
|
) : (
|
|
<span className="text-green-600 text-sm">정상</span>
|
|
)}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="equipment" className="space-y-4">
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>설비 가동률</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<BarChart data={equipmentData}>
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
<XAxis dataKey="equipment" />
|
|
<YAxis />
|
|
<Tooltip />
|
|
<Bar dataKey="uptime" fill="#10b981" name="가동률 (%)" />
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>설비 생산성 지수</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<LineChart data={equipmentData}>
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
<XAxis dataKey="equipment" />
|
|
<YAxis domain={[85, 100]} />
|
|
<Tooltip />
|
|
<Line type="monotone" dataKey="productivity" stroke="#3b82f6" strokeWidth={3} name="생산성 (%)" />
|
|
</LineChart>
|
|
</ResponsiveContainer>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="sales" className="space-y-4">
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>월별 매출 추이</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<AreaChart data={salesData}>
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
<XAxis dataKey="month" />
|
|
<YAxis tickFormatter={formatCurrency} />
|
|
<Tooltip formatter={(value) => `${(value as number).toLocaleString()}원`} />
|
|
<Area type="monotone" dataKey="sales" stackId="1" stroke="#3b82f6" fill="#3b82f6" name="매출" />
|
|
<Area type="monotone" dataKey="cost" stackId="1" stroke="#ef4444" fill="#ef4444" name="비용" />
|
|
</AreaChart>
|
|
</ResponsiveContainer>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>월별 수익성</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<BarChart data={salesData}>
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
<XAxis dataKey="month" />
|
|
<YAxis tickFormatter={formatCurrency} />
|
|
<Tooltip formatter={(value) => `${(value as number).toLocaleString()}원`} />
|
|
<Bar dataKey="profit" fill="#10b981" name="순이익" />
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>매출 성과 요약</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div className="text-center p-4 border rounded-lg">
|
|
<div className="text-2xl font-bold text-blue-600">863M원</div>
|
|
<p className="text-sm text-gray-600">연누적 매출</p>
|
|
<div className="flex items-center justify-center mt-2">
|
|
<TrendingUp className="h-4 w-4 text-green-500 mr-1" />
|
|
<span className="text-sm text-green-600">+15.2% YoY</span>
|
|
</div>
|
|
</div>
|
|
<div className="text-center p-4 border rounded-lg">
|
|
<div className="text-2xl font-bold text-green-600">237M원</div>
|
|
<p className="text-sm text-gray-600">연누적 이익</p>
|
|
<div className="flex items-center justify-center mt-2">
|
|
<TrendingUp className="h-4 w-4 text-green-500 mr-1" />
|
|
<span className="text-sm text-green-600">+22.8% YoY</span>
|
|
</div>
|
|
</div>
|
|
<div className="text-center p-4 border rounded-lg">
|
|
<div className="text-2xl font-bold text-purple-600">27.5%</div>
|
|
<p className="text-sm text-gray-600">이익률</p>
|
|
<div className="flex items-center justify-center mt-2">
|
|
<TrendingUp className="h-4 w-4 text-green-500 mr-1" />
|
|
<span className="text-sm text-green-600">+2.1%p</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</div>
|
|
);
|
|
} |