feat(WEB): 파비콘 변경 및 거래처 신용분석 모달 추가
- 파비콘: SVG 형식으로 변경 (white 로고 + 파란 배경) - 헤더 로고: SAM 로고 이미지로 교체 - 거래처 상세: 신용분석 모달 컴포넌트 추가 - 신용등급, 리스크 지표, 레이더 차트 - 프린트 기능 지원 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
103
claudedocs/[PLAN-2026-01-23] vendor-credit-analysis-modal.md
Normal file
103
claudedocs/[PLAN-2026-01-23] vendor-credit-analysis-modal.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# 신규 거래처 신용분석 모달
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
- **목적**: 신규 거래처 등록 시 국가관리 API를 통해 받아온 기업 신용정보를 표시
|
||||||
|
- **위치**: 거래처 등록 완료 후 모달로 표시
|
||||||
|
- **현재 단계**: 목업 데이터로 UI 구현 (추후 API 연동)
|
||||||
|
|
||||||
|
## 화면 구성
|
||||||
|
|
||||||
|
### 1. 헤더
|
||||||
|
- 로고 + "SAM 기업 신용분석 리포트"
|
||||||
|
- 조회일시 표시
|
||||||
|
|
||||||
|
### 2. 기업 정보
|
||||||
|
- "신규거래 신용정보 조회" 뱃지
|
||||||
|
- "기업 신용 분석" 제목
|
||||||
|
- 사업자번호, 법인명 (대표자명), 평가기준일 정보
|
||||||
|
|
||||||
|
### 3. 자료 효력기간 안내
|
||||||
|
- 노란 배경의 알림 박스
|
||||||
|
- 데이터 유효기간 및 면책 안내
|
||||||
|
|
||||||
|
### 4. 종합 신용 신호등
|
||||||
|
- 5단계 신호등 표시 (Level 1~5)
|
||||||
|
- 현재 레벨 강조 (예: 양호 Level 4)
|
||||||
|
- 신용 등급 설명 텍스트
|
||||||
|
- "유료 상세 분석 제공받기" 버튼
|
||||||
|
|
||||||
|
### 5. 신용 리스크 프로필
|
||||||
|
- 오각형 레이더 차트
|
||||||
|
- 한국신용평가등급
|
||||||
|
- 금융 종합 위험도
|
||||||
|
- 매입 결제
|
||||||
|
- 매출 결제
|
||||||
|
- 저당권설정
|
||||||
|
|
||||||
|
### 6. 신용 상세 정보
|
||||||
|
- 신용채무정보 버튼
|
||||||
|
- 신용등급추이정보 버튼
|
||||||
|
- 정보 없음 안내 텍스트
|
||||||
|
|
||||||
|
### 7. 하단 거래 승인 판정
|
||||||
|
- 안전/위험 배지
|
||||||
|
- 신용등급 (Level 1~5)
|
||||||
|
- 거래 유형 (계속사업자/신규거래 등)
|
||||||
|
- 외상 가능 여부
|
||||||
|
- "거래 승인 완료" 버튼
|
||||||
|
|
||||||
|
## 데이터 구조
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface CreditAnalysisData {
|
||||||
|
// 기업 정보
|
||||||
|
businessNumber: string; // 사업자번호
|
||||||
|
companyName: string; // 법인명
|
||||||
|
representativeName: string; // 대표자명
|
||||||
|
evaluationDate: string; // 평가기준일
|
||||||
|
|
||||||
|
// 신용 등급
|
||||||
|
creditLevel: 1 | 2 | 3 | 4 | 5; // 1: 위험, 5: 최우량
|
||||||
|
creditStatus: '위험' | '주의' | '보통' | '양호' | '우량';
|
||||||
|
|
||||||
|
// 리스크 프로필 (0~100)
|
||||||
|
riskProfile: {
|
||||||
|
koreaCreditRating: number; // 한국신용평가등급
|
||||||
|
financialRisk: number; // 금융 종합 위험도
|
||||||
|
purchasePayment: number; // 매입 결제
|
||||||
|
salesPayment: number; // 매출 결제
|
||||||
|
mortgageSetting: number; // 저당권설정
|
||||||
|
};
|
||||||
|
|
||||||
|
// 거래 승인 판정
|
||||||
|
approval: {
|
||||||
|
safety: '안전' | '주의' | '위험';
|
||||||
|
level: number;
|
||||||
|
businessType: string; // 계속사업자, 신규거래 등
|
||||||
|
creditAvailable: boolean; // 외상 가능 여부
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 파일 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
src/components/accounting/VendorManagement/
|
||||||
|
├── CreditAnalysisModal.tsx # 신용분석 모달 컴포넌트
|
||||||
|
└── CreditAnalysisModal/
|
||||||
|
├── index.tsx # 메인 모달
|
||||||
|
├── CreditSignal.tsx # 신용 신호등 컴포넌트
|
||||||
|
├── RiskRadarChart.tsx # 레이더 차트 컴포넌트
|
||||||
|
└── types.ts # 타입 정의
|
||||||
|
|
||||||
|
src/app/[locale]/(protected)/dev/
|
||||||
|
└── credit-analysis-test/
|
||||||
|
└── page.tsx # 테스트 페이지
|
||||||
|
```
|
||||||
|
|
||||||
|
## 구현 순서
|
||||||
|
|
||||||
|
1. [x] 계획 md 파일 작성
|
||||||
|
2. [ ] CreditAnalysisModal 컴포넌트 생성
|
||||||
|
3. [ ] 테스트 페이지 생성
|
||||||
|
4. [ ] dev/test-urls에 URL 추가
|
||||||
BIN
public/sam-logo.png
Normal file
BIN
public/sam-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
4
src/app/icon.svg
Normal file
4
src/app/icon.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 193 192">
|
||||||
|
<rect width="193" height="192" rx="24" fill="#3B82F6"/>
|
||||||
|
<image x="0" y="0" width="193" height="192" xlink:href="data:image/png;base64,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"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
@@ -0,0 +1,208 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { AlertTriangle, CheckCircle } from 'lucide-react';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { CreditSignal } from './CreditSignal';
|
||||||
|
import { RiskRadarChart } from './RiskRadarChart';
|
||||||
|
import type { CreditAnalysisData } from './types';
|
||||||
|
import { CREDIT_LEVEL_CONFIG } from './types';
|
||||||
|
|
||||||
|
interface CreditAnalysisDocumentProps {
|
||||||
|
data: CreditAnalysisData;
|
||||||
|
onApprove?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CreditAnalysisDocument({
|
||||||
|
data,
|
||||||
|
onApprove,
|
||||||
|
}: CreditAnalysisDocumentProps) {
|
||||||
|
const levelConfig = CREDIT_LEVEL_CONFIG[data.creditLevel];
|
||||||
|
const [activeTab, setActiveTab] = useState('shortTermOverdue');
|
||||||
|
|
||||||
|
// 탭 콘텐츠 렌더링
|
||||||
|
const renderTabContent = (content: string | null, emptyMessage: string) => {
|
||||||
|
if (content) {
|
||||||
|
return <p className="text-sm text-gray-700">{content}</p>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="text-center py-4">
|
||||||
|
<p className="text-sm text-gray-500">▲ {emptyMessage}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white p-8 min-h-full">
|
||||||
|
{/* 헤더 */}
|
||||||
|
<div className="flex items-center justify-between border-b pb-4 mb-6">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-8 h-8 bg-green-500 rounded flex items-center justify-center">
|
||||||
|
<span className="text-white font-bold text-sm">S</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-lg font-semibold">
|
||||||
|
SAM 기업 신용분석 리포트
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<span className="text-sm text-gray-500">
|
||||||
|
{data.evaluationDate}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* 기업 정보 */}
|
||||||
|
<div className="text-center">
|
||||||
|
<Badge variant="outline" className="mb-2 bg-blue-50 text-blue-600 border-blue-200">
|
||||||
|
신규거래 신용정보 조회
|
||||||
|
</Badge>
|
||||||
|
<h2 className="text-xl font-bold text-gray-800 mt-2">
|
||||||
|
- 기업 신용 분석 -
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-gray-600 mt-2">
|
||||||
|
사업자번호: {data.businessNumber} | {data.companyName} | 거래기준일: {data.evaluationDate.split(' ')[0]}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
|
조회일자: {data.queryDate}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 자료 효력기간 안내 */}
|
||||||
|
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<AlertTriangle className="w-5 h-5 text-yellow-600 flex-shrink-0 mt-0.5" />
|
||||||
|
<div className="text-sm">
|
||||||
|
<p className="font-semibold text-yellow-800">자료 효력기간 안내</p>
|
||||||
|
<p className="text-yellow-700 mt-1">
|
||||||
|
본 보고서(신용분석 6개월 {data.evaluationDate}) 자료는 기준일 당시의 시점이며, 이후 발생한 신용상태의 변동은 반영되지 않았음을 알려드립니다. 신용분석 결과에 의한 거래 관련 판단 및 결정에 대해서는 본사에서 책임지지 않습니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 종합 신용 신호등 & 리스크 프로필 */}
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
{/* 종합 신용 신호등 */}
|
||||||
|
<div className="bg-gray-50 rounded-lg p-6">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-800 mb-4 text-center">
|
||||||
|
종합 신용 신호등
|
||||||
|
</h3>
|
||||||
|
<CreditSignal
|
||||||
|
level={data.creditLevel}
|
||||||
|
taxStatus={data.taxStatus}
|
||||||
|
/>
|
||||||
|
<div className="mt-4 text-center">
|
||||||
|
<Button variant="outline" size="sm" className="text-blue-600 border-blue-300">
|
||||||
|
유료 상세 분석 제공받기
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 신용 리스크 프로필 */}
|
||||||
|
<div className="bg-gray-50 rounded-lg p-6">
|
||||||
|
<RiskRadarChart data={data.riskProfile} creditDetailInfo={data.creditDetailInfo} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 신용 상세 정보 - 탭 */}
|
||||||
|
<div className="border rounded-lg p-4">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-800 mb-4 text-center">
|
||||||
|
신용 상세 정보
|
||||||
|
</h3>
|
||||||
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
||||||
|
<TabsList className="grid w-full grid-cols-4">
|
||||||
|
<TabsTrigger value="shortTermOverdue" className="text-xs sm:text-sm">
|
||||||
|
단기연체정보
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="creditJudgment" className="text-xs sm:text-sm">
|
||||||
|
신용도판단정보
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="checkingSuspension" className="text-xs sm:text-sm">
|
||||||
|
당좌거래정지
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="courtManagement" className="text-xs sm:text-sm">
|
||||||
|
법정관리/워크아웃
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<TabsContent value="shortTermOverdue" className="mt-4">
|
||||||
|
{renderTabContent(data.creditDetailTab.shortTermOverdue, '단기연체 정보가 없습니다')}
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="creditJudgment" className="mt-4">
|
||||||
|
{renderTabContent(data.creditDetailTab.creditJudgment, '신용도판단 정보가 없습니다')}
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="checkingSuspension" className="mt-4">
|
||||||
|
{renderTabContent(data.creditDetailTab.checkingSuspension, '당좌거래정지 정보가 없습니다')}
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="courtManagement" className="mt-4">
|
||||||
|
{renderTabContent(data.creditDetailTab.courtManagement, '법정관리/워크아웃 정보가 없습니다')}
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 거래 승인 판정 */}
|
||||||
|
<div className="bg-gray-100 rounded-lg p-4">
|
||||||
|
<div className="flex items-center justify-between flex-wrap gap-4">
|
||||||
|
<div className="flex items-center gap-6 flex-wrap">
|
||||||
|
<div className="text-center">
|
||||||
|
<span className="text-sm text-gray-600">거래 승인 판정</span>
|
||||||
|
<div className="mt-1">
|
||||||
|
<Badge
|
||||||
|
className={cn(
|
||||||
|
'text-base px-4 py-1',
|
||||||
|
data.approval.safety === '안전'
|
||||||
|
? 'bg-green-500 hover:bg-green-600'
|
||||||
|
: data.approval.safety === '주의'
|
||||||
|
? 'bg-yellow-500 hover:bg-yellow-600'
|
||||||
|
: 'bg-red-500 hover:bg-red-600'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{data.approval.safety}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="h-12 border-l border-gray-300 hidden sm:block" />
|
||||||
|
<div className="text-center">
|
||||||
|
<span className="text-sm text-gray-600">신용등급</span>
|
||||||
|
<p className={cn('font-bold', levelConfig.color)}>
|
||||||
|
Level {data.approval.level}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="h-12 border-l border-gray-300 hidden sm:block" />
|
||||||
|
<div className="text-center">
|
||||||
|
<span className="text-sm text-gray-600">거래</span>
|
||||||
|
<p className="font-medium text-gray-800">{data.approval.businessType}</p>
|
||||||
|
</div>
|
||||||
|
<div className="h-12 border-l border-gray-300 hidden sm:block" />
|
||||||
|
<div className="text-center">
|
||||||
|
<span className="text-sm text-gray-600">외상가능</span>
|
||||||
|
<p className={cn(
|
||||||
|
'font-medium',
|
||||||
|
data.approval.creditAvailable ? 'text-green-600' : 'text-red-600'
|
||||||
|
)}>
|
||||||
|
{data.approval.creditAvailable ? '가능' : '불가'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{onApprove && (
|
||||||
|
<Button
|
||||||
|
onClick={onApprove}
|
||||||
|
className="bg-green-500 hover:bg-green-600 gap-2"
|
||||||
|
>
|
||||||
|
<CheckCircle className="w-4 h-4" />
|
||||||
|
거래 승인 완료
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 푸터 */}
|
||||||
|
<div className="text-center text-xs text-gray-400 pt-2 border-t">
|
||||||
|
<p>SAM Intelligence</p>
|
||||||
|
<p>본 신용정보 보고서에 수록된 정보는 특정기업의 신용을 평가하는 자료로 활용될 수 있습니다</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import type { CreditLevel } from './types';
|
||||||
|
import { CREDIT_LEVEL_CONFIG } from './types';
|
||||||
|
|
||||||
|
interface CreditSignalProps {
|
||||||
|
level: CreditLevel;
|
||||||
|
taxStatus?: string;
|
||||||
|
showDescription?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CreditSignal({
|
||||||
|
level,
|
||||||
|
taxStatus,
|
||||||
|
showDescription = false,
|
||||||
|
}: CreditSignalProps) {
|
||||||
|
const config = CREDIT_LEVEL_CONFIG[level];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center gap-4">
|
||||||
|
{/* 신호등 */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{[1, 2, 3, 4, 5].map((l) => (
|
||||||
|
<div
|
||||||
|
key={l}
|
||||||
|
className={cn(
|
||||||
|
'w-8 h-8 rounded-full border-2 transition-all',
|
||||||
|
l <= level
|
||||||
|
? cn(config.bgColor, 'border-transparent')
|
||||||
|
: 'bg-gray-200 border-gray-300'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 등급 표시 */}
|
||||||
|
<div className="text-center">
|
||||||
|
<div className={cn('text-2xl font-bold', config.color)}>
|
||||||
|
{config.status} (Level {level})
|
||||||
|
</div>
|
||||||
|
{taxStatus && (
|
||||||
|
<div className="text-sm text-gray-500 mt-1">
|
||||||
|
{taxStatus}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 설명 */}
|
||||||
|
{showDescription && (
|
||||||
|
<p className="text-sm text-gray-600 text-center max-w-xs">
|
||||||
|
{config.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Radar,
|
||||||
|
RadarChart,
|
||||||
|
PolarGrid,
|
||||||
|
PolarAngleAxis,
|
||||||
|
PolarRadiusAxis,
|
||||||
|
ResponsiveContainer,
|
||||||
|
} from 'recharts';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import type { RiskProfile, CreditDetailInfo } from './types';
|
||||||
|
|
||||||
|
interface RiskRadarChartProps {
|
||||||
|
data: RiskProfile;
|
||||||
|
creditDetailInfo?: CreditDetailInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RISK_LABELS: Record<keyof RiskProfile, string> = {
|
||||||
|
corporateRisk: '기업 리스크',
|
||||||
|
publicRecord: '공공기록',
|
||||||
|
stability: '안정성',
|
||||||
|
growth: '성장성',
|
||||||
|
overdueHistory: '연체이력',
|
||||||
|
};
|
||||||
|
|
||||||
|
const DETAIL_LABELS: Record<keyof CreditDetailInfo, string> = {
|
||||||
|
koreaCreditRating: '한국신용평가등급',
|
||||||
|
financialRisk: '금융 종합 위험도',
|
||||||
|
purchasePayment: '매입 결제',
|
||||||
|
salesPayment: '매출 결제',
|
||||||
|
mortgageSetting: '저당권설정',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function RiskRadarChart({ data, creditDetailInfo }: RiskRadarChartProps) {
|
||||||
|
const chartData = Object.entries(data).map(([key, value]) => ({
|
||||||
|
subject: RISK_LABELS[key as keyof RiskProfile],
|
||||||
|
value,
|
||||||
|
fullMark: 100,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-800 mb-4">신용 리스크 프로필</h3>
|
||||||
|
|
||||||
|
<div className="w-full max-w-[300px] h-[220px]">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<RadarChart cx="50%" cy="50%" outerRadius="70%" data={chartData}>
|
||||||
|
<PolarGrid stroke="#e5e7eb" />
|
||||||
|
<PolarAngleAxis
|
||||||
|
dataKey="subject"
|
||||||
|
tick={{ fill: '#6b7280', fontSize: 11 }}
|
||||||
|
/>
|
||||||
|
<PolarRadiusAxis
|
||||||
|
angle={90}
|
||||||
|
domain={[0, 100]}
|
||||||
|
tick={false}
|
||||||
|
axisLine={false}
|
||||||
|
/>
|
||||||
|
<Radar
|
||||||
|
name="신용 점수"
|
||||||
|
dataKey="value"
|
||||||
|
stroke="#3b82f6"
|
||||||
|
fill="#93c5fd"
|
||||||
|
fillOpacity={0.5}
|
||||||
|
strokeWidth={2}
|
||||||
|
/>
|
||||||
|
</RadarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 레이더 차트 하단 상세 정보 */}
|
||||||
|
{creditDetailInfo && (
|
||||||
|
<div className="w-full mt-4 grid grid-cols-2 gap-x-6 gap-y-2">
|
||||||
|
{Object.entries(creditDetailInfo).map(([key, value]) => (
|
||||||
|
<div key={key} className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600">{DETAIL_LABELS[key as keyof CreditDetailInfo]}</span>
|
||||||
|
<span className={cn(
|
||||||
|
'font-medium',
|
||||||
|
value === '우량' ? 'text-blue-600' :
|
||||||
|
value === '양호' ? 'text-green-600' :
|
||||||
|
value === '보통' ? 'text-yellow-600' :
|
||||||
|
value === '주의' ? 'text-orange-600' :
|
||||||
|
'text-red-600'
|
||||||
|
)}>
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { DocumentViewer } from '@/components/document-system';
|
||||||
|
import { CreditAnalysisDocument } from './CreditAnalysisDocument';
|
||||||
|
import type { CreditAnalysisData } from './types';
|
||||||
|
|
||||||
|
interface CreditAnalysisModalProps {
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
data: CreditAnalysisData;
|
||||||
|
onApprove?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CreditAnalysisModal({
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
data,
|
||||||
|
onApprove,
|
||||||
|
}: CreditAnalysisModalProps) {
|
||||||
|
return (
|
||||||
|
<DocumentViewer
|
||||||
|
title="SAM 기업 신용분석 리포트"
|
||||||
|
subtitle={`${data.companyName} | ${data.businessNumber}`}
|
||||||
|
open={open}
|
||||||
|
onOpenChange={onOpenChange}
|
||||||
|
features={{
|
||||||
|
print: true,
|
||||||
|
zoom: true,
|
||||||
|
drag: true,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CreditAnalysisDocument data={data} onApprove={onApprove} />
|
||||||
|
</DocumentViewer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { MOCK_CREDIT_DATA } from './types';
|
||||||
|
export type { CreditAnalysisData } from './types';
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
// ===== 기업 신용분석 데이터 타입 =====
|
||||||
|
|
||||||
|
export type CreditLevel = 1 | 2 | 3 | 4 | 5;
|
||||||
|
export type CreditStatus = '위험' | '주의' | '보통' | '양호' | '우량';
|
||||||
|
export type SafetyLevel = '안전' | '주의' | '위험';
|
||||||
|
export type RiskStatus = '우량' | '양호' | '보통' | '주의' | '위험';
|
||||||
|
|
||||||
|
// 레이더 차트용 - 신용 리스크 프로필 (5각형)
|
||||||
|
export interface RiskProfile {
|
||||||
|
corporateRisk: number; // 기업 리스크 (0~100)
|
||||||
|
publicRecord: number; // 공공기록 (0~100)
|
||||||
|
stability: number; // 안정성 (0~100)
|
||||||
|
growth: number; // 성장성 (0~100)
|
||||||
|
overdueHistory: number; // 연체이력 (0~100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 신호등 아래 표시될 상세 정보
|
||||||
|
export interface CreditDetailInfo {
|
||||||
|
koreaCreditRating: RiskStatus; // 한국신용평가등급
|
||||||
|
financialRisk: RiskStatus; // 금융 종합 위험도
|
||||||
|
purchasePayment: RiskStatus; // 매입 결제
|
||||||
|
salesPayment: RiskStatus; // 매출 결제
|
||||||
|
mortgageSetting: RiskStatus; // 저당권설정
|
||||||
|
}
|
||||||
|
|
||||||
|
// 신용 상세 정보 탭 데이터
|
||||||
|
export interface CreditDetailTabData {
|
||||||
|
shortTermOverdue: string | null; // 단기연체정보
|
||||||
|
creditJudgment: string | null; // 신용도판단정보
|
||||||
|
checkingSuspension: string | null; // 당좌거래정지
|
||||||
|
courtManagement: string | null; // 법정관리/워크아웃
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApprovalInfo {
|
||||||
|
safety: SafetyLevel;
|
||||||
|
level: CreditLevel;
|
||||||
|
businessType: string; // 계속사업자, 신규거래 등
|
||||||
|
creditAvailable: boolean; // 외상 가능 여부
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreditAnalysisData {
|
||||||
|
// 기업 정보
|
||||||
|
businessNumber: string; // 사업자번호
|
||||||
|
companyName: string; // 법인명
|
||||||
|
representativeName: string; // 대표자명
|
||||||
|
evaluationDate: string; // 평가기준일
|
||||||
|
queryDate: string; // 조회일자
|
||||||
|
|
||||||
|
// 신용 등급
|
||||||
|
creditLevel: CreditLevel;
|
||||||
|
creditStatus: CreditStatus;
|
||||||
|
taxStatus: string; // 국세청 상태 (예: "국세청 상태 기준")
|
||||||
|
|
||||||
|
// 리스크 프로필 (레이더 차트용)
|
||||||
|
riskProfile: RiskProfile;
|
||||||
|
|
||||||
|
// 신호등 아래 상세 정보
|
||||||
|
creditDetailInfo: CreditDetailInfo;
|
||||||
|
|
||||||
|
// 신용 상세 정보 탭 데이터
|
||||||
|
creditDetailTab: CreditDetailTabData;
|
||||||
|
|
||||||
|
// 거래 승인 판정
|
||||||
|
approval: ApprovalInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 신용 레벨 설정 =====
|
||||||
|
export const CREDIT_LEVEL_CONFIG: Record<CreditLevel, {
|
||||||
|
status: CreditStatus;
|
||||||
|
color: string;
|
||||||
|
bgColor: string;
|
||||||
|
description: string;
|
||||||
|
}> = {
|
||||||
|
1: {
|
||||||
|
status: '위험',
|
||||||
|
color: 'text-red-600',
|
||||||
|
bgColor: 'bg-red-500',
|
||||||
|
description: '신용 위험 등급으로 거래 시 주의가 필요합니다.',
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
status: '주의',
|
||||||
|
color: 'text-orange-600',
|
||||||
|
bgColor: 'bg-orange-500',
|
||||||
|
description: '신용 주의 등급으로 거래 조건 검토가 필요합니다.',
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
status: '보통',
|
||||||
|
color: 'text-yellow-600',
|
||||||
|
bgColor: 'bg-yellow-500',
|
||||||
|
description: '신용 보통 등급으로 일반적인 거래가 가능합니다.',
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
status: '양호',
|
||||||
|
color: 'text-green-600',
|
||||||
|
bgColor: 'bg-green-500',
|
||||||
|
description: '신용 양호 등급으로 안정적인 거래가 가능합니다.',
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
status: '우량',
|
||||||
|
color: 'text-blue-600',
|
||||||
|
bgColor: 'bg-blue-500',
|
||||||
|
description: '신용 우량 등급으로 최상의 거래 조건이 가능합니다.',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 목업 데이터 =====
|
||||||
|
export const MOCK_CREDIT_DATA: CreditAnalysisData = {
|
||||||
|
businessNumber: '514-87-00635',
|
||||||
|
companyName: '(주)한가 양산공장',
|
||||||
|
representativeName: '홍길동',
|
||||||
|
evaluationDate: '2026-01-22 21:05:04',
|
||||||
|
queryDate: '2026-01-23 09:30:00',
|
||||||
|
creditLevel: 4,
|
||||||
|
creditStatus: '양호',
|
||||||
|
taxStatus: '국세청 상태 기준',
|
||||||
|
riskProfile: {
|
||||||
|
corporateRisk: 80,
|
||||||
|
publicRecord: 75,
|
||||||
|
stability: 85,
|
||||||
|
growth: 70,
|
||||||
|
overdueHistory: 90,
|
||||||
|
},
|
||||||
|
creditDetailInfo: {
|
||||||
|
koreaCreditRating: '양호',
|
||||||
|
financialRisk: '양호',
|
||||||
|
purchasePayment: '양호',
|
||||||
|
salesPayment: '양호',
|
||||||
|
mortgageSetting: '양호',
|
||||||
|
},
|
||||||
|
creditDetailTab: {
|
||||||
|
shortTermOverdue: null,
|
||||||
|
creditJudgment: null,
|
||||||
|
checkingSuspension: null,
|
||||||
|
courtManagement: null,
|
||||||
|
},
|
||||||
|
approval: {
|
||||||
|
safety: '안전',
|
||||||
|
level: 4,
|
||||||
|
businessType: '계속사업자',
|
||||||
|
creditAvailable: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -8,6 +8,7 @@ import { toast } from 'sonner';
|
|||||||
import { getClientById, createClient, updateClient, deleteClient } from './actions';
|
import { getClientById, createClient, updateClient, deleteClient } from './actions';
|
||||||
import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
|
import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
|
||||||
import { vendorConfig } from './vendorConfig';
|
import { vendorConfig } from './vendorConfig';
|
||||||
|
import { CreditAnalysisModal, MOCK_CREDIT_DATA } from './CreditAnalysisModal';
|
||||||
|
|
||||||
// 필드명 매핑
|
// 필드명 매핑
|
||||||
const FIELD_NAME_MAP: Record<string, string> = {
|
const FIELD_NAME_MAP: Record<string, string> = {
|
||||||
@@ -145,6 +146,9 @@ export function VendorDetail({ mode, vendorId }: VendorDetailProps) {
|
|||||||
// 새 메모 입력
|
// 새 메모 입력
|
||||||
const [newMemo, setNewMemo] = useState('');
|
const [newMemo, setNewMemo] = useState('');
|
||||||
|
|
||||||
|
// 신용분석 모달
|
||||||
|
const [isCreditModalOpen, setIsCreditModalOpen] = useState(false);
|
||||||
|
|
||||||
// Validation 함수
|
// Validation 함수
|
||||||
const validateForm = useCallback(() => {
|
const validateForm = useCallback(() => {
|
||||||
const errors: Record<string, string> = {};
|
const errors: Record<string, string> = {};
|
||||||
@@ -478,8 +482,17 @@ export function VendorDetail({ mode, vendorId }: VendorDetailProps) {
|
|||||||
|
|
||||||
{/* 신용/거래 정보 */}
|
{/* 신용/거래 정보 */}
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader className="flex flex-row items-center justify-between">
|
||||||
<CardTitle className="text-lg">신용/거래 정보</CardTitle>
|
<CardTitle className="text-lg">신용/거래 정보</CardTitle>
|
||||||
|
{isViewMode && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setIsCreditModalOpen(true)}
|
||||||
|
>
|
||||||
|
신용정보 보기
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<CardContent className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
{renderSelectField('신용등급', 'creditRating', formData.creditRating, CREDIT_RATING_SELECTOR_OPTIONS)}
|
{renderSelectField('신용등급', 'creditRating', formData.creditRating, CREDIT_RATING_SELECTOR_OPTIONS)}
|
||||||
@@ -635,16 +648,25 @@ export function VendorDetail({ mode, vendorId }: VendorDetailProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntegratedDetailTemplate
|
<>
|
||||||
config={dynamicConfig}
|
<IntegratedDetailTemplate
|
||||||
mode={templateMode}
|
config={dynamicConfig}
|
||||||
initialData={formData as unknown as Record<string, unknown>}
|
mode={templateMode}
|
||||||
itemId={vendorId}
|
initialData={formData as unknown as Record<string, unknown>}
|
||||||
isLoading={isLoading}
|
itemId={vendorId}
|
||||||
onSubmit={handleSubmit}
|
isLoading={isLoading}
|
||||||
onDelete={vendorId ? handleDelete : undefined}
|
onSubmit={handleSubmit}
|
||||||
renderView={() => renderFormContent()}
|
onDelete={vendorId ? handleDelete : undefined}
|
||||||
renderForm={() => renderFormContent()}
|
renderView={() => renderFormContent()}
|
||||||
/>
|
renderForm={() => renderFormContent()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 신용분석 모달 */}
|
||||||
|
<CreditAnalysisModal
|
||||||
|
open={isCreditModalOpen}
|
||||||
|
onOpenChange={setIsCreditModalOpen}
|
||||||
|
data={MOCK_CREDIT_DATA}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
|
import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
|
||||||
import { vendorConfig } from './vendorConfig';
|
import { vendorConfig } from './vendorConfig';
|
||||||
|
import { CreditAnalysisModal, MOCK_CREDIT_DATA } from './CreditAnalysisModal';
|
||||||
import type { Vendor, VendorMemo } from './types';
|
import type { Vendor, VendorMemo } from './types';
|
||||||
import {
|
import {
|
||||||
VENDOR_CATEGORY_SELECTOR_OPTIONS,
|
VENDOR_CATEGORY_SELECTOR_OPTIONS,
|
||||||
@@ -113,6 +114,9 @@ export function VendorDetailClient({ mode, vendorId, initialData }: VendorDetail
|
|||||||
// 새 메모 입력
|
// 새 메모 입력
|
||||||
const [newMemo, setNewMemo] = useState('');
|
const [newMemo, setNewMemo] = useState('');
|
||||||
|
|
||||||
|
// 신용분석 모달
|
||||||
|
const [isCreditModalOpen, setIsCreditModalOpen] = useState(false);
|
||||||
|
|
||||||
// 상세/수정 모드에서 로고 목데이터 초기화
|
// 상세/수정 모드에서 로고 목데이터 초기화
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialData && !formData.logoUrl) {
|
if (initialData && !formData.logoUrl) {
|
||||||
@@ -428,8 +432,15 @@ export function VendorDetailClient({ mode, vendorId, initialData }: VendorDetail
|
|||||||
|
|
||||||
{/* 신용/거래 정보 */}
|
{/* 신용/거래 정보 */}
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader className="flex flex-row items-center justify-between">
|
||||||
<CardTitle className="text-lg">신용/거래 정보</CardTitle>
|
<CardTitle className="text-lg">신용/거래 정보</CardTitle>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setIsCreditModalOpen(true)}
|
||||||
|
>
|
||||||
|
신용정보 보기
|
||||||
|
</Button>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<CardContent className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
{renderSelectField('신용등급', 'creditRating', formData.creditRating, CREDIT_RATING_SELECTOR_OPTIONS)}
|
{renderSelectField('신용등급', 'creditRating', formData.creditRating, CREDIT_RATING_SELECTOR_OPTIONS)}
|
||||||
@@ -549,15 +560,24 @@ export function VendorDetailClient({ mode, vendorId, initialData }: VendorDetail
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntegratedDetailTemplate
|
<>
|
||||||
config={dynamicConfig}
|
<IntegratedDetailTemplate
|
||||||
mode={templateMode}
|
config={dynamicConfig}
|
||||||
initialData={initialData as Record<string, unknown>}
|
mode={templateMode}
|
||||||
itemId={vendorId}
|
initialData={initialData as Record<string, unknown>}
|
||||||
onSubmit={handleSubmit}
|
itemId={vendorId}
|
||||||
onDelete={vendorId ? handleDelete : undefined}
|
onSubmit={handleSubmit}
|
||||||
renderView={() => renderFormContent()}
|
onDelete={vendorId ? handleDelete : undefined}
|
||||||
renderForm={() => renderFormContent()}
|
renderView={() => renderFormContent()}
|
||||||
/>
|
renderForm={() => renderFormContent()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 신용분석 모달 */}
|
||||||
|
<CreditAnalysisModal
|
||||||
|
open={isCreditModalOpen}
|
||||||
|
onOpenChange={setIsCreditModalOpen}
|
||||||
|
data={MOCK_CREDIT_DATA}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useMenuStore } from '@/store/menuStore';
|
|||||||
import type { SerializableMenuItem } from '@/store/menuStore';
|
import type { SerializableMenuItem } from '@/store/menuStore';
|
||||||
import type { MenuItem } from '@/store/menuStore';
|
import type { MenuItem } from '@/store/menuStore';
|
||||||
import { useRouter, usePathname } from 'next/navigation';
|
import { useRouter, usePathname } from 'next/navigation';
|
||||||
|
import Image from 'next/image';
|
||||||
import { useEffect, useState, useMemo, useCallback } from 'react';
|
import { useEffect, useState, useMemo, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
Menu,
|
Menu,
|
||||||
@@ -572,8 +573,8 @@ export default function AuthenticatedLayout({ children }: AuthenticatedLayoutPro
|
|||||||
onClick={handleGoHome}
|
onClick={handleGoHome}
|
||||||
title="대시보드로 이동"
|
title="대시보드로 이동"
|
||||||
>
|
>
|
||||||
<div className="w-6 h-6 min-w-6 min-h-6 min-[320px]:w-8 min-[320px]:h-8 min-[320px]:min-w-8 min-[320px]:min-h-8 sm:w-10 sm:h-10 sm:min-w-10 sm:min-h-10 flex-shrink-0 aspect-square rounded-lg min-[320px]:rounded-xl flex items-center justify-center shadow-md relative overflow-hidden bg-gradient-to-br from-blue-500 to-blue-600">
|
<div className="w-6 h-6 min-w-6 min-h-6 min-[320px]:w-8 min-[320px]:h-8 min-[320px]:min-w-8 min-[320px]:min-h-8 sm:w-10 sm:h-10 sm:min-w-10 sm:min-h-10 flex-shrink-0 aspect-square rounded-lg min-[320px]:rounded-xl flex items-center justify-center shadow-md relative overflow-hidden">
|
||||||
<div className="text-white font-bold text-sm min-[320px]:text-base sm:text-lg">S</div>
|
<Image src="/sam-logo.png" alt="SAM" fill className="object-contain p-0.5" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="font-bold text-foreground text-left text-xs min-[320px]:text-sm sm:text-base">SAM</h1>
|
<h1 className="font-bold text-foreground text-left text-xs min-[320px]:text-sm sm:text-base">SAM</h1>
|
||||||
@@ -830,8 +831,8 @@ export default function AuthenticatedLayout({ children }: AuthenticatedLayoutPro
|
|||||||
onClick={handleGoHome}
|
onClick={handleGoHome}
|
||||||
title="대시보드로 이동"
|
title="대시보드로 이동"
|
||||||
>
|
>
|
||||||
<div className="w-12 h-12 rounded-xl flex items-center justify-center shadow-md relative overflow-hidden bg-gradient-to-br from-blue-500 to-blue-600 flex-shrink-0">
|
<div className="w-12 h-12 rounded-xl flex items-center justify-center shadow-md relative overflow-hidden flex-shrink-0">
|
||||||
<div className="text-white font-bold text-xl">S</div>
|
<Image src="/sam-logo.png" alt="SAM" fill className="object-contain p-1" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-bold text-foreground">SAM</h1>
|
<h1 className="text-xl font-bold text-foreground">SAM</h1>
|
||||||
|
|||||||
Reference in New Issue
Block a user