Files
sam-design/src/components/AccountingManagement.tsx
정재웅 060b9ce2ef 프로젝트 초기 설정 및 구조 추가
- Vite + React 프로젝트 구조 설정
- 불필요한 PDF 파일 삭제

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 13:01:43 +09:00

446 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
import { Button } from "./ui/button";
import { Badge } from "./ui/badge";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs";
import {
DollarSign,
TrendingUp,
TrendingDown,
CreditCard,
Banknote,
PieChart,
Calculator,
FileText,
AlertCircle,
CheckCircle,
ArrowUpRight,
ArrowDownRight,
Building2,
Calendar
} from "lucide-react";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table";
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line, PieChart as RechartsPieChart, Cell, Pie } from "recharts";
export function AccountingManagement() {
const [selectedPeriod, setSelectedPeriod] = useState("month");
// 매출/매입 데이터
const salesPurchaseData = [
{ month: "1월", sales: 450, purchase: 280, profit: 170 },
{ month: "2월", sales: 520, purchase: 310, profit: 210 },
{ month: "3월", sales: 480, purchase: 295, profit: 185 },
{ month: "4월", sales: 610, purchase: 350, profit: 260 },
{ month: "5월", sales: 580, purchase: 340, profit: 240 },
{ month: "6월", sales: 650, purchase: 380, profit: 270 }
];
// 거래처별 미수금
const receivables = [
{ company: "삼성전자", amount: 45000000, days: 45, status: "위험" },
{ company: "LG전자", amount: 32000000, days: 28, status: "주의" },
{ company: "현대자동차", amount: 28000000, days: 15, status: "정상" },
{ company: "SK하이닉스", amount: 25000000, days: 52, status: "위험" },
{ company: "네이버", amount: 18000000, days: 22, status: "정상" }
];
// 건별 원가 분석
const costAnalysis = [
{
orderNo: "ORD-2024-001",
product: "방화셔터 3000×3000",
salesAmount: 15000000,
materialCost: 6500000,
laborCost: 3500000,
overheadCost: 2000000,
totalCost: 12000000,
profit: 3000000,
profitRate: 20.0
},
{
orderNo: "ORD-2024-002",
product: "일반셔터 2500×2500",
salesAmount: 8500000,
materialCost: 3200000,
laborCost: 2100000,
overheadCost: 1200000,
totalCost: 6500000,
profit: 2000000,
profitRate: 23.5
},
{
orderNo: "ORD-2024-003",
product: "특수셔터 4000×3500",
salesAmount: 22000000,
materialCost: 9500000,
laborCost: 5200000,
overheadCost: 2800000,
totalCost: 17500000,
profit: 4500000,
profitRate: 20.5
}
];
// 원가 구성 비율
const costComposition = [
{ name: "자재비", value: 54, color: "#3B82F6" },
{ name: "인건비", value: 29, color: "#10B981" },
{ name: "경비", value: 17, color: "#F59E0B" }
];
return (
<div className="p-4 md:p-6 space-y-6">
{/* 헤더 */}
<div className="bg-card border border-border/20 rounded-xl p-6">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h1 className="text-3xl font-bold text-foreground mb-2"> </h1>
<p className="text-muted-foreground">/, , </p>
</div>
<div className="flex space-x-2">
<Button variant="outline">
<FileText className="h-4 w-4 mr-2" />
</Button>
<Button className="bg-primary">
<Calculator className="h-4 w-4 mr-2" />
</Button>
</div>
</div>
</div>
{/* 주요 지표 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card className="border border-border/20">
<CardHeader className="pb-3">
<CardTitle className="text-sm text-muted-foreground flex items-center justify-between">
<span> </span>
<DollarSign className="h-4 w-4 text-green-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-foreground mb-2">6,500</div>
<div className="flex items-center space-x-1 text-sm text-green-600">
<ArrowUpRight className="h-3 w-3" />
<span> +12.1%</span>
</div>
</CardContent>
</Card>
<Card className="border border-border/20">
<CardHeader className="pb-3">
<CardTitle className="text-sm text-muted-foreground flex items-center justify-between">
<span> </span>
<CreditCard className="h-4 w-4 text-orange-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-foreground mb-2">3,800</div>
<div className="flex items-center space-x-1 text-sm text-orange-600">
<ArrowUpRight className="h-3 w-3" />
<span> +11.8%</span>
</div>
</CardContent>
</Card>
<Card className="border border-border/20">
<CardHeader className="pb-3">
<CardTitle className="text-sm text-muted-foreground flex items-center justify-between">
<span> </span>
<TrendingUp className="h-4 w-4 text-blue-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-foreground mb-2">2,700</div>
<div className="flex items-center space-x-1 text-sm text-blue-600">
<span>이익률: 41.5%</span>
</div>
</CardContent>
</Card>
<Card className="border border-border/20">
<CardHeader className="pb-3">
<CardTitle className="text-sm text-muted-foreground flex items-center justify-between">
<span> </span>
<AlertCircle className="h-4 w-4 text-red-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-foreground mb-2">1,480</div>
<div className="flex items-center space-x-1 text-sm text-red-600">
<span>30 초과: 700만원</span>
</div>
</CardContent>
</Card>
</div>
{/* 탭 메뉴 */}
<Tabs defaultValue="sales" className="space-y-4">
<TabsList className="grid w-full grid-cols-5">
<TabsTrigger value="sales">/</TabsTrigger>
<TabsTrigger value="receivables"> </TabsTrigger>
<TabsTrigger value="cost"> </TabsTrigger>
<TabsTrigger value="profit"> </TabsTrigger>
<TabsTrigger value="transactions"></TabsTrigger>
</TabsList>
{/* 매출/매입 관리 */}
<TabsContent value="sales" className="space-y-4">
<Card className="border border-border/20">
<CardHeader>
<CardTitle className="flex items-center space-x-3">
<TrendingUp className="h-6 w-6 text-primary" />
<span>/ </span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="h-80 min-h-[20rem]">
<ResponsiveContainer width="100%" height="100%" minHeight={320}>
<LineChart data={salesPurchaseData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="sales" stroke="#10B981" strokeWidth={2} name="매출" />
<Line type="monotone" dataKey="purchase" stroke="#F59E0B" strokeWidth={2} name="매입" />
<Line type="monotone" dataKey="profit" stroke="#3B82F6" strokeWidth={2} name="이익" />
</LineChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
</TabsContent>
{/* 미수금 관리 */}
<TabsContent value="receivables" className="space-y-4">
<Card className="border border-border/20">
<CardHeader>
<CardTitle className="flex items-center space-x-3">
<CreditCard className="h-6 w-6 text-red-600" />
<span> </span>
</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead className="text-center"></TableHead>
<TableHead className="text-center"></TableHead>
<TableHead className="text-center"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{receivables.map((item, index) => (
<TableRow key={index}>
<TableCell className="font-medium">{item.company}</TableCell>
<TableCell className="text-right font-bold">
{item.amount.toLocaleString()}
</TableCell>
<TableCell className="text-center">{item.days}</TableCell>
<TableCell className="text-center">
<Badge
className={
item.status === "위험"
? "bg-red-500 text-white"
: item.status === "주의"
? "bg-yellow-500 text-white"
: "bg-green-500 text-white"
}
>
{item.status}
</Badge>
</TableCell>
<TableCell className="text-center">
<Button size="sm" variant="outline">
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
</TabsContent>
{/* 원가 분석 */}
<TabsContent value="cost" className="space-y-4">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
<Card className="border border-border/20">
<CardHeader>
<CardTitle className="flex items-center space-x-3">
<PieChart className="h-6 w-6 text-primary" />
<span> </span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="h-64 min-h-[16rem]">
<ResponsiveContainer width="100%" height="100%" minHeight={256}>
<RechartsPieChart>
<Pie
data={costComposition}
cx="50%"
cy="50%"
labelLine={false}
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
outerRadius={80}
fill="#8884d8"
dataKey="value"
>
{costComposition.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
<Tooltip />
</RechartsPieChart>
</ResponsiveContainer>
</div>
<div className="space-y-2 mt-4">
{costComposition.map((item, index) => (
<div key={index} className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div
className="w-4 h-4 rounded"
style={{ backgroundColor: item.color }}
/>
<span className="text-sm font-medium">{item.name}</span>
</div>
<span className="font-bold">{item.value}%</span>
</div>
))}
</div>
</CardContent>
</Card>
<Card className="border border-border/20">
<CardHeader>
<CardTitle className="flex items-center space-x-3">
<Calculator className="h-6 w-6 text-primary" />
<span> </span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3">
{costAnalysis.slice(0, 3).map((item, index) => (
<div
key={index}
className="p-3 bg-muted/50 rounded-lg border border-border/50"
>
<div className="flex justify-between items-start mb-2">
<div>
<span className="text-xs text-muted-foreground">{item.orderNo}</span>
<p className="font-bold text-sm">{item.product}</p>
</div>
<Badge
className={
item.profitRate >= 25
? "bg-green-500 text-white"
: item.profitRate >= 20
? "bg-blue-500 text-white"
: "bg-yellow-500 text-white"
}
>
{item.profitRate}%
</Badge>
</div>
<div className="grid grid-cols-2 gap-2 text-xs">
<div>
<span className="text-muted-foreground">: </span>
<span className="font-bold">
{(item.salesAmount / 1000000).toFixed(1)}M
</span>
</div>
<div>
<span className="text-muted-foreground">: </span>
<span className="font-bold">
{(item.totalCost / 1000000).toFixed(1)}M
</span>
</div>
<div className="col-span-2">
<span className="text-muted-foreground">: </span>
<span className="font-bold text-green-600">
{(item.profit / 1000000).toFixed(1)}M
</span>
</div>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
</TabsContent>
{/* 손익 현황 */}
<TabsContent value="profit" className="space-y-4">
<Card className="border border-border/20">
<CardHeader>
<CardTitle className="flex items-center space-x-3">
<Banknote className="h-6 w-6 text-primary" />
<span> </span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="h-80 min-h-[20rem]">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={salesPurchaseData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="profit" stroke="#10B981" strokeWidth={2} name="순이익" />
</LineChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
</TabsContent>
{/* 입출금 내역 */}
<TabsContent value="transactions" className="space-y-4">
<Card className="border border-border/20">
<CardHeader>
<CardTitle className="flex items-center space-x-3">
<Building2 className="h-6 w-6 text-primary" />
<span> </span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="p-4 bg-green-50 dark:bg-green-950/20 rounded-lg border border-green-200">
<div className="flex justify-between items-center">
<div>
<Badge className="bg-green-600 text-white mb-2"></Badge>
<p className="font-bold"></p>
<p className="text-sm text-muted-foreground">2024-10-13 14:30</p>
</div>
<div className="text-right">
<p className="text-2xl font-bold text-green-600">+25,000,000</p>
<p className="text-xs text-muted-foreground"> </p>
</div>
</div>
</div>
<div className="p-4 bg-red-50 dark:bg-red-950/20 rounded-lg border border-red-200">
<div className="flex justify-between items-center">
<div>
<Badge className="bg-red-600 text-white mb-2"></Badge>
<p className="font-bold"></p>
<p className="text-sm text-muted-foreground">2024-10-13 11:20</p>
</div>
<div className="text-right">
<p className="text-2xl font-bold text-red-600">-15,000,000</p>
<p className="text-xs text-muted-foreground"> </p>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
);
}