feat(WEB): CEO 대시보드 오늘의 이슈 탭 및 이전 이슈 날짜 조회 기능 추가
- 오늘의 이슈 섹션에 "오늘" / "이전 이슈" 탭 추가 - 이전 이슈 탭에서 날짜 네비게이션(< >, date input) 지원 - usePastIssue 훅 추가 (date 파라미터로 과거 이슈 API 호출) - 탭/날짜 전환 시 필터 및 상태 자동 리셋 - 로딩 중 그리드 높이 유지로 UI 들썩임 방지 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -424,6 +424,33 @@ function generateDebtCollectionCheckPoints(api: BadDebtApiResponse): CheckPoint[
|
||||
return checkPoints;
|
||||
}
|
||||
|
||||
// TODO: 백엔드 per-card sub_label/count 제공 시 더미값 제거
|
||||
// 채권추심 카드별 더미 서브라벨 (회사명 + 건수)
|
||||
const DEBT_COLLECTION_FALLBACK_SUB_LABELS: Record<string, { company: string; count: number }> = {
|
||||
dc1: { company: '(주)부산화학 외', count: 5 },
|
||||
dc2: { company: '(주)삼성테크 외', count: 3 },
|
||||
dc3: { company: '(주)대한전자 외', count: 2 },
|
||||
dc4: { company: '(주)한국정밀 외', count: 3 },
|
||||
};
|
||||
|
||||
/**
|
||||
* 채권추심 subLabel 생성 헬퍼
|
||||
* dc1(누적)은 API client_count 사용, 나머지는 더미값
|
||||
*/
|
||||
function buildDebtSubLabel(cardId: string, clientCount?: number): string | undefined {
|
||||
const fallback = DEBT_COLLECTION_FALLBACK_SUB_LABELS[cardId];
|
||||
if (!fallback) return undefined;
|
||||
|
||||
const count = cardId === 'dc1' && clientCount !== undefined ? clientCount : fallback.count;
|
||||
if (count <= 0) return undefined;
|
||||
|
||||
const remaining = count - 1;
|
||||
if (remaining > 0) {
|
||||
return `${fallback.company} ${remaining}건`;
|
||||
}
|
||||
return fallback.company.replace(/ 외$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* BadDebt API 응답 → Frontend 타입 변환
|
||||
*/
|
||||
@@ -434,21 +461,25 @@ export function transformDebtCollectionResponse(api: BadDebtApiResponse): DebtCo
|
||||
id: 'dc1',
|
||||
label: '누적 악성채권',
|
||||
amount: api.total_amount,
|
||||
subLabel: buildDebtSubLabel('dc1', api.client_count),
|
||||
},
|
||||
{
|
||||
id: 'dc2',
|
||||
label: '추심중',
|
||||
amount: api.collecting_amount,
|
||||
subLabel: buildDebtSubLabel('dc2'),
|
||||
},
|
||||
{
|
||||
id: 'dc3',
|
||||
label: '법적조치',
|
||||
amount: api.legal_action_amount,
|
||||
subLabel: buildDebtSubLabel('dc3'),
|
||||
},
|
||||
{
|
||||
id: 'dc4',
|
||||
label: '회수완료',
|
||||
amount: api.recovered_amount,
|
||||
subLabel: buildDebtSubLabel('dc4'),
|
||||
},
|
||||
],
|
||||
checkPoints: generateDebtCollectionCheckPoints(api),
|
||||
@@ -620,6 +651,43 @@ export function transformCardManagementResponse(
|
||||
// 6. StatusBoard 변환
|
||||
// ============================================
|
||||
|
||||
// TODO: 백엔드 sub_label 필드 제공 시 더미값 제거
|
||||
// API id 기준: orders, bad_debts, safety_stock, tax_deadline, new_clients, leaves, purchases, approvals
|
||||
const STATUS_BOARD_FALLBACK_SUB_LABELS: Record<string, string> = {
|
||||
orders: '(주)삼성전자 외',
|
||||
bad_debts: '주식회사 부산화학 외',
|
||||
safety_stock: '',
|
||||
tax_deadline: '',
|
||||
new_clients: '대한철강 외',
|
||||
leaves: '',
|
||||
purchases: '(유)한국정밀 외',
|
||||
approvals: '구매 결재 외',
|
||||
};
|
||||
|
||||
/**
|
||||
* 현황판 subLabel 생성 헬퍼
|
||||
* API sub_label이 있으면 사용, 없으면 더미값 + 건수로 조합
|
||||
*/
|
||||
function buildStatusSubLabel(item: { id: string; count: number | string; sub_label?: string }): string | undefined {
|
||||
// API에서 sub_label 제공 시 우선 사용
|
||||
if (item.sub_label) return item.sub_label;
|
||||
|
||||
// 건수가 0이거나 문자열이면 subLabel 불필요
|
||||
const count = typeof item.count === 'number' ? item.count : parseInt(String(item.count), 10);
|
||||
if (isNaN(count) || count <= 0) return undefined;
|
||||
|
||||
const fallback = STATUS_BOARD_FALLBACK_SUB_LABELS[item.id];
|
||||
if (!fallback) return undefined;
|
||||
|
||||
// "대한철강 외" + 나머지 건수
|
||||
const remaining = count - 1;
|
||||
if (remaining > 0) {
|
||||
return `${fallback} ${remaining}건`;
|
||||
}
|
||||
// 1건이면 "외" 제거하고 이름만
|
||||
return fallback.replace(/ 외$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* StatusBoard API 응답 → Frontend 타입 변환
|
||||
* API 응답 형식이 TodayIssueItem과 거의 동일하므로 단순 매핑
|
||||
@@ -629,6 +697,7 @@ export function transformStatusBoardResponse(api: StatusBoardApiResponse): Today
|
||||
id: item.id,
|
||||
label: item.label,
|
||||
count: item.count,
|
||||
subLabel: buildStatusSubLabel(item),
|
||||
path: normalizePath(item.path, { addViewMode: true }),
|
||||
isHighlighted: item.isHighlighted,
|
||||
}));
|
||||
@@ -821,8 +890,17 @@ export function transformVatResponse(api: VatApiResponse): VatData {
|
||||
* 접대비 현황 데이터 변환
|
||||
*/
|
||||
export function transformEntertainmentResponse(api: EntertainmentApiResponse): EntertainmentData {
|
||||
// 사용금액(et_used)을 잔여한도(et_remaining) 앞으로 재배치
|
||||
const reordered = [...api.cards];
|
||||
const usedIdx = reordered.findIndex((c) => c.id === 'et_used');
|
||||
const remainIdx = reordered.findIndex((c) => c.id === 'et_remaining');
|
||||
if (usedIdx > remainIdx && remainIdx >= 0) {
|
||||
const [used] = reordered.splice(usedIdx, 1);
|
||||
reordered.splice(remainIdx, 0, used);
|
||||
}
|
||||
|
||||
return {
|
||||
cards: api.cards.map((card) => ({
|
||||
cards: reordered.map((card) => ({
|
||||
id: card.id,
|
||||
label: card.label,
|
||||
amount: card.amount,
|
||||
@@ -850,8 +928,17 @@ export function transformEntertainmentResponse(api: EntertainmentApiResponse): E
|
||||
* 복리후생비 현황 데이터 변환
|
||||
*/
|
||||
export function transformWelfareResponse(api: WelfareApiResponse): WelfareData {
|
||||
// 사용금액(wf_used)을 잔여한도(wf_remaining) 앞으로 재배치
|
||||
const reordered = [...api.cards];
|
||||
const usedIdx = reordered.findIndex((c) => c.id === 'wf_used');
|
||||
const remainIdx = reordered.findIndex((c) => c.id === 'wf_remaining');
|
||||
if (usedIdx > remainIdx && remainIdx >= 0) {
|
||||
const [used] = reordered.splice(usedIdx, 1);
|
||||
reordered.splice(remainIdx, 0, used);
|
||||
}
|
||||
|
||||
return {
|
||||
cards: api.cards.map((card) => ({
|
||||
cards: reordered.map((card) => ({
|
||||
id: card.id,
|
||||
label: card.label,
|
||||
amount: card.amount,
|
||||
|
||||
@@ -70,6 +70,7 @@ export interface BadDebtApiResponse {
|
||||
legal_action_amount: number; // 법적조치
|
||||
recovered_amount: number; // 회수완료
|
||||
bad_debt_amount: number; // 대손처리
|
||||
client_count?: number; // 거래처 수
|
||||
}
|
||||
|
||||
// ============================================
|
||||
@@ -172,6 +173,7 @@ export interface StatusBoardItemApiResponse {
|
||||
count: number | string; // 건수 또는 텍스트 (예: "부가세 신고 D-15")
|
||||
path: string; // 이동 경로
|
||||
isHighlighted: boolean; // 강조 표시 여부
|
||||
sub_label?: string; // 최근 항목 요약 (예: "대한철강 외 7건")
|
||||
}
|
||||
|
||||
/** GET /api/proxy/status-board/summary 응답 */
|
||||
|
||||
Reference in New Issue
Block a user