From eb3288dab73270392e62c3628afdae485a02cba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EB=B3=91=EC=B2=A0?= Date: Wed, 28 Jan 2026 15:48:17 +0900 Subject: [PATCH] =?UTF-8?q?fix(WEB):=20=EC=98=A4=EB=8A=98=EC=9D=98=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20notification=5Ftype=20=EA=B8=B0=EB=B0=98?= =?UTF-8?q?=20=EB=B3=80=ED=99=98=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit transformers.ts: - notification_type 영문 코드 기반 변환 (sales_order, bad_debt 등) - NOTIFICATION_TYPE_TO_BADGE 매핑 테이블 추가 - validateNotificationType 함수로 타입 안전성 확보 types.ts: - TodayIssueNotificationType 타입 추가 - TodayIssueListItem에 notificationType 필드 추가 TodayIssueSection: - notificationType 기반 색상 매핑 적용 Co-Authored-By: Claude Opus 4.5 --- .../sections/EnhancedSections.tsx | 2 +- .../sections/TodayIssueSection.tsx | 102 +++++++++--------- src/components/business/CEODashboard/types.ts | 18 +++- src/lib/api/dashboard/transformers.ts | 77 ++++++++----- src/lib/api/dashboard/types.ts | 15 ++- 5 files changed, 131 insertions(+), 83 deletions(-) diff --git a/src/components/business/CEODashboard/sections/EnhancedSections.tsx b/src/components/business/CEODashboard/sections/EnhancedSections.tsx index 546c7457..941679ef 100644 --- a/src/components/business/CEODashboard/sections/EnhancedSections.tsx +++ b/src/components/business/CEODashboard/sections/EnhancedSections.tsx @@ -486,7 +486,7 @@ export function EnhancedMonthlyExpenseSection({ data, onCardClick }: EnhancedMon ) : ( )} -

{cp.message}

+

{cp.message}

); })} diff --git a/src/components/business/CEODashboard/sections/TodayIssueSection.tsx b/src/components/business/CEODashboard/sections/TodayIssueSection.tsx index 253330f8..580a3bc3 100644 --- a/src/components/business/CEODashboard/sections/TodayIssueSection.tsx +++ b/src/components/business/CEODashboard/sections/TodayIssueSection.tsx @@ -26,9 +26,9 @@ import { Info, type LucideIcon, } from 'lucide-react'; -import type { TodayIssueListItem, TodayIssueListBadgeType } from '../types'; +import type { TodayIssueListItem, TodayIssueNotificationType } from '../types'; -// 뱃지 스타일 매핑 (아이콘 + 색상) +// 뱃지 스타일 매핑 (notification_type 코드 기반) interface BadgeStyle { bg: string; text: string; @@ -36,31 +36,18 @@ interface BadgeStyle { Icon: LucideIcon; } -const BADGE_STYLES: Record = { - '수주 성공': { bg: 'bg-blue-50', text: 'text-blue-700', iconBg: 'bg-blue-500', Icon: ShoppingCart }, - '추심 이슈': { bg: 'bg-purple-50', text: 'text-purple-700', iconBg: 'bg-purple-500', Icon: AlertCircle }, - '적정 재고': { bg: 'bg-orange-50', text: 'text-orange-700', iconBg: 'bg-orange-500', Icon: Package }, - '지출예상내역서': { bg: 'bg-green-50', text: 'text-green-700', iconBg: 'bg-green-500', Icon: Receipt }, - '세금 신고': { bg: 'bg-red-50', text: 'text-red-700', iconBg: 'bg-red-500', Icon: FileText }, - '결재 요청': { bg: 'bg-amber-50', text: 'text-amber-700', iconBg: 'bg-amber-500', Icon: CheckCircle2 }, - '신규거래처': { bg: 'bg-emerald-50', text: 'text-emerald-700', iconBg: 'bg-emerald-500', Icon: Building2 }, - '입금': { bg: 'bg-cyan-50', text: 'text-cyan-700', iconBg: 'bg-cyan-500', Icon: TrendingUp }, - '출금': { bg: 'bg-pink-50', text: 'text-pink-700', iconBg: 'bg-pink-500', Icon: TrendingDown }, - '기타': { bg: 'bg-gray-50', text: 'text-gray-700', iconBg: 'bg-gray-500', Icon: Info }, -}; - -// 기존 호환용 뱃지 색상 (legacy) -const BADGE_COLORS: Record = { - '수주 성공': 'bg-blue-100 text-blue-700 hover:bg-blue-100', - '추심 이슈': 'bg-purple-100 text-purple-700 hover:bg-purple-100', - '적정 재고': 'bg-orange-100 text-orange-700 hover:bg-orange-100', - '지출예상내역서': 'bg-green-100 text-green-700 hover:bg-green-100', - '세금 신고': 'bg-red-100 text-red-700 hover:bg-red-100', - '결재 요청': 'bg-yellow-100 text-yellow-700 hover:bg-yellow-100', - '신규거래처': 'bg-emerald-100 text-emerald-700 hover:bg-emerald-100', - '입금': 'bg-cyan-100 text-cyan-700 hover:bg-cyan-100', - '출금': 'bg-pink-100 text-pink-700 hover:bg-pink-100', - '기타': 'bg-gray-100 text-gray-700 hover:bg-gray-100', +// notification_type 코드 기반 스타일 매핑 (API 고정값 사용) +const NOTIFICATION_STYLES: Record = { + sales_order: { bg: 'bg-blue-50', text: 'text-blue-700', iconBg: 'bg-blue-500', Icon: ShoppingCart }, + bad_debt: { bg: 'bg-purple-50', text: 'text-purple-700', iconBg: 'bg-purple-500', Icon: AlertCircle }, + safety_stock: { bg: 'bg-orange-50', text: 'text-orange-700', iconBg: 'bg-orange-500', Icon: Package }, + expected_expense: { bg: 'bg-green-50', text: 'text-green-700', iconBg: 'bg-green-500', Icon: Receipt }, + vat_report: { bg: 'bg-red-50', text: 'text-red-700', iconBg: 'bg-red-500', Icon: FileText }, + approval_request: { bg: 'bg-amber-50', text: 'text-amber-700', iconBg: 'bg-amber-500', Icon: CheckCircle2 }, + new_vendor: { bg: 'bg-emerald-50', text: 'text-emerald-700', iconBg: 'bg-emerald-500', Icon: Building2 }, + deposit: { bg: 'bg-cyan-50', text: 'text-cyan-700', iconBg: 'bg-cyan-500', Icon: TrendingUp }, + withdrawal: { bg: 'bg-pink-50', text: 'text-pink-700', iconBg: 'bg-pink-500', Icon: TrendingDown }, + other: { bg: 'bg-gray-50', text: 'text-gray-700', iconBg: 'bg-gray-500', Icon: Info }, }; // 신용등급 색상 매핑 (A=녹색, B=노랑, C=주황, D=빨강) @@ -78,25 +65,33 @@ const getRandomCreditRating = (): CreditRating => { return ratings[Math.floor(Math.random() * ratings.length)]; }; -// 필터 옵션 키 (API TodayIssue 모델과 동기화) +// 필터 옵션 키 (notification_type 코드 기반) const FILTER_KEYS = [ 'all', - '수주 성공', - '추심 이슈', - '적정 재고', - '지출예상내역서', - '세금 신고', - '결재 요청', - '신규거래처', - '입금', - '출금', - '기타', + 'sales_order', + 'bad_debt', + 'safety_stock', + 'expected_expense', + 'vat_report', + 'approval_request', + 'new_vendor', + 'deposit', + 'withdrawal', + 'other', ] as const; -// badge를 필터 키로 변환 (정의되지 않은 타입은 기타로) -const getFilterKey = (badge: string): string => { - const knownBadges = ['수주 성공', '추심 이슈', '적정 재고', '지출예상내역서', '세금 신고', '결재 요청', '신규거래처', '입금', '출금']; - return knownBadges.includes(badge) ? badge : '기타'; +// notification_type → 한글 라벨 매핑 (필터 표시용) +const NOTIFICATION_TYPE_LABELS: Record = { + sales_order: '수주 성공', + bad_debt: '추심 이슈', + safety_stock: '적정 재고', + expected_expense: '지출예상내역서', + vat_report: '세금 신고', + approval_request: '결재 요청', + new_vendor: '신규거래처', + deposit: '입금', + withdrawal: '출금', + other: '기타', }; interface TodayIssueSectionProps { @@ -115,37 +110,37 @@ export function TodayIssueSection({ items }: TodayIssueSectionProps) { const creditRatings = useMemo(() => { const ratings: Record = {}; items.forEach((item) => { - if (item.badge === '신규거래처') { + if (item.notificationType === 'new_vendor') { ratings[item.id] = getRandomCreditRating(); } }); return ratings; }, [items]); - // 항목별 수량 계산 (입금/출금 등은 기타로 카운트) + // 항목별 수량 계산 (notification_type 코드 기반) const itemCounts = useMemo(() => { const counts: Record = { all: activeItems.length }; FILTER_KEYS.forEach((key) => { if (key !== 'all') { - counts[key] = activeItems.filter((item) => getFilterKey(item.badge) === key).length; + counts[key] = activeItems.filter((item) => item.notificationType === key).length; } }); return counts; }, [activeItems]); - // 필터 옵션 (수량 분리) + // 필터 옵션 (notification_type 코드 기반, 한글 라벨 표시) const filterOptions = useMemo(() => { return FILTER_KEYS.map((key) => ({ value: key, - label: key === 'all' ? '전체' : key, + label: key === 'all' ? '전체' : NOTIFICATION_TYPE_LABELS[key as TodayIssueNotificationType], count: itemCounts[key] || 0, })); }, [itemCounts]); - // 필터링된 아이템 (입금/출금 등은 기타로 분류) + // 필터링된 아이템 (notification_type 코드 기반) const filteredItems = filter === 'all' ? activeItems - : activeItems.filter((item) => getFilterKey(item.badge) === filter); + : activeItems.filter((item) => item.notificationType === filter); // 아이템 클릭 const handleItemClick = (item: TodayIssueListItem) => { @@ -205,8 +200,11 @@ export function TodayIssueSection({ items }: TodayIssueSectionProps) { ) : ( filteredItems.map((item) => { - const badgeStyle = BADGE_STYLES[item.badge as TodayIssueListBadgeType] || BADGE_STYLES['기타']; + // notification_type 코드 기반 스타일 매핑 (안정적) + const badgeStyle = NOTIFICATION_STYLES[item.notificationType] || NOTIFICATION_STYLES.other; const BadgeIcon = badgeStyle.Icon; + // 표시용 한글 라벨 + const displayLabel = NOTIFICATION_TYPE_LABELS[item.notificationType] || item.badge; return (
- {item.badge} + {displayLabel} @@ -230,7 +228,7 @@ export function TodayIssueSection({ items }: TodayIssueSectionProps) { {/* 신용등급 배지 (신규업체인 경우) */} - {item.badge === '신규업체' && creditRatings[item.id] && ( + {item.notificationType === 'new_vendor' && creditRatings[item.id] && (