This commit is contained in:
유병철
2026-01-21 20:56:38 +09:00
21 changed files with 2846 additions and 31 deletions

View File

@@ -0,0 +1,6 @@
/**
* CEO Dashboard API 모듈 export
*/
export * from './types';
export * from './transformers';

View File

@@ -0,0 +1,642 @@
/**
* CEO Dashboard API 응답 → Frontend 타입 변환 함수
*
* 참조: docs/plans/AI_리포트_키워드_색상체계_가이드_v1.4.md
*/
import type {
DailyReportApiResponse,
ReceivablesApiResponse,
BadDebtApiResponse,
ExpectedExpenseApiResponse,
CardTransactionApiResponse,
StatusBoardApiResponse,
TodayIssueApiResponse,
CalendarApiResponse,
VatApiResponse,
EntertainmentApiResponse,
WelfareApiResponse,
} from './types';
import type {
DailyReportData,
ReceivableData,
DebtCollectionData,
MonthlyExpenseData,
CardManagementData,
TodayIssueItem,
TodayIssueListItem,
TodayIssueListBadgeType,
CalendarScheduleItem,
CheckPoint,
CheckPointType,
VatData,
EntertainmentData,
WelfareData,
HighlightColor,
} from '@/components/business/CEODashboard/types';
// ============================================
// 헬퍼 함수
// ============================================
/**
* 금액 포맷팅
* @example formatAmount(3050000000) → "30.5억원"
*/
function formatAmount(amount: number): string {
const absAmount = Math.abs(amount);
if (absAmount >= 100000000) {
return `${(amount / 100000000).toFixed(1)}억원`;
} else if (absAmount >= 10000) {
return `${Math.round(amount / 10000).toLocaleString()}만원`;
}
return `${amount.toLocaleString()}`;
}
/**
* 날짜 포맷팅 (API → 한국어 형식)
* @example formatDate("2026-01-20", "월요일") → "2026년 1월 20일 월요일"
*/
function formatDate(dateStr: string, dayOfWeek: string): string {
const date = new Date(dateStr);
return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}${dayOfWeek}`;
}
/**
* 퍼센트 변화율 계산
*/
function calculateChangeRate(current: number, previous: number): number {
if (previous === 0) return current > 0 ? 100 : 0;
return ((current - previous) / previous) * 100;
}
// ============================================
// 1. DailyReport 변환
// ============================================
/**
* 일일 일보 CheckPoints 생성
* 참조: AI 리포트 색상 체계 가이드 - 섹션 2
*/
function generateDailyReportCheckPoints(api: DailyReportApiResponse): CheckPoint[] {
const checkPoints: CheckPoint[] = [];
// 출금 정보
const withdrawal = api.krw_totals.expense;
if (withdrawal > 0) {
checkPoints.push({
id: 'dr-withdrawal',
type: 'info' as CheckPointType,
message: `어제 ${formatAmount(withdrawal)} 출금했습니다.`,
highlights: [
{ text: formatAmount(withdrawal), color: 'red' as const },
],
});
}
// 입금 정보
const deposit = api.krw_totals.income;
if (deposit > 0) {
checkPoints.push({
id: 'dr-deposit',
type: 'success' as CheckPointType,
message: `어제 ${formatAmount(deposit)}이 입금되었습니다.`,
highlights: [
{ text: formatAmount(deposit), color: 'green' as const },
{ text: '입금', color: 'green' as const },
],
});
}
// 현금성 자산 현황
const cashAsset = api.cash_asset_total;
checkPoints.push({
id: 'dr-cash-asset',
type: 'info' as CheckPointType,
message: `총 현금성 자산이 ${formatAmount(cashAsset)}입니다.`,
highlights: [
{ text: formatAmount(cashAsset), color: 'blue' as const },
],
});
return checkPoints;
}
/**
* DailyReport API 응답 → Frontend 타입 변환
*/
export function transformDailyReportResponse(api: DailyReportApiResponse): DailyReportData {
return {
date: formatDate(api.date, api.day_of_week),
cards: [
{
id: 'dr1',
label: '현금성 자산 합계',
amount: api.cash_asset_total,
},
{
id: 'dr2',
label: '외국환(USD) 합계',
amount: api.foreign_currency_total,
currency: 'USD',
},
{
id: 'dr3',
label: '입금 합계',
amount: api.krw_totals.income,
},
{
id: 'dr4',
label: '출금 합계',
amount: api.krw_totals.expense,
},
],
checkPoints: generateDailyReportCheckPoints(api),
};
}
// ============================================
// 2. Receivable 변환
// ============================================
/**
* 미수금 현황 CheckPoints 생성
*/
function generateReceivableCheckPoints(api: ReceivablesApiResponse): CheckPoint[] {
const checkPoints: CheckPoint[] = [];
// 연체 거래처 경고
if (api.overdue_vendor_count > 0) {
checkPoints.push({
id: 'rv-overdue',
type: 'warning' as CheckPointType,
message: `연체 거래처 ${api.overdue_vendor_count}곳. 회수 조치가 필요합니다.`,
highlights: [
{ text: `연체 거래처 ${api.overdue_vendor_count}`, color: 'red' as const },
],
});
}
// 미수금 현황
if (api.total_receivables > 0) {
checkPoints.push({
id: 'rv-total',
type: 'info' as CheckPointType,
message: `총 미수금 ${formatAmount(api.total_receivables)}입니다.`,
highlights: [
{ text: formatAmount(api.total_receivables), color: 'blue' as const },
],
});
}
return checkPoints;
}
/**
* Receivables API 응답 → Frontend 타입 변환
*/
export function transformReceivableResponse(api: ReceivablesApiResponse): ReceivableData {
// 누적 미수금 = 이월 + 매출 - 입금
const cumulativeReceivable = api.total_carry_forward + api.total_sales - api.total_deposits;
return {
cards: [
{
id: 'rv1',
label: '누적 미수금',
amount: cumulativeReceivable,
subItems: [
{ label: '이월', value: api.total_carry_forward },
{ label: '매출', value: api.total_sales },
{ label: '입금', value: api.total_deposits },
],
},
{
id: 'rv2',
label: '당월 미수금',
amount: api.total_receivables,
subItems: [
{ label: '매출', value: api.total_sales },
{ label: '입금', value: api.total_deposits },
],
},
{
id: 'rv3',
label: '거래처 현황',
amount: api.vendor_count,
unit: '곳',
subLabel: `연체 ${api.overdue_vendor_count}`,
},
],
checkPoints: generateReceivableCheckPoints(api),
detailButtonLabel: '미수금 상세',
detailButtonPath: '/accounting/receivables-status',
};
}
// ============================================
// 3. DebtCollection 변환
// ============================================
/**
* 채권추심 CheckPoints 생성
*/
function generateDebtCollectionCheckPoints(api: BadDebtApiResponse): CheckPoint[] {
const checkPoints: CheckPoint[] = [];
// 법적조치 진행 중
if (api.legal_action_amount > 0) {
checkPoints.push({
id: 'dc-legal',
type: 'warning' as CheckPointType,
message: `법적조치 진행 중 ${formatAmount(api.legal_action_amount)}입니다.`,
highlights: [
{ text: formatAmount(api.legal_action_amount), color: 'red' as const },
],
});
}
// 회수 완료
if (api.recovered_amount > 0) {
checkPoints.push({
id: 'dc-recovered',
type: 'success' as CheckPointType,
message: `${formatAmount(api.recovered_amount)}을 회수 완료했습니다.`,
highlights: [
{ text: formatAmount(api.recovered_amount), color: 'green' as const },
{ text: '회수 완료', color: 'green' as const },
],
});
}
return checkPoints;
}
/**
* BadDebt API 응답 → Frontend 타입 변환
*/
export function transformDebtCollectionResponse(api: BadDebtApiResponse): DebtCollectionData {
return {
cards: [
{
id: 'dc1',
label: '누적 악성채권',
amount: api.total_amount,
},
{
id: 'dc2',
label: '추심중',
amount: api.collecting_amount,
},
{
id: 'dc3',
label: '법적조치',
amount: api.legal_action_amount,
},
{
id: 'dc4',
label: '회수완료',
amount: api.recovered_amount,
},
],
checkPoints: generateDebtCollectionCheckPoints(api),
detailButtonPath: '/accounting/bad-debt-collection',
};
}
// ============================================
// 4. MonthlyExpense 변환
// ============================================
/**
* 당월 예상 지출 CheckPoints 생성
*/
function generateMonthlyExpenseCheckPoints(api: ExpectedExpenseApiResponse): CheckPoint[] {
const checkPoints: CheckPoint[] = [];
// 총 예상 지출
checkPoints.push({
id: 'me-total',
type: 'info' as CheckPointType,
message: `이번 달 예상 지출은 ${formatAmount(api.total_amount)}입니다.`,
highlights: [
{ text: formatAmount(api.total_amount), color: 'blue' as const },
],
});
return checkPoints;
}
/**
* ExpectedExpense API 응답 → Frontend 타입 변환
* 주의: 실제 API는 상세 분류(매입/카드/어음 등)를 제공하지 않음
* by_transaction_type에서 추출하거나 기본값 사용
*/
export function transformMonthlyExpenseResponse(api: ExpectedExpenseApiResponse): MonthlyExpenseData {
// transaction_type별 금액 추출
const purchaseTotal = api.by_transaction_type['purchase']?.total ?? 0;
const cardTotal = api.by_transaction_type['card']?.total ?? 0;
const billTotal = api.by_transaction_type['bill']?.total ?? 0;
return {
cards: [
{
id: 'me1',
label: '매입',
amount: purchaseTotal,
},
{
id: 'me2',
label: '카드',
amount: cardTotal,
},
{
id: 'me3',
label: '발행어음',
amount: billTotal,
},
{
id: 'me4',
label: '총 예상 지출 합계',
amount: api.total_amount,
},
],
checkPoints: generateMonthlyExpenseCheckPoints(api),
};
}
// ============================================
// 5. CardManagement 변환
// ============================================
/**
* 카드/가지급금 CheckPoints 생성
*/
function generateCardManagementCheckPoints(api: CardTransactionApiResponse): CheckPoint[] {
const checkPoints: CheckPoint[] = [];
// 전월 대비 변화
const changeRate = calculateChangeRate(api.current_month_total, api.previous_month_total);
if (Math.abs(changeRate) > 10) {
const type: CheckPointType = changeRate > 0 ? 'warning' : 'info';
checkPoints.push({
id: 'cm-change',
type,
message: `당월 카드 사용액이 전월 대비 ${changeRate > 0 ? '+' : ''}${changeRate.toFixed(1)}% 변동했습니다.`,
highlights: [
{ text: `${changeRate > 0 ? '+' : ''}${changeRate.toFixed(1)}%`, color: changeRate > 0 ? 'red' as const : 'green' as const },
],
});
}
// 당월 사용액
checkPoints.push({
id: 'cm-current',
type: 'info' as CheckPointType,
message: `당월 카드 사용 총 ${formatAmount(api.current_month_total)}입니다.`,
highlights: [
{ text: formatAmount(api.current_month_total), color: 'blue' as const },
],
});
return checkPoints;
}
/**
* CardTransaction API 응답 → Frontend 타입 변환
* 주의: 가지급금, 법인세 예상 가중 등은 별도 API 필요 (현재 목업 유지)
*/
export function transformCardManagementResponse(
api: CardTransactionApiResponse,
fallbackData?: CardManagementData
): CardManagementData {
const changeRate = calculateChangeRate(api.current_month_total, api.previous_month_total);
return {
// 가지급금 관련 경고는 API 데이터가 없으므로 fallback 사용
warningBanner: fallbackData?.warningBanner,
cards: [
{
id: 'cm1',
label: '카드',
amount: api.current_month_total,
previousLabel: `전월 대비 ${changeRate > 0 ? '+' : ''}${changeRate.toFixed(1)}%`,
},
// 아래 항목들은 API에서 제공하지 않으므로 fallback 사용
fallbackData?.cards[1] ?? {
id: 'cm2',
label: '가지급금',
amount: 0,
},
fallbackData?.cards[2] ?? {
id: 'cm3',
label: '법인세 예상 가중',
amount: 0,
},
fallbackData?.cards[3] ?? {
id: 'cm4',
label: '대표자 종합세 예상 가중',
amount: 0,
},
],
checkPoints: generateCardManagementCheckPoints(api),
};
}
// ============================================
// 6. StatusBoard 변환
// ============================================
/**
* StatusBoard API 응답 → Frontend 타입 변환
* API 응답 형식이 TodayIssueItem과 거의 동일하므로 단순 매핑
*/
export function transformStatusBoardResponse(api: StatusBoardApiResponse): TodayIssueItem[] {
return api.items.map((item) => ({
id: item.id,
label: item.label,
count: item.count,
path: item.path,
isHighlighted: item.isHighlighted,
}));
}
// ============================================
// 7. TodayIssue 변환
// ============================================
/** 유효한 뱃지 타입 목록 */
const VALID_BADGE_TYPES: TodayIssueListBadgeType[] = [
'수주 성공',
'주식 이슈',
'직정 제고',
'지출예상내역서',
'세금 신고',
'결재 요청',
'기타',
];
/**
* API 뱃지 문자열 → Frontend 뱃지 타입 변환
* 유효하지 않은 뱃지는 '기타'로 폴백
*/
function validateBadgeType(badge: string): TodayIssueListBadgeType {
if (VALID_BADGE_TYPES.includes(badge as TodayIssueListBadgeType)) {
return badge as TodayIssueListBadgeType;
}
return '기타';
}
/**
* TodayIssue API 응답 → Frontend 타입 변환
* 오늘의 이슈 리스트 데이터 변환
*/
export function transformTodayIssueResponse(api: TodayIssueApiResponse): {
items: TodayIssueListItem[];
totalCount: number;
} {
return {
items: api.items.map((item) => ({
id: item.id,
badge: validateBadgeType(item.badge),
content: item.content,
time: item.time,
date: item.date,
needsApproval: item.needsApproval ?? false,
path: item.path,
})),
totalCount: api.total_count,
};
}
// ============================================
// 8. Calendar 변환
// ============================================
/**
* Calendar API 응답 → Frontend 타입 변환
* API 응답 형식이 CalendarScheduleItem과 동일하므로 단순 매핑
*/
export function transformCalendarResponse(api: CalendarApiResponse): {
items: CalendarScheduleItem[];
totalCount: number;
} {
return {
items: api.items.map((item) => ({
id: item.id,
title: item.title,
startDate: item.startDate,
endDate: item.endDate,
startTime: item.startTime,
endTime: item.endTime,
isAllDay: item.isAllDay,
type: item.type,
department: item.department,
personName: item.personName,
color: item.color,
})),
totalCount: api.total_count,
};
}
// ============================================
// 9. Vat 변환
// ============================================
/** 유효한 하이라이트 색상 목록 */
const VALID_HIGHLIGHT_COLORS: HighlightColor[] = ['red', 'green', 'blue'];
/**
* API 색상 문자열 → Frontend 하이라이트 색상 변환
* 유효하지 않은 색상은 'blue'로 폴백
*/
function validateHighlightColor(color: string): HighlightColor {
if (VALID_HIGHLIGHT_COLORS.includes(color as HighlightColor)) {
return color as HighlightColor;
}
return 'blue';
}
/**
* Vat API 응답 → Frontend 타입 변환
* 부가세 현황 데이터 변환
*/
export function transformVatResponse(api: VatApiResponse): VatData {
return {
cards: api.cards.map((card) => ({
id: card.id,
label: card.label,
amount: card.amount,
subLabel: card.subLabel,
unit: card.unit,
})),
checkPoints: api.check_points.map((cp) => ({
id: cp.id,
type: cp.type as CheckPointType,
message: cp.message,
highlights: cp.highlights?.map((h) => ({
text: h.text,
color: validateHighlightColor(h.color),
})),
})),
};
}
// ============================================
// 10. Entertainment 변환
// ============================================
/**
* Entertainment API 응답 → Frontend 타입 변환
* 접대비 현황 데이터 변환
*/
export function transformEntertainmentResponse(api: EntertainmentApiResponse): EntertainmentData {
return {
cards: api.cards.map((card) => ({
id: card.id,
label: card.label,
amount: card.amount,
subLabel: card.subLabel,
unit: card.unit,
})),
checkPoints: api.check_points.map((cp) => ({
id: cp.id,
type: cp.type as CheckPointType,
message: cp.message,
highlights: cp.highlights?.map((h) => ({
text: h.text,
color: validateHighlightColor(h.color),
})),
})),
};
}
// ============================================
// 11. Welfare 변환
// ============================================
/**
* Welfare API 응답 → Frontend 타입 변환
* 복리후생비 현황 데이터 변환
*/
export function transformWelfareResponse(api: WelfareApiResponse): WelfareData {
return {
cards: api.cards.map((card) => ({
id: card.id,
label: card.label,
amount: card.amount,
subLabel: card.subLabel,
unit: card.unit,
})),
checkPoints: api.check_points.map((cp) => ({
id: cp.id,
type: cp.type as CheckPointType,
message: cp.message,
highlights: cp.highlights?.map((h) => ({
text: h.text,
color: validateHighlightColor(h.color),
})),
})),
};
}

View File

@@ -0,0 +1,286 @@
/**
* CEO Dashboard API 응답 타입 정의
*
* Laravel API 응답과 Frontend 타입 간의 매핑을 위한 타입들
*/
// ============================================
// 1. DailyReport API 응답 타입
// ============================================
/** KRW/USD 통화별 합계 */
export interface CurrencyTotals {
carryover: number; // 전월 이월
income: number; // 수입 (입금)
expense: number; // 지출 (출금)
balance: number; // 잔액
}
/** GET /api/proxy/daily-report/summary 응답 */
export interface DailyReportApiResponse {
date: string; // "2026-01-20"
day_of_week: string; // "월요일"
note_receivable_total: number; // 수취채권 합계
foreign_currency_total: number; // 외화 합계 (USD)
cash_asset_total: number; // 현금성 자산 합계
krw_totals: CurrencyTotals; // 원화 합계
usd_totals: CurrencyTotals; // 달러 합계
}
// ============================================
// 2. Receivables API 응답 타입
// ============================================
/** GET /api/proxy/receivables/summary 응답 */
export interface ReceivablesApiResponse {
total_carry_forward: number; // 이월 미수금
total_sales: number; // 당월 매출
total_deposits: number; // 당월 입금
total_bills: number; // 당월 어음
total_receivables: number; // 미수금 잔액
vendor_count: number; // 거래처 수
overdue_vendor_count: number; // 연체 거래처 수
}
// ============================================
// 3. BadDebt (채권추심) API 응답 타입
// ============================================
/** GET /api/proxy/bad-debts/summary 응답 */
export interface BadDebtApiResponse {
total_amount: number; // 총 악성채권
collecting_amount: number; // 추심중
legal_action_amount: number; // 법적조치
recovered_amount: number; // 회수완료
bad_debt_amount: number; // 대손처리
}
// ============================================
// 4. ExpectedExpense (당월 예상 지출) API 응답 타입
// ============================================
/** 상태/유형별 집계 아이템 */
export interface ExpenseSummaryItem {
total: number;
count: number;
}
/** GET /api/proxy/expected-expenses/summary 응답 */
export interface ExpectedExpenseApiResponse {
total_amount: number;
total_count: number;
by_payment_status: Record<string, ExpenseSummaryItem>;
by_transaction_type: Record<string, ExpenseSummaryItem>;
by_month: Record<string, ExpenseSummaryItem>;
}
// ============================================
// 5. CardTransaction (카드/가지급금) API 응답 타입
// ============================================
/** GET /api/proxy/card-transactions/summary 응답 */
export interface CardTransactionApiResponse {
previous_month_total: number; // 전월 카드 사용액
current_month_total: number; // 당월 카드 사용액
total_count: number; // 총 건수
total_amount: number; // 총 금액
}
// ============================================
// 공통 API 응답 Wrapper
// ============================================
/** 표준 API 응답 래퍼 */
export interface ApiResponse<T> {
success: boolean;
message: string;
data: T;
}
// ============================================
// Dashboard Hook 상태 타입
// ============================================
/** 섹션별 로딩 상태 */
export interface DashboardLoadingState {
dailyReport: boolean;
receivable: boolean;
debtCollection: boolean;
monthlyExpense: boolean;
cardManagement: boolean;
}
/** 섹션별 에러 상태 */
export interface DashboardErrorState {
dailyReport: string | null;
receivable: string | null;
debtCollection: string | null;
monthlyExpense: string | null;
cardManagement: string | null;
}
// ============================================
// 6. StatusBoard (현황판) API 응답 타입
// ============================================
/** 현황판 카드 아이템 */
export interface StatusBoardItemApiResponse {
id: string; // 카드 ID (orders, bad_debts, etc.)
label: string; // 카드 라벨
count: number | string; // 건수 또는 텍스트 (예: "부가세 신고 D-15")
path: string; // 이동 경로
isHighlighted: boolean; // 강조 표시 여부
}
/** GET /api/proxy/status-board/summary 응답 */
export interface StatusBoardApiResponse {
items: StatusBoardItemApiResponse[];
}
// ============================================
// 7. TodayIssue (오늘의 이슈 리스트) API 응답 타입
// ============================================
/** 오늘의 이슈 아이템 */
export interface TodayIssueItemApiResponse {
id: string; // 항목 고유 ID
badge: string; // 이슈 카테고리 뱃지
content: string; // 이슈 내용
time: string; // 상대 시간 (예: "10분 전")
date?: string; // 날짜 (ISO 형식)
needsApproval?: boolean; // 승인/반려 버튼 표시 여부
path?: string; // 클릭 시 이동할 경로
}
/** GET /api/proxy/today-issues/summary 응답 */
export interface TodayIssueApiResponse {
items: TodayIssueItemApiResponse[];
total_count: number; // 전체 이슈 건수
}
// ============================================
// 8. Calendar (캘린더) API 응답 타입
// ============================================
/** 캘린더 일정 타입 */
export type CalendarScheduleType = 'schedule' | 'order' | 'construction' | 'other';
/** 캘린더 일정 아이템 */
export interface CalendarScheduleItemApiResponse {
id: string; // 일정 ID (타입_ID 형식, 예: "wo_123")
title: string; // 일정 제목
startDate: string; // 시작일 (Y-m-d)
endDate: string; // 종료일 (Y-m-d)
startTime: string | null; // 시작 시간 (HH:mm) 또는 null
endTime: string | null; // 종료 시간 (HH:mm) 또는 null
isAllDay: boolean; // 종일 여부
type: CalendarScheduleType; // 일정 타입
department: string | null; // 부서명
personName: string | null; // 담당자명
color: string | null; // 일정 색상
}
/** GET /api/proxy/calendar/schedules 응답 */
export interface CalendarApiResponse {
items: CalendarScheduleItemApiResponse[];
total_count: number; // 총 일정 수
}
// ============================================
// 9. Vat (부가세) API 응답 타입
// ============================================
/** 부가세 금액 카드 아이템 */
export interface VatAmountCardApiResponse {
id: string; // 카드 ID (vat_sales_tax, vat_purchases_tax, etc.)
label: string; // 카드 라벨
amount: number; // 금액 또는 건수
subLabel?: string; // 부가 라벨 (예: "환급")
unit?: string; // 단위 (예: "건")
}
/** 부가세 체크포인트 하이라이트 아이템 */
export interface VatHighlightItemApiResponse {
text: string; // 하이라이트 텍스트
color: string; // 색상 (red, blue, green 등)
}
/** 부가세 체크포인트 아이템 */
export interface VatCheckPointApiResponse {
id: string; // 체크포인트 ID
type: string; // 타입 (success, warning, error)
message: string; // 메시지
highlights?: VatHighlightItemApiResponse[]; // 하이라이트 아이템 목록
}
/** GET /api/proxy/vat/summary 응답 */
export interface VatApiResponse {
cards: VatAmountCardApiResponse[];
check_points: VatCheckPointApiResponse[];
}
// ============================================
// 10. Entertainment (접대비) API 응답 타입
// ============================================
/** 접대비 금액 카드 아이템 */
export interface EntertainmentAmountCardApiResponse {
id: string; // 카드 ID (et_sales, et_limit, etc.)
label: string; // 카드 라벨
amount: number; // 금액
subLabel?: string; // 부가 라벨
unit?: string; // 단위
}
/** 접대비 체크포인트 하이라이트 아이템 */
export interface EntertainmentHighlightItemApiResponse {
text: string; // 하이라이트 텍스트
color: string; // 색상 (red, green, orange 등)
}
/** 접대비 체크포인트 아이템 */
export interface EntertainmentCheckPointApiResponse {
id: string; // 체크포인트 ID
type: string; // 타입 (success, warning, error)
message: string; // 메시지
highlights?: EntertainmentHighlightItemApiResponse[]; // 하이라이트 아이템 목록
}
/** GET /api/proxy/entertainment/summary 응답 */
export interface EntertainmentApiResponse {
cards: EntertainmentAmountCardApiResponse[];
check_points: EntertainmentCheckPointApiResponse[];
}
// ============================================
// 11. Welfare (복리후생비) API 응답 타입
// ============================================
/** 복리후생비 금액 카드 아이템 */
export interface WelfareAmountCardApiResponse {
id: string; // 카드 ID (wf_annual_limit, wf_period_limit, etc.)
label: string; // 카드 라벨
amount: number; // 금액
subLabel?: string; // 부가 라벨
unit?: string; // 단위
}
/** 복리후생비 체크포인트 하이라이트 아이템 */
export interface WelfareHighlightItemApiResponse {
text: string; // 하이라이트 텍스트
color: string; // 색상 (red, green, orange 등)
}
/** 복리후생비 체크포인트 아이템 */
export interface WelfareCheckPointApiResponse {
id: string; // 체크포인트 ID
type: string; // 타입 (success, warning, error)
message: string; // 메시지
highlights?: WelfareHighlightItemApiResponse[]; // 하이라이트 아이템 목록
}
/** GET /api/proxy/welfare/summary 응답 */
export interface WelfareApiResponse {
cards: WelfareAmountCardApiResponse[];
check_points: WelfareCheckPointApiResponse[];
}