refactor: CEO 대시보드 mockData/modalConfigs 정리 및 BillManagement 간소화

- mockData 불필요 데이터 대폭 제거
- modalConfigs (cardManagement, entertainment, monthlyExpense, vat, welfare) 정리
- CEODashboard 컴포넌트 개선
- BillManagementClient 간소화
This commit is contained in:
유병철
2026-03-05 21:22:17 +09:00
parent 1675f3edcf
commit bec933b3b4
8 changed files with 77 additions and 1588 deletions

View File

@@ -10,7 +10,7 @@
* - tableHeaderActions: 거래처, 구분, 상태 필터
*/
import { useState, useMemo, useCallback } from 'react';
import { useState, useMemo, useCallback, useEffect, useRef } from 'react';
import { useRouter } from 'next/navigation';
import { formatNumber } from '@/lib/utils/amount';
import { useDateRange } from '@/hooks';
@@ -32,8 +32,6 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Label } from '@/components/ui/label';
import {
UniversalListPage,
type UniversalListConfig,
@@ -148,6 +146,16 @@ export function BillManagementClient({
}
}, [searchQuery, billTypeFilter, statusFilter, vendorFilter, startDate, endDate, sortOption, itemsPerPage]);
// ===== 필터 변경 시 자동 재조회 =====
const isInitialMount = useRef(true);
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
return;
}
loadData(1);
}, [loadData]);
// ===== 체크박스 핸들러 =====
const toggleSelection = useCallback((id: string) => {
setSelectedItems(prev => {
@@ -348,32 +356,8 @@ export function BillManagementClient({
);
},
// 모바일 필터 설정
filterConfig: [
{
key: 'vendorFilter',
label: '거래처',
type: 'single',
options: vendorOptions.filter(o => o.value !== 'all'),
},
{
key: 'billType',
label: '구분',
type: 'single',
options: BILL_TYPE_FILTER_OPTIONS.filter(o => o.value !== 'all'),
},
{
key: 'status',
label: '상태',
type: 'single',
options: BILL_STATUS_FILTER_OPTIONS.filter(o => o.value !== 'all'),
},
],
initialFilters: {
vendorFilter: vendorFilter,
billType: billTypeFilter,
status: statusFilter,
},
// 모바일 필터 설정 (tableHeaderActions와 중복 방지를 위해 비워둠)
filterConfig: [],
filterTitle: '어음 필터',
// 날짜 선택기
@@ -392,44 +376,12 @@ export function BillManagementClient({
icon: Plus,
},
// 헤더 액션: 수취/발행 라디오 + 상태 선택 + 저장
// 모바일: 라디오/상태필터는 숨기고 저장만 표시 (filterConfig 바텀시트와 중복 방지)
// 데스크톱: 모두 표시
// 헤더 액션: 저장 버튼만 (필터는 tableHeaderActions에서 통합 관리)
headerActions: () => (
<div className="flex items-center gap-3" style={{ display: 'flex' }}>
<div className="hidden xl:flex items-center gap-3">
<RadioGroup
value={billTypeFilter}
onValueChange={(value) => { setBillTypeFilter(value); loadData(1); }}
className="flex items-center gap-3"
>
<div className="flex items-center space-x-1">
<RadioGroupItem value="received" id="received" />
<Label htmlFor="received" className="cursor-pointer text-sm whitespace-nowrap"></Label>
</div>
<div className="flex items-center space-x-1">
<RadioGroupItem value="issued" id="issued" />
<Label htmlFor="issued" className="cursor-pointer text-sm whitespace-nowrap"></Label>
</div>
</RadioGroup>
<Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="min-w-[100px] w-auto">
<SelectValue placeholder="상태" />
</SelectTrigger>
<SelectContent>
{BILL_STATUS_FILTER_OPTIONS.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<Button onClick={handleSave} size="sm" disabled={isLoading}>
<Save className="h-4 w-4 mr-1" />
</Button>
</div>
<Button onClick={handleSave} size="sm" disabled={isLoading}>
<Save className="h-4 w-4 mr-1" />
</Button>
),
// 테이블 헤더 액션 (필터)
@@ -448,7 +400,7 @@ export function BillManagementClient({
</SelectContent>
</Select>
<Select value={billTypeFilter} onValueChange={(value) => { setBillTypeFilter(value); loadData(1); }}>
<Select value={billTypeFilter} onValueChange={setBillTypeFilter}>
<SelectTrigger className="min-w-[100px] w-auto">
<SelectValue placeholder="구분" />
</SelectTrigger>
@@ -461,7 +413,7 @@ export function BillManagementClient({
</SelectContent>
</Select>
<Select value={statusFilter} onValueChange={(value) => { setStatusFilter(value); loadData(1); }}>
<Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="min-w-[110px] w-auto">
<SelectValue placeholder="보관중" />
</SelectTrigger>

View File

@@ -38,15 +38,9 @@ import { SummaryNavBar } from './SummaryNavBar';
import { useSectionSummary } from './useSectionSummary';
import { useCEODashboard, useTodayIssue, useCalendar, useVat, useEntertainment, useEntertainmentDetail, useWelfare, useWelfareDetail, useVatDetail, useMonthlyExpenseDetail, type MonthlyExpenseCardId } from '@/hooks/useCEODashboard';
import { useCardManagementModals } from '@/hooks/useCardManagementModals';
import {
getMonthlyExpenseModalConfig,
getCardManagementModalConfig,
getCardManagementModalConfigWithData,
getEntertainmentModalConfig,
getWelfareModalConfig,
getVatModalConfig,
} from './modalConfigs';
import { getCardManagementModalConfigWithData } from './modalConfigs';
import { transformEntertainmentDetailResponse, transformWelfareDetailResponse, transformVatDetailResponse } from '@/lib/api/dashboard/transformers';
import { toast } from 'sonner';
export function CEODashboard() {
const router = useRouter();
@@ -324,49 +318,37 @@ export function CEODashboard() {
setCurrentModalCardId('cm2');
setDetailModalConfig(config);
setIsDetailModalOpen(true);
} else {
toast.error('데이터를 불러올 수 없습니다');
}
} catch {
// API 실패 시 fallback mock 데이터 사용
const config = getCardManagementModalConfig('cm2');
if (config) {
setCurrentModalCardId('cm2');
setDetailModalConfig(config);
setIsDetailModalOpen(true);
}
toast.error('데이터를 불러올 수 없습니다');
}
}, [cardManagementModals]);
// 접대비 현황 카드 클릭 - API 데이터로 모달 열기 (fallback: 정적 config)
// 접대비 현황 카드 클릭 - API 데이터로 모달 열기
const handleEntertainmentCardClick = useCallback(async (cardId: string) => {
// et_sales 카드는 별도 정적 config 사용 (매출 상세)
if (cardId === 'et_sales') {
const config = getEntertainmentModalConfig(cardId);
if (config) {
setDetailModalConfig(config);
setIsDetailModalOpen(true);
}
return;
}
// 리스크 카드 → API에서 상세 데이터 fetch, 반환값 직접 사용
setCurrentModalCardId('entertainment_detail');
const apiConfig = await entertainmentDetailData.refetch();
const config = apiConfig ?? getEntertainmentModalConfig(cardId);
if (config) {
setDetailModalConfig(config);
if (apiConfig) {
setDetailModalConfig(apiConfig);
setIsDetailModalOpen(true);
} else {
toast.error('데이터를 불러올 수 없습니다');
}
}, [entertainmentDetailData]);
// 복리후생비 현황 카드 클릭 (모든 카드가 동일한 상세 모달)
// 복리후생비 클릭 - API 데이터로 모달 열기 (fallback: 정적 config)
const handleWelfareCardClick = useCallback(async () => {
const apiConfig = await welfareDetailData.refetch();
const config = apiConfig ?? getWelfareModalConfig(dashboardSettings.welfare.calculationType);
setDetailModalConfig(config);
setCurrentModalCardId('welfare_detail');
setIsDetailModalOpen(true);
}, [welfareDetailData, dashboardSettings.welfare.calculationType]);
if (apiConfig) {
setDetailModalConfig(apiConfig);
setCurrentModalCardId('welfare_detail');
setIsDetailModalOpen(true);
} else {
toast.error('데이터를 불러올 수 없습니다');
}
}, [welfareDetailData]);
// 신고기간 변경 시 API 재호출
const handlePeriodChange = useCallback(async (periodValue: string) => {
@@ -394,17 +376,19 @@ export function CEODashboard() {
}
}, []);
// 부가세 클릭 (모든 카드가 동일한 상세 모달) - API 데이터로 열기 (fallback: 정적 config)
// 부가세 클릭 (모든 카드가 동일한 상세 모달) - API 데이터로 열기
const handleVatClick = useCallback(async () => {
setCurrentModalCardId('vat_detail');
const apiConfig = await vatDetailData.refetch();
const config = apiConfig ?? getVatModalConfig();
// onPeriodChange 콜백 주입
if (config.periodSelect) {
config.periodSelect.onPeriodChange = handlePeriodChange;
if (apiConfig) {
if (apiConfig.periodSelect) {
apiConfig.periodSelect.onPeriodChange = handlePeriodChange;
}
setDetailModalConfig(apiConfig);
setIsDetailModalOpen(true);
} else {
toast.error('데이터를 불러올 수 없습니다');
}
setDetailModalConfig(config);
setIsDetailModalOpen(true);
}, [vatDetailData, handlePeriodChange]);
// 캘린더 일정 클릭 (기존 일정 수정)

View File

@@ -1,487 +1,10 @@
import type { CEODashboardData } from './types';
/* ============================================
* 전체 Mock 데이터 주석 처리
* API 연동 완료 후 이 파일 삭제 예정
* 기존 mock 데이터는 git history에서 확인 가능
* ============================================ */
// 빈 기본값 (타입 안전성 유지 — 필수 필드만)
/**
* Mock 데이터 제거 완료 — 빈 기본값만 유지
* dev 페이지에서 import하므로 파일 유지
*/
export const mockData: CEODashboardData = {
todayIssue: [],
todayIssueList: [],
};
/* ============================================
* 아래는 주석 처리된 기존 Mock 데이터
* ============================================
import type {
SalesStatusData,
PurchaseStatusData,
DailyProductionData,
UnshippedData,
DailyAttendanceData,
} from './types';
const _originalMockData: CEODashboardData = {
todayIssue: [],
todayIssueList: [],
dailyReport: {
date: '2026년 1월 5일 월요일',
cards: [
{ id: 'dr1', label: '일일일보', amount: 3050000000, path: '/ko/accounting/daily-report' },
{ id: 'dr2', label: '미수금 잔액', amount: 3050000000, path: '/ko/accounting/receivables-status' },
{ id: 'dr3', label: '미지급금 잔액', amount: 3050000000 },
{ id: 'dr4', label: '당월 예상 지출 합계', amount: 350000000 },
],
checkPoints: [
{
id: 'dr-cp1',
type: 'success',
message: '어제 3.5억원 출금했습니다. 최근 7일 평균 대비 2배 이상으로 점검이 필요합니다.',
highlights: [
{ text: '3.5억원 출금', color: 'red' },
{ text: '점검이 필요', color: 'red' },
],
},
{
id: 'dr-cp2',
type: 'success',
message: '어제 10.2억원이 입금되었습니다. 대한건설 선수금 입금이 주요 원인입니다.',
highlights: [
{ text: '10.2억원', color: 'green' },
{ text: '입금', color: 'green' },
{ text: '대한건설 선수금 입금', color: 'green' },
],
},
{
id: 'dr-cp3',
type: 'success',
message: '총 현금성 자산이 300.2억원입니다. 월 운영비용 대비 18개월분이 확보되어 안정적입니다.',
highlights: [
{ text: '18개월분', color: 'blue' },
{ text: '안정적', color: 'blue' },
],
},
],
},
monthlyExpense: {
cards: [
{ id: 'me1', label: '매입', amount: 3050000000, previousLabel: '전월 대비 +10.5%' },
{ id: 'me2', label: '카드', amount: 30123000, previousLabel: '전월 대비 +10.5%' },
{ id: 'me3', label: '발행어음', amount: 30123000, previousLabel: '전월 대비 +10.5%' },
{ id: 'me4', label: '총 예상 지출 합계', amount: 350000000, previousLabel: '전월 대비 +10.5%' },
],
checkPoints: [
{
id: 'me-cp1',
type: 'success',
message: '이번 달 예상 지출이 전월 대비 15% 증가했습니다. 매입 비용 증가가 주요 원인입니다.',
highlights: [
{ text: '전월 대비 15% 증가', color: 'red' },
],
},
{
id: 'me-cp2',
type: 'success',
message: '이번 달 예상 지출이 예산을 12% 초과했습니다. 비용 항목별 점검이 필요합니다.',
highlights: [
{ text: '예산을 12% 초과', color: 'red' },
],
},
{
id: 'me-cp3',
type: 'success',
message: '이번 달 예상 지출이 전월 대비 8% 감소했습니다. {계정과목명} 비용이 줄었습니다.',
highlights: [
{ text: '전월 대비 8% 감소', color: 'green' },
],
},
],
},
entertainment: {
cards: [
{ id: 'et1', label: '주말/심야', amount: 3123000, previousLabel: '미증빙 5건' },
{ id: 'et2', label: '기피업종 (유흥, 귀금속 등)', amount: 3123000, previousLabel: '불인정 5건' },
{ id: 'et3', label: '고액 결제', amount: 3123000, previousLabel: '미증빙 5건' },
{ id: 'et4', label: '증빙 미비', amount: 3123000, previousLabel: '미증빙 5건' },
],
checkPoints: [
{
id: 'et-cp1',
type: 'success',
message: '{1사분기} 접대비 사용 1,000만원 / 한도 4,012만원 (75%). 여유 있게 운영 중입니다.',
highlights: [
{ text: '1,000만원', color: 'green' },
{ text: '4,012만원 (75%)', color: 'green' },
],
},
{
id: 'et-cp2',
type: 'success',
message: '접대비 한도 85% 도달. 잔여 한도 600만원입니다. 사용 계획을 점검해 주세요.',
highlights: [
{ text: '잔여 한도 600만원', color: 'red' },
],
},
{
id: 'et-cp3',
type: 'error',
message: '접대비 한도 초과 320만원 발생. 초과분은 손금불산입되어 법인세 부담이 증가합니다.',
highlights: [
{ text: '320만원 발생', color: 'red' },
],
},
{
id: 'et-cp4',
type: 'error',
message: '접대비 사용 중 3건(45만원)의 거래처 정보가 누락되었습니다. 기록을 보완해 주세요.',
highlights: [
{ text: '3건(45만원)', color: 'red' },
{ text: '거래처 정보가 누락', color: 'red' },
],
},
],
},
welfare: {
cards: [
{ id: 'wf1', label: '비과세 한도 초과', amount: 3123000, previousLabel: '5건' },
{ id: 'wf2', label: '사적 사용 의심', amount: 3123000, previousLabel: '5건' },
{ id: 'wf3', label: '특정인 편중', amount: 3123000, previousLabel: '5건' },
{ id: 'wf4', label: '항목별 한도 초과', amount: 3123000, previousLabel: '5건' },
],
checkPoints: [
{
id: 'wf-cp1',
type: 'success',
message: '1인당 월 복리후생비 20만원. 업계 평균(15~25만원) 내 정상 운영 중입니다.',
highlights: [
{ text: '1인당 월 복리후생비 20만원', color: 'green' },
],
},
{
id: 'wf-cp2',
type: 'error',
message: '식대가 월 25만원으로 비과세 한도(20만원)를 초과했습니다. 초과분은 근로소득 과세됩니다.',
highlights: [
{ text: '식대가 월 25만원으로', color: 'red' },
{ text: '초과', color: 'red' },
],
},
],
},
receivable: {
cards: [
{
id: 'rv1',
label: '누적 미수금',
amount: 30123000,
subItems: [
{ label: '매출', value: 60123000 },
{ label: '입금', value: 30000000 },
],
},
{
id: 'rv2',
label: '당월 미수금',
amount: 10123000,
},
{
id: 'rv3',
label: '미수금 거래처',
amount: 31,
unit: '건',
subItems: [
{ label: '연체', value: '21건' },
{ label: '악성채권', value: '11건' },
],
},
{
id: 'rv4',
label: '미수금 Top 3',
amount: 0,
displayValue: '상세보기',
},
],
checkPoints: [
{
id: 'rv-cp1',
type: 'success',
message: '90일 이상 장기 미수금 3건(2,500만원) 발생. 회수 조치가 필요합니다.',
highlights: [
{ text: '90일 이상 장기 미수금 3건(2,500만원) 발생', color: 'red' },
],
},
{
id: 'rv-cp2',
type: 'success',
message: '(주)대한전자 미수금 1,500만원으로 전체의 35%를 차지합니다. 리스크 분산이 필요합니다.',
highlights: [
{ text: '(주)대한전자 미수금 1,500만원으로 전체의 35%를', color: 'red' },
],
},
],
detailButtonPath: '/ko/accounting/receivables-status',
},
debtCollection: {
cards: [
{ id: 'dc1', label: '누적 악성채권', amount: 350000000, subLabel: '25건' },
{ id: 'dc2', label: '추심중', amount: 30123000, subLabel: '12건' },
{ id: 'dc3', label: '법적조치', amount: 3123000, subLabel: '3건' },
{ id: 'dc4', label: '추심종료', amount: 280000000, subLabel: '10건' },
],
checkPoints: [
{
id: 'dc-cp1',
type: 'success',
message: '(주)대한전자 건 지급명령 신청 완료. 법원 결정까지 약 2주 소요 예정입니다.',
highlights: [{ text: '(주)대한전자 건 지급명령 신청 완료.', color: 'red' }],
},
{
id: 'dc-cp2',
type: 'success',
message: '(주)삼성테크 건 회수 불가 판정. 대손 처리 검토가 필요합니다.',
highlights: [{ text: '(주)삼성테크 건 회수 불가 판정.', color: 'red' }],
},
],
detailButtonPath: '/ko/accounting/bad-debt-collection',
},
vat: {
cards: [
{ id: 'vat1', label: '매출세액', amount: 3050000000 },
{ id: 'vat2', label: '매입세액', amount: 2050000000 },
{ id: 'vat3', label: '예상 납부세액', amount: 110000000 },
{ id: 'vat4', label: '세금계산서 미발행', amount: 3, unit: '건' },
],
checkPoints: [
{
id: 'vat-cp1',
type: 'success',
message: '2026년 1기 예정신고 기준, 예상 환급세액은 5,200,000원입니다. 설비투자에 따른 매입세액 증가가 주요 원인입니다.',
highlights: [{ text: '2026년 1기 예정신고 기준, 예상 환급세액은 5,200,000원입니다.', color: 'red' }],
},
{
id: 'vat-cp2',
type: 'success',
message: '2026년 1기 예정신고 기준, 예상 납부세액은 110,100,000원입니다. 전기 대비 12.9% 증가했으며, 이는 매출 증가에 따른 정상적인 증가로 판단됩니다.',
highlights: [{ text: '2026년 1기 예정신고 기준, 예상 납부세액은 110,100,000원입니다.', color: 'red' }],
},
],
},
// ===== 신규 섹션 Mock 데이터 =====
salesStatus: {
cumulativeSales: 312300000,
achievementRate: 94.5,
yoyChange: 12.5,
monthlySales: 312300000,
monthlyTrend: [
{ month: '8월', amount: 250000000 },
{ month: '9월', amount: 280000000 },
{ month: '10월', amount: 310000000 },
{ month: '11월', amount: 290000000 },
{ month: '12월', amount: 320000000 },
{ month: '1월', amount: 300000000 },
{ month: '2월', amount: 312300000 },
],
clientSales: [
{ name: '대한건설', amount: 95000000 },
{ name: '삼성테크', amount: 78000000 },
{ name: '현대산업', amount: 62000000 },
{ name: 'LG전자', amount: 45000000 },
{ name: '기타', amount: 32300000 },
],
dailyItems: [
{ date: '2026-02-01', client: '대한건설', item: '스크린 외', amount: 25000000, status: '입금완료' },
{ date: '2026-02-03', client: '삼성테크', item: '슬루 외', amount: 18000000, status: '미입금' },
{ date: '2026-02-05', client: '현대산업', item: '절곡 외', amount: 32000000, status: '입금완료' },
{ date: '2026-02-07', client: 'LG전자', item: '스크린', amount: 15000000, status: '부분입금' },
{ date: '2026-02-10', client: '대한건설', item: '슬루', amount: 28000000, status: '입금완료' },
{ date: '2026-02-12', client: '삼성테크', item: '절곡', amount: 22000000, status: '미입금' },
{ date: '2026-02-15', client: '현대산업', item: '스크린 외', amount: 35000000, status: '입금완료' },
],
dailyTotal: 312300000,
},
purchaseStatus: {
cumulativePurchase: 312300000,
unpaidAmount: 312300000,
yoyChange: -12.5,
monthlyTrend: [
{ month: '8월', amount: 180000000 },
{ month: '9월', amount: 200000000 },
{ month: '10월', amount: 220000000 },
{ month: '11월', amount: 195000000 },
{ month: '12월', amount: 230000000 },
{ month: '1월', amount: 210000000 },
{ month: '2월', amount: 312300000 },
],
materialRatio: [
{ name: '원자재', value: 55, percentage: 55, color: '#3b82f6' },
{ name: '부자재', value: 35, percentage: 35, color: '#10b981' },
{ name: '소모품', value: 10, percentage: 10, color: '#f59e0b' },
],
dailyItems: [
{ date: '2026-02-01', supplier: '한국철강', item: '철판 외', amount: 45000000, status: '결제완료' },
{ date: '2026-02-03', supplier: '삼성소재', item: '알루미늄', amount: 28000000, status: '미결제' },
{ date: '2026-02-05', supplier: '현대자재', item: '볼트/너트', amount: 12000000, status: '결제완료' },
{ date: '2026-02-08', supplier: 'LG화학', item: '도료 외', amount: 18000000, status: '부분결제' },
{ date: '2026-02-10', supplier: '한국철강', item: '스테인리스', amount: 52000000, status: '미결제' },
{ date: '2026-02-13', supplier: '삼성소재', item: '구리판', amount: 35000000, status: '결제완료' },
],
dailyTotal: 312300000,
},
dailyProduction: {
date: '2026년 2월 23일 월요일',
processes: [
{
processName: '스크린',
totalWork: 10,
todo: 10,
inProgress: 10,
completed: 10,
urgent: 3,
subLine: 2,
regular: 5,
workerCount: 8,
workItems: [
{ id: 'sp1', orderNo: 'SO-2026-001', client: '대한건설', product: '스크린 A형', quantity: 50, status: '진행중' },
{ id: 'sp2', orderNo: 'SO-2026-002', client: '삼성테크', product: '스크린 B형', quantity: 30, status: '진행중' },
{ id: 'sp3', orderNo: 'SO-2026-003', client: '현대산업', product: '스크린 C형', quantity: 20, status: '대기' },
{ id: 'sp4', orderNo: 'SO-2026-004', client: 'LG전자', product: '스크린 D형', quantity: 40, status: '대기' },
{ id: 'sp5', orderNo: 'SO-2026-005', client: '대한건설', product: '스크린 E형', quantity: 25, status: '완료' },
],
workers: [
{ name: '김철수', assigned: 5, completed: 3, rate: 60 },
{ name: '이영희', assigned: 4, completed: 4, rate: 100 },
{ name: '박민수', assigned: 3, completed: 2, rate: 67 },
{ name: '정수진', assigned: 3, completed: 1, rate: 33 },
],
},
{
processName: '슬랫',
totalWork: 10,
todo: 10,
inProgress: 10,
completed: 10,
urgent: 2,
subLine: 3,
regular: 5,
workerCount: 6,
workItems: [
{ id: 'sl1', orderNo: 'SO-2026-010', client: '대한건설', product: '슬루 A형', quantity: 40, status: '진행중' },
{ id: 'sl2', orderNo: 'SO-2026-011', client: '삼성테크', product: '슬루 B형', quantity: 25, status: '진행중' },
{ id: 'sl3', orderNo: 'SO-2026-012', client: '현대산업', product: '슬루 C형', quantity: 35, status: '대기' },
],
workers: [
{ name: '최동훈', assigned: 4, completed: 3, rate: 75 },
{ name: '강미영', assigned: 3, completed: 2, rate: 67 },
{ name: '윤상호', assigned: 3, completed: 3, rate: 100 },
],
},
{
processName: '절곡',
totalWork: 10,
todo: 10,
inProgress: 10,
completed: 10,
urgent: 1,
subLine: 2,
regular: 7,
workerCount: 5,
workItems: [
{ id: 'jg1', orderNo: 'SO-2026-020', client: '현대산업', product: '절곡 A형', quantity: 60, status: '진행중' },
{ id: 'jg2', orderNo: 'SO-2026-021', client: 'LG전자', product: '절곡 B형', quantity: 45, status: '대기' },
{ id: 'jg3', orderNo: 'SO-2026-022', client: '대한건설', product: '절곡 C형', quantity: 30, status: '완료' },
],
workers: [
{ name: '한지원', assigned: 4, completed: 4, rate: 100 },
{ name: '서준혁', assigned: 3, completed: 2, rate: 67 },
],
},
],
shipment: {
expectedAmount: 150000000,
expectedCount: 12,
actualAmount: 120000000,
actualCount: 9,
},
},
unshipped: {
items: [
{ id: 'us1', portNo: 'P-2026-001', siteName: '강남 현장', orderClient: '대한건설', dueDate: '2026-02-25', daysLeft: 2 },
{ id: 'us2', portNo: 'P-2026-002', siteName: '서초 현장', orderClient: '삼성테크', dueDate: '2026-02-26', daysLeft: 3 },
{ id: 'us3', portNo: 'P-2026-003', siteName: '판교 현장', orderClient: '현대산업', dueDate: '2026-02-27', daysLeft: 4 },
{ id: 'us4', portNo: 'P-2026-004', siteName: '송도 현장', orderClient: 'LG전자', dueDate: '2026-02-28', daysLeft: 5 },
{ id: 'us5', portNo: 'P-2026-005', siteName: '마포 현장', orderClient: '대한건설', dueDate: '2026-03-01', daysLeft: 6 },
{ id: 'us6', portNo: 'P-2026-006', siteName: '영등포 현장', orderClient: '삼성테크', dueDate: '2026-03-03', daysLeft: 8 },
{ id: 'us7', portNo: 'P-2026-007', siteName: '용산 현장', orderClient: '현대산업', dueDate: '2026-03-05', daysLeft: 10 },
],
},
constructionData: {
thisMonth: 15,
completed: 15,
items: [
{ id: 'cs1', siteName: '강남 현장', client: '대한건설', startDate: '2026-02-01', endDate: '2026-02-28', progress: 85, status: '진행중' },
{ id: 'cs2', siteName: '서초 현장', client: '삼성테크', startDate: '2026-02-05', endDate: '2026-03-05', progress: 60, status: '진행중' },
{ id: 'cs3', siteName: '판교 현장', client: '현대산업', startDate: '2026-02-10', endDate: '2026-03-10', progress: 40, status: '진행중' },
{ id: 'cs4', siteName: '송도 현장', client: 'LG전자', startDate: '2026-03-01', endDate: '2026-03-30', progress: 0, status: '예정' },
{ id: 'cs5', siteName: '마포 현장', client: '대한건설', startDate: '2026-01-15', endDate: '2026-02-15', progress: 100, status: '완료' },
],
},
dailyAttendance: {
present: 10,
onLeave: 10,
late: 10,
absent: 10,
employees: [
{ id: 'att1', department: '생산부', position: '과장', name: '김철수', status: '출근' },
{ id: 'att2', department: '영업부', position: '대리', name: '이영희', status: '출근' },
{ id: 'att3', department: '관리부', position: '사원', name: '박민수', status: '휴가' },
{ id: 'att4', department: '생산부', position: '부장', name: '정수진', status: '지각' },
{ id: 'att5', department: '영업부', position: '과장', name: '최동훈', status: '출근' },
{ id: 'att6', department: '관리부', position: '대리', name: '강미영', status: '결근' },
{ id: 'att7', department: '생산부', position: '사원', name: '윤상호', status: '출근' },
],
},
calendarSchedules: [
{
id: 'sch1',
title: '제목',
startDate: '2026-01-01',
endDate: '2026-01-04',
startTime: '09:00',
endTime: '12:00',
type: 'schedule',
department: '부서명',
},
{
id: 'sch2',
title: '제목',
startDate: '2026-01-06',
endDate: '2026-01-06',
type: 'schedule',
personName: '홍길동',
},
{
id: 'sch3',
title: '제목',
startDate: '2026-01-06',
endDate: '2026-01-06',
startTime: '09:00',
endTime: '12:00',
type: 'order',
department: '부서명',
},
{
id: 'sch4',
title: '제목',
startDate: '2026-01-06',
endDate: '2026-01-06',
startTime: '12:35',
type: 'construction',
personName: '홍길동',
},
],
};
============================================ */

View File

@@ -32,7 +32,7 @@ export interface CardManagementModalData {
/**
* API 데이터를 사용하여 모달 설정을 동적으로 생성
* 데이터가 없는 경우 fallback 설정 사용
* 데이터가 없는 경우 null 반환 (mock fallback 제거)
*/
export function getCardManagementModalConfigWithData(
cardId: string,
@@ -40,297 +40,26 @@ export function getCardManagementModalConfigWithData(
): DetailModalConfig | null {
switch (cardId) {
case 'cm1':
if (data.cm1Data) {
return transformCm1ModalConfig(data.cm1Data);
}
return getCardManagementModalConfig(cardId);
return data.cm1Data ? transformCm1ModalConfig(data.cm1Data) : null;
case 'cm2':
if (data.cm2Data) {
return transformCm2ModalConfig(data.cm2Data);
}
return getCardManagementModalConfig(cardId);
return data.cm2Data ? transformCm2ModalConfig(data.cm2Data) : null;
case 'cm3':
if (data.cm3Data) {
return transformCm3ModalConfig(data.cm3Data);
}
return getCardManagementModalConfig(cardId);
return data.cm3Data ? transformCm3ModalConfig(data.cm3Data) : null;
case 'cm4':
if (data.cm4Data) {
return transformCm4ModalConfig(data.cm4Data);
}
return getCardManagementModalConfig(cardId);
return data.cm4Data ? transformCm4ModalConfig(data.cm4Data) : null;
default:
return null;
}
}
// ============================================
// Fallback 모달 설정 (API 데이터 없을 때 사용)
// ============================================
/**
* Fallback: 정적 목업 데이터 기반 모달 설정
* API 데이터가 없을 때 사용
* Fallback 모달 설정 (mock 제거 완료 — null 반환)
* API 데이터가 없을 때 모달을 열지 않음
*/
export function getCardManagementModalConfig(cardId: string): DetailModalConfig | null {
const configs: Record<string, DetailModalConfig> = {
cm1: {
title: '카드 사용 상세',
summaryCards: [
{ label: '당월 카드 사용', value: 30123000, unit: '원' },
{ label: '전월 대비', value: '+10.5%', isComparison: true, isPositive: true },
{ label: '미정리 건수', value: '5건' },
],
barChart: {
title: '월별 카드 사용 추이',
data: [
{ name: '7월', value: 28000000 },
{ name: '8월', value: 32000000 },
{ name: '9월', value: 27000000 },
{ name: '10월', value: 35000000 },
{ name: '11월', value: 29000000 },
{ name: '12월', value: 30123000 },
],
dataKey: 'value',
xAxisKey: 'name',
color: '#60A5FA',
},
pieChart: {
title: '사용자별 카드 사용 비율',
data: [
{ name: '대표이사', value: 15000000, percentage: 50, color: '#60A5FA' },
{ name: '경영지원팀', value: 9000000, percentage: 30, color: '#34D399' },
{ name: '영업팀', value: 6123000, percentage: 20, color: '#FBBF24' },
],
},
table: {
title: '카드 사용 내역',
columns: [
{ key: 'no', label: 'No.', align: 'center' },
{ key: 'cardName', label: '카드명', align: 'left' },
{ key: 'user', label: '사용자', align: 'center' },
{ key: 'date', label: '사용일시', align: 'center', format: 'date' },
{ key: 'store', label: '가맹점명', align: 'left' },
{ key: 'amount', label: '사용금액', align: 'right', format: 'currency' },
{ key: 'usageType', label: '사용유형', align: 'center', highlightValue: '미설정' },
],
data: [
{ cardName: '법인카드1', user: '대표이사', date: '2026-01-05 18:30', store: '스타벅스 강남점', amount: 45000, usageType: '복리후생비' },
{ cardName: '법인카드1', user: '대표이사', date: '2026-01-04 12:15', store: '한식당', amount: 350000, usageType: '접대비' },
{ cardName: '법인카드2', user: '경영지원팀', date: '2026-01-03 14:20', store: '오피스디포', amount: 125000, usageType: '소모품비' },
{ cardName: '법인카드1', user: '대표이사', date: '2026-01-02 19:45', store: '골프장', amount: 850000, usageType: '미설정' },
{ cardName: '법인카드3', user: '영업팀', date: '2026-01-02 11:30', store: 'GS칼텍스', amount: 80000, usageType: '교통비' },
{ cardName: '법인카드2', user: '경영지원팀', date: '2026-01-01 16:00', store: '이마트', amount: 230000, usageType: '미설정' },
{ cardName: '법인카드1', user: '대표이사', date: '2025-12-30 20:30', store: '백화점', amount: 1500000, usageType: '미설정' },
{ cardName: '법인카드3', user: '영업팀', date: '2025-12-29 09:15', store: '커피빈', amount: 32000, usageType: '복리후생비' },
{ cardName: '법인카드2', user: '경영지원팀', date: '2025-12-28 13:45', store: '문구점', amount: 55000, usageType: '소모품비' },
{ cardName: '법인카드1', user: '대표이사', date: '2025-12-27 21:00', store: '호텔', amount: 450000, usageType: '미설정' },
],
filters: [
{
key: 'user',
options: [
{ value: 'all', label: '전체' },
{ value: '대표이사', label: '대표이사' },
{ value: '경영지원팀', label: '경영지원팀' },
{ value: '영업팀', label: '영업팀' },
],
defaultValue: 'all',
},
{
key: 'usageType',
options: [
{ value: 'all', label: '전체' },
{ value: '미설정', label: '미설정' },
{ value: '복리후생비', label: '복리후생비' },
{ value: '접대비', label: '접대비' },
{ value: '소모품비', label: '소모품비' },
{ value: '교통비', label: '교통비' },
],
defaultValue: 'all',
},
],
showTotal: true,
totalLabel: '합계',
totalValue: 30123000,
totalColumnKey: 'amount',
},
},
// P52: 가지급금 상세
cm2: {
title: '가지급금 상세',
dateFilter: {
enabled: true,
defaultPreset: '당월',
showSearch: true,
},
summaryCards: [
{ label: '가지급금 합계', value: '4.5억원' },
{ label: '가지급금 총액', value: 6000000, unit: '원' },
{ label: '건수', value: '10건' },
],
reviewCards: {
title: '가지급금 검토 필요',
cards: [
{ label: '카드', amount: 3123000, subLabel: '미정리 5건' },
{ label: '경조사', amount: 3123000, subLabel: '미증빙 5건' },
{ label: '상품권', amount: 3123000, subLabel: '미증빙 5건' },
{ label: '접대비', amount: 3123000, subLabel: '미증빙 5건' },
],
},
table: {
title: '가지급금 내역',
columns: [
{ key: 'no', label: 'No.', align: 'center' },
{ key: 'date', label: '발생일', align: 'center' },
{ key: 'classification', label: '분류', align: 'center' },
{ key: 'category', label: '구분', align: 'center' },
{ key: 'amount', label: '금액', align: 'right', format: 'currency' },
{ key: 'response', label: '대응', align: 'left' },
],
data: [
{ date: '2025-12-12', classification: '카드', category: '카드명', amount: 1000000, response: '미정리' },
{ date: '2025-12-12', classification: '카드', category: '카드명', amount: 1000000, response: '미증빙' },
{ date: '2025-12-12', classification: '경조사', category: '계좌명', amount: 1000000, response: '미증빙' },
{ date: '2025-12-12', classification: '상품권', category: '계좌명', amount: 1000000, response: '미증빙' },
{ date: '2025-12-12', classification: '접대비', category: '카드명', amount: 1000000, response: '주말 카드 사용' },
{ date: '2025-12-12', classification: '접대비', category: '카드명', amount: 1000000, response: '접대비 불인정' },
{ date: '2025-12-12', classification: '카드', category: '카드명', amount: 1000000, response: '불인정 가맹점(귀금속)' },
],
filters: [
{
key: 'classification',
options: [
{ value: 'all', label: '전체' },
{ value: '카드', label: '카드' },
{ value: '경조사', label: '경조사' },
{ value: '상품권', label: '상품권' },
{ value: '접대비', label: '접대비' },
],
defaultValue: 'all',
},
{
key: 'sortOrder',
options: [
{ value: 'all', label: '정렬' },
{ value: 'amountDesc', label: '금액 높은순' },
{ value: 'amountAsc', label: '금액 낮은순' },
{ value: 'latest', label: '최신순' },
],
defaultValue: 'all',
},
],
showTotal: true,
totalLabel: '합계',
totalValue: 111000000,
totalColumnKey: 'amount',
},
},
cm3: {
title: '법인세 예상 가중 상세',
summaryCards: [
{ label: '법인세 예상 증가', value: 3123000, unit: '원' },
{ label: '인정 이자', value: 6000000, unit: '원' },
{ label: '가지급금', value: '4.5억원' },
{ label: '인정이자', value: 6000000, unit: '원' },
],
comparisonSection: {
leftBox: {
title: '없을때 법인세',
items: [
{ label: '과세표준', value: '3억원' },
{ label: '법인세', value: 50970000, unit: '원' },
],
borderColor: 'orange',
},
rightBox: {
title: '있을때 법인세',
items: [
{ label: '과세표준', value: '3.06억원' },
{ label: '법인세', value: 54093000, unit: '원' },
],
borderColor: 'blue',
},
vsLabel: '법인세 예상 증가',
vsValue: 3123000,
vsSubLabel: '법인 세율 -12.5%',
},
referenceTable: {
title: '법인세 과세표준 (2024년 기준)',
columns: [
{ key: 'bracket', label: '과세표준', align: 'left' },
{ key: 'rate', label: '세율', align: 'center' },
{ key: 'formula', label: '계산식', align: 'left' },
],
data: [
{ bracket: '2억원 이하', rate: '9%', formula: '과세표준 × 9%' },
{ bracket: '2억원 초과 ~ 200억원 이하', rate: '19%', formula: '1,800만원 + (2억원 초과분 × 19%)' },
{ bracket: '200억원 초과 ~ 3,000억원 이하', rate: '21%', formula: '37.62억원 + (200억원 초과분 × 21%)' },
{ bracket: '3,000억원 초과', rate: '24%', formula: '625.62억원 + (3,000억원 초과분 × 24%)' },
],
},
},
cm4: {
title: '대표자 종합소득세 예상 가중 상세',
summaryCards: [
{ label: '대표자 종합세 예상 가중', value: 3123000, unit: '원' },
{ label: '추가 세금', value: '+12.5%', isComparison: true, isPositive: false },
{ label: '가지급금', value: '4.5억원' },
{ label: '인정이자 4.6%', value: 6000000, unit: '원' },
],
comparisonSection: {
leftBox: {
title: '가지급금 인정이자가 반영된 종합소득세',
items: [
{ label: '현재 예상 과세표준 (근로소득+상여)', value: 6000000, unit: '원' },
{ label: '현재 적용 세율', value: '19%' },
{ label: '현재 예상 세액', value: 10000000, unit: '원' },
],
borderColor: 'orange',
},
rightBox: {
title: '가지급금 인정이자가 정리된 종합소득세',
items: [
{ label: '가지급금 정리 시 예상 과세표준 (근로소득+상여)', value: 6000000, unit: '원' },
{ label: '가지급금 정리 시 적용 세율', value: '19%' },
{ label: '가지급금 정리 시 예상 세액', value: 10000000, unit: '원' },
],
borderColor: 'blue',
},
vsLabel: '종합소득세 예상 절감',
vsValue: 3123000,
vsSubLabel: '감소 세금 -12.5%',
vsBreakdown: [
{ label: '종합소득세', value: -2000000, unit: '원' },
{ label: '지방소득세', value: -200000, unit: '원' },
{ label: '4대 보험', value: -1000000, unit: '원' },
],
},
referenceTable: {
title: '종합소득세 과세표준 (2024년 기준)',
columns: [
{ key: 'bracket', label: '과세표준', align: 'left' },
{ key: 'rate', label: '세율', align: 'center' },
{ key: 'deduction', label: '누진공제', align: 'right' },
{ key: 'formula', label: '계산식', align: 'left' },
],
data: [
{ bracket: '1,400만원 이하', rate: '6%', deduction: '-', formula: '과세표준 × 6%' },
{ bracket: '1,400만원 초과 ~ 5,000만원 이하', rate: '15%', deduction: '126만원', formula: '과세표준 × 15% - 126만원' },
{ bracket: '5,000만원 초과 ~ 8,800만원 이하', rate: '24%', deduction: '576만원', formula: '과세표준 × 24% - 576만원' },
{ bracket: '8,800만원 초과 ~ 1.5억원 이하', rate: '35%', deduction: '1,544만원', formula: '과세표준 × 35% - 1,544만원' },
{ bracket: '1.5억원 초과 ~ 3억원 이하', rate: '38%', deduction: '1,994만원', formula: '과세표준 × 38% - 1,994만원' },
{ bracket: '3억원 초과 ~ 5억원 이하', rate: '40%', deduction: '2,594만원', formula: '과세표준 × 40% - 2,594만원' },
{ bracket: '5억원 초과 ~ 10억원 이하', rate: '42%', deduction: '3,594만원', formula: '과세표준 × 42% - 3,594만원' },
{ bracket: '10억원 초과', rate: '45%', deduction: '6,594만원', formula: '과세표준 × 45% - 6,594만원' },
],
},
},
};
return configs[cardId] || null;
export function getCardManagementModalConfig(_cardId: string): DetailModalConfig | null {
return null;
}

View File

@@ -1,243 +1,10 @@
import type { DetailModalConfig } from '../types';
/**
* 접대비 상세 공통 모달 config (et2, et3, et4 공통)
*/
const entertainmentDetailConfig: DetailModalConfig = {
title: '접대비 상세',
dateFilter: {
enabled: true,
defaultPreset: '당월',
showSearch: true,
},
summaryCards: [
// 첫 번째 줄: 당해년도
{ label: '당해년도 접대비 총 한도', value: 3123000, unit: '원' },
{ label: '당해년도 접대비 잔여한도', value: 6000000, unit: '원' },
{ label: '당해년도 접대비 사용금액', value: 6000000, unit: '원' },
{ label: '당해년도 접대비 초과 금액', value: 0, unit: '원' },
],
reviewCards: {
title: '접대비 검토 필요',
cards: [
{ label: '주말/심야', amount: 3123000, subLabel: '미증빙 5건' },
{ label: '기피업종 (유흥, 귀금속 등)', amount: 3123000, subLabel: '불인정 5건' },
{ label: '고액 결제', amount: 3123000, subLabel: '미증빙 5건' },
{ label: '증빙 미비', amount: 3123000, subLabel: '미증빙 5건' },
],
},
barChart: {
title: '월별 접대비 사용 추이',
data: [
{ name: '1월', value: 3500000 },
{ name: '2월', value: 4200000 },
{ name: '3월', value: 2300000 },
{ name: '4월', value: 3800000 },
{ name: '5월', value: 4500000 },
{ name: '6월', value: 3200000 },
{ name: '7월', value: 2800000 },
],
dataKey: 'value',
xAxisKey: 'name',
color: '#60A5FA',
},
pieChart: {
title: '사용자별 접대비 사용 비율',
data: [
{ name: '홍길동', value: 15000000, percentage: 53, color: '#60A5FA' },
{ name: '김철수', value: 10000000, percentage: 31, color: '#34D399' },
{ name: '이영희', value: 10000000, percentage: 10, color: '#FBBF24' },
{ name: '기타', value: 2000000, percentage: 6, color: '#F87171' },
],
},
table: {
title: '월별 접대비 사용 내역',
columns: [
{ key: 'no', label: 'No.', align: 'center' },
{ key: 'cardName', label: '카드명', align: 'left' },
{ key: 'user', label: '사용자', align: 'center' },
{ key: 'useDate', label: '사용일시', align: 'center', format: 'date' },
{ key: 'transDate', label: '거래일시', align: 'center', format: 'date' },
{ key: 'amount', label: '사용금액', align: 'right', format: 'currency' },
{ key: 'content', label: '내용', align: 'left' },
],
data: [
{ cardName: '카드명', user: '홍길동', useDate: '2025-12-12 12:12', transDate: '가맹점명', amount: 1000000, content: '심야 카드 사용' },
{ cardName: '카드명', user: '홍길동', useDate: '2025-12-12 12:12', transDate: '가맹점명', amount: 1000000, content: '미증빙' },
{ cardName: '카드명', user: '홍길동', useDate: '2025-12-12 12:12', transDate: '가맹점명', amount: 1000000, content: '고액 결제' },
{ cardName: '카드명', user: '김철수', useDate: '2025-10-14 12:12', transDate: '가맹점명', amount: 1000000, content: '불인정 가맹점 (귀금속)' },
{ cardName: '카드명', user: '이영희', useDate: '2025-12-12 12:12', transDate: '가맹점명', amount: 1000000, content: '접대비 불인정' },
],
filters: [
{
key: 'user',
options: [
{ value: 'all', label: '전체' },
{ value: '홍길동', label: '홍길동' },
{ value: '김철수', label: '김철수' },
{ value: '이영희', label: '이영희' },
],
defaultValue: 'all',
},
{
key: 'content',
options: [
{ value: 'all', label: '전체' },
{ value: '주말/심야', label: '주말/심야' },
{ value: '기피업종', label: '기피업종' },
{ value: '고액 결제', label: '고액 결제' },
{ value: '증빙 미비', label: '증빙 미비' },
],
defaultValue: 'all',
},
],
showTotal: true,
totalLabel: '합계',
totalValue: 11000000,
totalColumnKey: 'amount',
},
// 접대비 손금한도 계산 - 기본한도 / 수입금액별 추가한도
referenceTables: [
{
title: '접대비 손금한도 계산 - 기본한도',
columns: [
{ key: 'type', label: '법인 유형', align: 'left' },
{ key: 'annualLimit', label: '연간 기본한도', align: 'right' },
{ key: 'monthlyLimit', label: '월 환산', align: 'right' },
],
data: [
{ type: '일반법인', annualLimit: '12,000,000원', monthlyLimit: '1,000,000원' },
{ type: '중소기업', annualLimit: '36,000,000원', monthlyLimit: '3,000,000원' },
],
},
{
title: '수입금액별 추가한도',
columns: [
{ key: 'range', label: '수입금액 구간', align: 'left' },
{ key: 'formula', label: '추가한도 계산식', align: 'left' },
],
data: [
{ range: '100억원 이하', formula: '수입금액 × 0.2%' },
{ range: '100억 초과 ~ 500억 이하', formula: '2,000만원 + (수입금액 - 100억) × 0.1%' },
{ range: '500억원 초과', formula: '6,000만원 + (수입금액 - 500억) × 0.03%' },
],
},
],
// 접대비 계산
calculationCards: {
title: '접대비 계산',
cards: [
{ label: '중소기업 연간 기본한도', value: 36000000 },
{ label: '당해년도 수입금액별 추가한도', value: 16000000, operator: '+' },
{ label: '당해년도 접대비 총 한도', value: 52000000, operator: '=' },
],
},
// 접대비 현황 (분기별)
quarterlyTable: {
title: '접대비 현황',
rows: [
{ label: '한도금액', q1: 13000000, q2: 13000000, q3: 13000000, q4: 13000000, total: 52000000 },
{ label: '이월금액', q1: 0, q2: '', q3: '', q4: '', total: '' },
{ label: '사용금액', q1: 1000000, q2: '', q3: '', q4: '', total: '' },
{ label: '잔여한도', q1: 11000000, q2: '', q3: '', q4: '', total: '' },
{ label: '초과금액', q1: '', q2: '', q3: '', q4: '', total: '' },
],
},
};
/**
* 접대비 현황 모달 설정
* et_sales: 당해 매출 상세
* et_limit, et_remaining, et_used: 접대비 상세 (공통)
* API 연동 완료 — useEntertainmentDetail hook이 실제 데이터 반환
* 이 함수는 하위 호환용으로 유지하되 null 반환
*/
export function getEntertainmentModalConfig(cardId: string): DetailModalConfig | null {
const configs: Record<string, DetailModalConfig> = {
et_sales: {
title: '당해 매출 상세',
summaryCards: [
{ label: '당해년도 매출', value: 600000000, unit: '원' },
{ label: '전년 대비', value: '-12.5%', isComparison: true, isPositive: false },
{ label: '당월 매출', value: 6000000, unit: '원' },
],
barChart: {
title: '월별 매출 추이',
data: [
{ name: '1월', value: 85000000 },
{ name: '2월', value: 92000000 },
{ name: '3월', value: 78000000 },
{ name: '4월', value: 95000000 },
{ name: '5월', value: 88000000 },
{ name: '6월', value: 102000000 },
{ name: '7월', value: 60000000 },
],
dataKey: 'value',
xAxisKey: 'name',
color: '#60A5FA',
},
horizontalBarChart: {
title: '당해년도 거래처별 매출',
data: [
{ name: '(주)세우', value: 120000000 },
{ name: '대한건설', value: 95000000 },
{ name: '삼성테크', value: 78000000 },
{ name: '현대상사', value: 65000000 },
{ name: '기타', value: 42000000 },
],
color: '#60A5FA',
},
table: {
title: '일별 매출 내역',
columns: [
{ key: 'no', label: 'No.', align: 'center' },
{ key: 'date', label: '매출일', align: 'center', format: 'date' },
{ key: 'vendor', label: '거래처', align: 'left' },
{ key: 'amount', label: '매출금액', align: 'right', format: 'currency' },
{ key: 'type', label: '매출유형', align: 'center', highlightValue: '미설정' },
],
data: [
{ date: '2025-12-12', vendor: '회사명', amount: 11000000, type: '상품 매출' },
{ date: '2025-12-12', vendor: '회사명', amount: 11000000, type: '부품 매출' },
{ date: '2025-12-12', vendor: '회사명', amount: 11000000, type: '공사 매출' },
{ date: '2025-12-12', vendor: '회사명', amount: 11000000, type: '미설정' },
{ date: '2025-12-12', vendor: '회사명', amount: 11000000, type: '미설정' },
{ date: '2025-12-12', vendor: '회사명', amount: 11000000, type: '미설정' },
{ date: '2025-12-12', vendor: '회사명', amount: 11000000, type: '상품 매출' },
],
filters: [
{
key: 'type',
options: [
{ value: 'all', label: '전체' },
{ value: '상품 매출', label: '상품 매출' },
{ value: '부품 매출', label: '부품 매출' },
{ value: '공사 매출', label: '공사 매출' },
{ value: '임대 수익', label: '임대 수익' },
{ value: '기타 매출', label: '기타 매출' },
{ value: '미설정', label: '미설정' },
],
defaultValue: 'all',
},
],
showTotal: true,
totalLabel: '합계',
totalValue: 111000000,
totalColumnKey: 'amount',
},
},
// D1.7 리스크감지형 카드 ID → 접대비 상세 모달
et_weekend: entertainmentDetailConfig,
et_prohibited: entertainmentDetailConfig,
et_high_amount: entertainmentDetailConfig,
et_no_receipt: entertainmentDetailConfig,
// 레거시 카드 ID (하위 호환)
et_limit: entertainmentDetailConfig,
et_remaining: entertainmentDetailConfig,
et_used: entertainmentDetailConfig,
et1: entertainmentDetailConfig,
et2: entertainmentDetailConfig,
et3: entertainmentDetailConfig,
et4: entertainmentDetailConfig,
};
return configs[cardId] || null;
export function getEntertainmentModalConfig(_cardId: string): DetailModalConfig | null {
return null;
}

View File

@@ -1,269 +1,10 @@
import type { DetailModalConfig } from '../types';
/**
* 당월 예상 지출 모달 설정 (D1.7 기획서 P48-51 반영)
* 당월 예상 지출 모달 설정
* API 연동 완료 — useMonthlyExpenseDetail hook이 실제 데이터 반환
* 이 함수는 하위 호환용으로 유지하되 null 반환
*/
export function getMonthlyExpenseModalConfig(cardId: string): DetailModalConfig | null {
const configs: Record<string, DetailModalConfig> = {
// P48: 매입 상세
me1: {
title: '매입 상세',
dateFilter: {
enabled: true,
defaultPreset: '당월',
showSearch: true,
},
summaryCards: [
{ label: '매입', value: 3123000, unit: '원' },
{ label: '이전 대비', value: '-12.5%', isComparison: true, isPositive: false },
],
barChart: {
title: '매입 추이',
data: [
{ name: '1월', value: 45000000 },
{ name: '2월', value: 52000000 },
{ name: '3월', value: 48000000 },
{ name: '4월', value: 61000000 },
{ name: '5월', value: 55000000 },
{ name: '6월', value: 58000000 },
{ name: '7월', value: 50000000 },
],
dataKey: 'value',
xAxisKey: 'name',
color: '#60A5FA',
},
pieChart: {
title: '자재 유형별 구매 비율',
data: [
{ name: '원자재', value: 55000000, percentage: 55, color: '#60A5FA' },
{ name: '부자재', value: 35000000, percentage: 35, color: '#FBBF24' },
{ name: '포장재', value: 10000000, percentage: 10, color: '#F87171' },
],
},
table: {
title: '일별 매입 내역',
columns: [
{ key: 'no', label: 'No.', align: 'center' },
{ key: 'date', label: '매입일', align: 'center', format: 'date' },
{ key: 'vendor', label: '거래처', align: 'left' },
{ key: 'amount', label: '매입금액', align: 'right', format: 'currency' },
],
data: [
{ date: '2025-12-01', vendor: '회사명', amount: 11000000 },
{ date: '2025-12-01', vendor: '회사명', amount: 11000000 },
{ date: '2025-12-01', vendor: '회사명', amount: 11000000 },
{ date: '2025-12-01', vendor: '회사명', amount: 11000000 },
{ date: '2025-12-01', vendor: '회사명', amount: 11000000 },
{ date: '2025-12-01', vendor: '회사명', amount: 11000000 },
],
showTotal: true,
totalLabel: '합계',
totalValue: 111000000,
totalColumnKey: 'amount',
},
},
// P49: 카드 상세
me2: {
title: '카드 상세',
dateFilter: {
enabled: true,
defaultPreset: '당월',
showSearch: true,
},
summaryCards: [
{ label: '카드 사용', value: 6000000, unit: '원' },
{ label: '이전 대비', value: '-12.5%', isComparison: true, isPositive: false },
{ label: '건수', value: '10건' },
],
barChart: {
title: '카드 사용 추이',
data: [
{ name: '1월', value: 4500000 },
{ name: '2월', value: 5200000 },
{ name: '3월', value: 4800000 },
{ name: '4월', value: 6100000 },
{ name: '5월', value: 5500000 },
{ name: '6월', value: 5800000 },
{ name: '7월', value: 6000000 },
],
dataKey: 'value',
xAxisKey: 'name',
color: '#60A5FA',
},
pieChart: {
title: '사용자별 카드 사용 비율',
data: [
{ name: '홍길동', value: 55000000, percentage: 55, color: '#60A5FA' },
{ name: '김영희', value: 35000000, percentage: 35, color: '#FBBF24' },
{ name: '이정현', value: 10000000, percentage: 10, color: '#F87171' },
],
},
table: {
title: '일별 카드 사용 내역',
columns: [
{ key: 'no', label: 'No.', align: 'center' },
{ key: 'cardName', label: '카드명', align: 'left' },
{ key: 'user', label: '사용자', align: 'center' },
{ key: 'date', label: '사용일자', align: 'center', format: 'date' },
{ key: 'store', label: '가맹점명', align: 'left' },
{ key: 'amount', label: '사용금액', align: 'right', format: 'currency' },
{ key: 'usageType', label: '계정과목', align: 'center', highlightValue: '미설정' },
],
data: [
{ cardName: '홍길동', user: '홍길동', date: '2025-12-12 12:12', store: '가맹점명', amount: 1000000, usageType: '복리후생비' },
{ cardName: '홍길동', user: '홍길동', date: '2025-12-11 14:30', store: '가맹점명', amount: 1000000, usageType: '접대비' },
{ cardName: '홍길동', user: '홍길동', date: '2025-12-10 09:45', store: '가맹점명', amount: 1000000, usageType: '미설정' },
{ cardName: '홍길동', user: '홍길동', date: '2025-12-09 18:20', store: '가맹점명', amount: 1000000, usageType: '미설정' },
],
filters: [
{
key: 'user',
options: [
{ value: 'all', label: '전체' },
{ value: '홍길동', label: '홍길동' },
{ value: '김영희', label: '김영희' },
{ value: '이정현', label: '이정현' },
],
defaultValue: 'all',
},
],
showTotal: true,
totalLabel: '합계',
totalValue: 11000000,
totalColumnKey: 'amount',
},
},
// P50: 발행어음 상세
me3: {
title: '발행어음 상세',
dateFilter: {
enabled: true,
presets: ['당해년도', '전전월', '전월', '당월', '어제'],
defaultPreset: '당월',
showSearch: true,
},
summaryCards: [
{ label: '발행어음', value: 3123000, unit: '원' },
{ label: '이전 대비', value: '-12.5%', isComparison: true, isPositive: false },
],
barChart: {
title: '발행어음 추이',
data: [
{ name: '1월', value: 2000000 },
{ name: '2월', value: 2500000 },
{ name: '3월', value: 2200000 },
{ name: '4월', value: 2800000 },
{ name: '5월', value: 2600000 },
{ name: '6월', value: 3000000 },
{ name: '7월', value: 3123000 },
],
dataKey: 'value',
xAxisKey: 'name',
color: '#60A5FA',
},
pieChart: {
title: '거래처별 발행어음',
data: [
{ name: '거래처1', value: 50000000, percentage: 45, color: '#60A5FA' },
{ name: '거래처2', value: 35000000, percentage: 32, color: '#FBBF24' },
{ name: '거래처3', value: 20000000, percentage: 18, color: '#F87171' },
{ name: '거래처4', value: 6000000, percentage: 5, color: '#34D399' },
],
},
table: {
title: '일별 발행어음 내역',
columns: [
{ key: 'no', label: 'No.', align: 'center' },
{ key: 'vendor', label: '거래처', align: 'left' },
{ key: 'issueDate', label: '발행일', align: 'center', format: 'date' },
{ key: 'dueDate', label: '만기일', align: 'center', format: 'date' },
{ key: 'amount', label: '금액', align: 'right', format: 'currency' },
{ key: 'status', label: '상태', align: 'center' },
],
data: [
{ vendor: '회사명', issueDate: '2025-12-12', dueDate: '2025-12-12', amount: 1000000, status: '보관중' },
{ vendor: '회사명', issueDate: '2025-12-12', dueDate: '2025-12-12', amount: 1000000, status: '만기임박' },
{ vendor: '회사명', issueDate: '2025-12-12', dueDate: '2025-12-12', amount: 1000000, status: '보관중' },
{ vendor: '회사명', issueDate: '2025-12-12', dueDate: '2025-12-12', amount: 1000000, status: '만기임박' },
{ vendor: '회사명', issueDate: '2025-12-12', dueDate: '2025-12-12', amount: 1000000, status: '보관중' },
{ vendor: '회사명', issueDate: '2025-12-12', dueDate: '2025-12-12', amount: 1000000, status: '만기임박' },
],
filters: [
{
key: 'vendor',
options: [
{ value: 'all', label: '전체' },
{ value: '회사명', label: '회사명' },
],
defaultValue: 'all',
},
{
key: 'status',
options: [
{ value: 'all', label: '전체' },
{ value: '보관중', label: '보관중' },
{ value: '만기임박', label: '만기임박' },
{ value: '만기경과', label: '만기경과' },
{ value: '결제완료', label: '결제완료' },
{ value: '부도', label: '부도' },
],
defaultValue: 'all',
},
],
showTotal: true,
totalLabel: '합계',
totalValue: 111000000,
totalColumnKey: 'amount',
},
},
// P51: 당월 지출 예상 상세
me4: {
title: '당월 지출 예상 상세',
summaryCards: [
{ label: '당월 지출 예상', value: 6000000, unit: '원' },
{ label: '전월 대비', value: '-12.5%', isComparison: true, isPositive: false },
{ label: '총 계좌 잔액', value: 10000000, unit: '원' },
],
table: {
title: '당월 지출 승인 내역서',
columns: [
{ key: 'paymentDate', label: '예상 지급일', align: 'center' },
{ key: 'item', label: '항목', align: 'left' },
{ key: 'amount', label: '지출금액', align: 'right', format: 'currency', highlightColor: 'red' },
{ key: 'vendor', label: '거래처', align: 'center' },
{ key: 'account', label: '계좌', align: 'center' },
],
data: [
{ paymentDate: '2025-12-12', item: '품의 사유...', amount: 1000000, vendor: '회사명', account: '국민은행 1234' },
{ paymentDate: '2025-12-12', item: '품의 사유...', amount: 1000000, vendor: '회사명', account: '국민은행 1234' },
{ paymentDate: '2025-12-12', item: '(발행 어음) 123123123', amount: 1000000, vendor: '회사명', account: '국민은행 1234' },
{ paymentDate: '2025-12-12', item: '품의 사유...', amount: 1000000, vendor: '회사명', account: '국민은행 1234' },
{ paymentDate: '2025-12-12', item: '거래처명 12월분', amount: 1000000, vendor: '회사명', account: '국민은행 1234' },
{ paymentDate: '2025-12-12', item: '품의 사유...', amount: 1000000, vendor: '회사명', account: '국민은행 1234' },
{ paymentDate: '2025-12-12', item: '적요 내용', amount: 1000000, vendor: '회사명', account: '국민은행 1234' },
],
filters: [
{
key: 'vendor',
options: [
{ value: 'all', label: '전체' },
{ value: '회사명', label: '회사명' },
],
defaultValue: 'all',
},
],
showTotal: true,
totalLabel: '2025/12 계',
totalValue: 6000000,
totalColumnKey: 'amount',
footerSummary: [
{ label: '지출 합계', value: 6000000 },
{ label: '계좌 잔액', value: 10000000 },
{ label: '최종 차액', value: 4000000 },
],
},
},
};
return configs[cardId] || null;
export function getMonthlyExpenseModalConfig(_cardId: string): DetailModalConfig | null {
return null;
}

View File

@@ -2,76 +2,9 @@ import type { DetailModalConfig } from '../types';
/**
* 부가세 모달 설정
* 모든 카드가 동일한 상세 모달
* API 연동 완료 — useVatDetail hook이 실제 데이터 반환
* 이 함수는 하위 호환용으로 유지하되 null 반환
*/
export function getVatModalConfig(): DetailModalConfig {
return {
title: '예상 납부세액',
periodSelect: {
enabled: true,
options: [
{ value: '2026-1-expected', label: '2026년 1기 예정' },
{ value: '2025-2-confirmed', label: '2025년 2기 확정' },
{ value: '2025-2-expected', label: '2025년 2기 예정' },
{ value: '2025-1-confirmed', label: '2025년 1기 확정' },
],
defaultValue: '2026-1-expected',
},
summaryCards: [
{ label: '예상매출', value: '30.5억원' },
{ label: '예상매입', value: '20.5억원' },
{ label: '예상 납부세액', value: '1.1억원' },
],
// 부가세 요약 테이블
referenceTable: {
title: '2026년 1기 예정 부가세 요약',
columns: [
{ key: 'category', label: '구분', align: 'left' },
{ key: 'supplyAmount', label: '공급가액', align: 'right' },
{ key: 'taxAmount', label: '세액', align: 'right' },
],
data: [
{ category: '매출(전자세금계산서)', supplyAmount: '100,000,000', taxAmount: '10,000,000' },
{ category: '매입(전자세금계산서)', supplyAmount: '10,000,000', taxAmount: '1,000,000' },
{ category: '매입(종이세금계산서)', supplyAmount: '10,000,000', taxAmount: '1,000,000' },
{ category: '매입(계산서)', supplyAmount: '10,000,000', taxAmount: '1,000,000' },
{ category: '매입(신용카드)', supplyAmount: '10,000,000', taxAmount: '1,000,000' },
{ category: '납부세액', supplyAmount: '', taxAmount: '6,000,000' },
],
},
// 세금계산서 미발행/미수취 내역
table: {
title: '세금계산서 미발행/미수취 내역',
columns: [
{ key: 'no', label: 'No.', align: 'center' },
{ key: 'type', label: '구분', align: 'center' },
{ key: 'issueDate', label: '발생일자', align: 'center', format: 'date' },
{ key: 'vendor', label: '거래처', align: 'left' },
{ key: 'vat', label: '부가세', align: 'right', format: 'currency' },
{ key: 'invoiceStatus', label: '세금계산서 미발행/미수취', align: 'center' },
],
data: [
{ type: '매출', issueDate: '2025-12-12', vendor: '회사명', vat: 11000000, invoiceStatus: '미발행' },
{ type: '매입', issueDate: '2025-12-12', vendor: '회사명', vat: 11000000, invoiceStatus: '미수취' },
{ type: '매출', issueDate: '2025-12-12', vendor: '회사명', vat: 11000000, invoiceStatus: '미발행' },
{ type: '매입', issueDate: '2025-12-12', vendor: '회사명', vat: 11000000, invoiceStatus: '미수취' },
{ type: '매출', issueDate: '2025-12-12', vendor: '회사명', vat: 11000000, invoiceStatus: '미발행' },
],
filters: [
{
key: 'type',
options: [
{ value: 'all', label: '전체' },
{ value: '매출', label: '매출' },
{ value: '매입', label: '매입' },
],
defaultValue: 'all',
},
],
showTotal: true,
totalLabel: '합계',
totalValue: 111000000,
totalColumnKey: 'vat',
},
};
export function getVatModalConfig(): DetailModalConfig | null {
return null;
}

View File

@@ -2,149 +2,9 @@ import type { DetailModalConfig } from '../types';
/**
* 복리후생비 현황 모달 설정
*
* @deprecated 정적 목업 데이터 - API 연동 후에는 useWelfareDetail hook 사용 권장
*
* API 연동 방법:
* 1. useWelfareDetail hook 호출하여 modalConfig 가져오기
* 2. API 호출 실패 시 이 fallback config 사용
*
* @example
* const { modalConfig, loading, error, refetch } = useWelfareDetail({
* calculationType: 'fixed',
* year: 2026,
* quarter: 1,
* });
* const config = modalConfig ?? getWelfareModalConfig('fixed'); // fallback
*
* @param calculationType - 계산 방식 ('fixed': 직원당 정액 금액/월, 'ratio': 연봉 총액 비율)
* API 연동 완료 — useWelfareDetail hook이 실제 데이터 반환
* 이 함수는 하위 호환용으로 유지하되 null 반환
*/
export function getWelfareModalConfig(calculationType: 'fixed' | 'ratio'): DetailModalConfig {
// 계산 방식에 따른 조건부 calculationCards 생성
const calculationCards = calculationType === 'fixed'
? {
// 직원당 정액 금액/월 방식
title: '복리후생비 계산',
subtitle: '직원당 정액 금액/월 200,000원',
cards: [
{ label: '직원 수', value: 20, unit: '명' },
{ label: '연간 직원당 월급 금액', value: 2400000, unit: '원', operator: '×' as const },
{ label: '당해년도 복리후생비 총 한도', value: 48000000, unit: '원', operator: '=' as const },
],
}
: {
// 연봉 총액 비율 방식
title: '복리후생비 계산',
subtitle: '연봉 총액 기준 비율 20.5%',
cards: [
{ label: '연봉 총액', value: 1000000000, unit: '원' },
{ label: '비율', value: 20.5, unit: '%', operator: '×' as const },
{ label: '당해년도 복리후생비 총 한도', value: 205000000, unit: '원', operator: '=' as const },
],
};
return {
title: '복리후생비 상세',
dateFilter: {
enabled: true,
defaultPreset: '당월',
showSearch: true,
},
summaryCards: [
// 1행: 당해년도 기준
{ label: '당해년도 복리후생비 총 한도', value: 3123000, unit: '원' },
{ label: '당해년도 복리후생비 잔여한도', value: 6000000, unit: '원' },
{ label: '당해년도 복리후생비 사용금액', value: 6000000, unit: '원' },
{ label: '당해년도 복리후생비 초과 금액', value: 0, unit: '원' },
],
reviewCards: {
title: '복리후생비 검토 필요',
cards: [
{ label: '비과세 한도 초과', amount: 3123000, subLabel: '5건' },
{ label: '사적 사용 의심', amount: 3123000, subLabel: '5건' },
{ label: '특정인 편중', amount: 3123000, subLabel: '5건' },
{ label: '항목별 한도 초과', amount: 3123000, subLabel: '5건' },
],
},
barChart: {
title: '월별 복리후생비 사용 추이',
data: [
{ name: '1월', value: 1500000 },
{ name: '2월', value: 1800000 },
{ name: '3월', value: 2200000 },
{ name: '4월', value: 1900000 },
{ name: '5월', value: 2100000 },
{ name: '6월', value: 1700000 },
],
dataKey: 'value',
xAxisKey: 'name',
color: '#60A5FA',
},
pieChart: {
title: '항목별 사용 비율',
data: [
{ name: '식비', value: 55000000, percentage: 55, color: '#FBBF24' },
{ name: '건강검진', value: 25000000, percentage: 5, color: '#60A5FA' },
{ name: '경조사비', value: 10000000, percentage: 10, color: '#F87171' },
{ name: '기타', value: 10000000, percentage: 30, color: '#34D399' },
],
},
table: {
title: '일별 복리후생비 사용 내역',
columns: [
{ key: 'no', label: 'No.', align: 'center' },
{ key: 'cardName', label: '카드명', align: 'left' },
{ key: 'user', label: '사용자', align: 'center' },
{ key: 'date', label: '사용일자', align: 'center', format: 'date' },
{ key: 'store', label: '가맹점명', align: 'left' },
{ key: 'amount', label: '사용금액', align: 'right', format: 'currency' },
{ key: 'content', label: '내용', align: 'left' },
],
data: [
{ cardName: '카드명', user: '홍길동', date: '2025-12-12 12:12', store: '가맹점명', amount: 1000000, content: '비과세 한도 초과' },
{ cardName: '카드명', user: '홍길동', date: '2025-12-12 12:12', store: '가맹점명', amount: 1200000, content: '사적 사용 의심' },
{ cardName: '카드명', user: '홍길동', date: '2025-12-12 12:12', store: '가맹점명', amount: 1500000, content: '특정인 편중' },
{ cardName: '카드명', user: '홍길동', date: '2025-12-12 12:12', store: '가맹점명', amount: 1300000, content: '항목별 한도 초과' },
{ cardName: '카드명', user: '홍길동', date: '2025-12-12 12:12', store: '가맹점명', amount: 6000000, content: '비과세 한도 초과' },
],
filters: [
{
key: 'user',
options: [
{ value: 'all', label: '전체' },
{ value: '홍길동', label: '홍길동' },
],
defaultValue: 'all',
},
{
key: 'content',
options: [
{ value: 'all', label: '전체' },
{ value: '비과세 한도 초과', label: '비과세 한도 초과' },
{ value: '사적 사용 의심', label: '사적 사용 의심' },
{ value: '특정인 편중', label: '특정인 편중' },
{ value: '항목별 한도 초과', label: '항목별 한도 초과' },
],
defaultValue: 'all',
},
],
showTotal: true,
totalLabel: '합계',
totalValue: 11000000,
totalColumnKey: 'amount',
},
// 복리후생비 계산 (조건부 - calculationType에 따라)
calculationCards,
// 복리후생비 현황 (분기별 테이블)
quarterlyTable: {
title: '복리후생비 현황',
rows: [
{ label: '한도금액', q1: 12000000, q2: 12000000, q3: 12000000, q4: 12000000, total: 48000000 },
{ label: '이월금액', q1: 0, q2: '', q3: '', q4: '', total: '' },
{ label: '사용금액', q1: 1000000, q2: '', q3: '', q4: '', total: '' },
{ label: '잔여한도', q1: 11000000, q2: '', q3: '', q4: '', total: '' },
{ label: '초과금액', q1: '', q2: '', q3: '', q4: '', total: '' },
],
},
};
export function getWelfareModalConfig(_calculationType: 'fixed' | 'ratio'): DetailModalConfig | null {
return null;
}