다크모드
계정 허브 페이지 (세대×기간) — FE 메인테이너 매뉴얼
대상: 이 페이지를 이어 개발할 FE/AI. 상태: ✅ P1 구현 / ⚠️ 부과·연체·이월 read-only / ❌ 청구(고지서)·기간 셀렉터 미구현(P2).
무엇 / 왜
빌링 dynamic 정보(수납·연체·이월·조정)가 그동안 수납 상세 시트 한 곳에만 있었다. 세대(호실)의 한 청구월을 "계정"으로 보고 부과→청구→정산→연체→이월→조정을 한 화면에서 조망하는 풀페이지 허브. 기존 오버레이 시트는 빠른 수납처리용으로 유지하고, 허브는 ↗로 진입하는 드릴다운(§0-10).
정본 용어 체인(커밋 14e228ac6): 부과액 + 이월미납 + 연체료 = 받을금액 − 선수금 = 청구액(납부하실 금액). 청구액 = detail.totals.payable.
라우트
- 경로:
/service-charge/{actual|provisional}/console/account/:unitKey+?tab= - 등록:
src/router/buildLineRoutes.js—CORE_VIEWS['account/:unitKey']+SERVICE_CHARGE_CORE에 추가(양 에디션 공유, 중복 import 없음).billingLines.js는 건드리지 않음(콘솔 탭이 아니라 드릴다운). - 진입:
collecting/unit/blocks/DynamicTableOrg.vue행의 ↗(open_in_new) 버튼 →router.push(billingPath('account/' + row.key)). P1은 unit 리스트에만 (허브가 unit 단위 → contract/member→unit 매핑은 후속). ?tab=값(ascii):assessment(부과) ·invoice(청구) ·settlement(정산·기본) ·late-fee(연체) ·carryover(이월) ·adjustment(조정).
컴포넌트 트리
views/billing/_core/console/account/IndexView.vue 셸(Header + Main)
components/billing/_core/console/account/
├ HeaderOrg.vue 뒤로 + 세대명(detail.identity) + periodLabel + ConsoleTabBarOrg(active-group=collecting)
├ MainOrg.vue 청구액 스트립 + TabBarOrg + 6 탭 콘텐츠(.tabs-content data-state)
├ blocks/TabBarOrg.vue plain-button + data-state, pill enclosure(tabs-list-primary-moderate)
└ tabs/
├ AssessmentTab.vue 당기 부과(billedByComponent) read
├ InvoiceTab.vue P2 스텁(고지서 불변·재발행 안내)
├ SettlementTab.vue ★수납 시트 블록 공유 마운트
├ LateFeeTab.vue 연체료 구간 분절 read
├ CarryoverTab.vue 이월(forwarded) 차수 read
└ AdjustmentTab.vue ReceivableAdjustmentsOrg 재호스팅(emit cancel/approve)데이터 흐름 (핵심)
- 단일 진실 =
useCollectingActions(selectionRef)(src/composables/useCollectingActions.js).selectionRef=MainOrg의computed(() => ({ axis:'unit', key: route.params.unitKey })).- 반환:
{ asOf, detail, onAsOf, onActionConfirm, onAdjustmentCancel, onAdjustmentApprove, receiptAsPayment, onCancelOpen, onCancelConfirm }. detail = detailFor('unit', unitKey, asOf)—useCollectingDetail반응형 모델(periods/totals/advance/receipts/adjustments).
- 시트와 공유:
SheetCollectingDetailArt.vue도 동일useCollectingActions(selectedCollecting)를 쓴다. 그래서 같은 세대를 시트로 열든 허브로 열든 확정/조정 결과가 동일(reactive singleton: useReceipts/useReceivableAdjustments). - 정산 탭만 액션:
SettlementTab이props.actions를 받아 블록(collecting/detail/blocks/*)에 그대로 배선.DialogCollectingCancelArt(iddialog-collecting-cancel-art)·SheetDerivationArt도 정산 탭이 호스팅(허브에선 시트가 동시 마운트되지 않아 id 충돌 없음). - 나머지 탭은 read: 전부
detail파생. 부과=당기 차수billedByComponent, 연체=periods[].lateFee.breakdown평탄화, 이월=periods.filter(kind==='forwarded'), 조정=detail.adjustments(+emit).
DS / 패턴
- 탭바는 pill enclosure 변형(
tabs-list tabs-list-primary-moderate tabs-list-radius-full+tabs-trigger tabs-trigger-md+data-state). line variant는 미채택 — underline indicator(.tabs-indicator)가 Reka 래퍼의--reka-tabs-indicator-*계산에 의존하는데 콘솔은 plain-button data-state 컨벤션. 콘솔의 기존 축 탭바와 동일 어휘. - 탭 count 칩 없음 —
.tabs-trigger-count가 EDS 미보유(MDS만). 필요 시 leysys-design EDS에 정식 승격(ADR) 후 sync. 콘솔 ad-hoc 금지. - 청구액 스트립 = §8-3(카드 밖 단일 최상위 수치, 비접힘) —
card card-sm card-inset-edged bg-neutral-minimal, 청구액만title-lg.
게이팅 / 확장 포인트
- 기간(period): P1은 edition
periodLabel단일값. 과거 청구월 선택 → 라우트에 period param 추가 + 드롭다운(HeaderOrg) +detailFor에 기간 필터.MainOrg.selection이 확장 지점. - 청구(고지서) 탭(P2):
useInvoiceDocuments(불변 스냅샷 v1/v2 재발행) + 인쇄(statement-app/.../invoice/print/).InvoiceTab.vue교체. - contract/member 진입(후속): 허브 axis를 쿼리로 일반화하거나 contract/member→unit 해석 후 진입.
- 부과 탭 payable 실배선: 현재 read만. 실서비스 InvoiceTable 연동은 P2.
테스트 / 검증
npm run build통과.node scripts/precommit-check.mjsDS 감사 clean(ad-hoc 0 / SSOT 0).- 스모크: 수납(유닛) 리스트 ↗ →
account/:unitKey?tab=settlement. 상단 청구액 = 시트 청구액 동일.?tab=전환·새로고침 유지. 정산 탭 수납 1건 확정 → 청구액/잔액/수납내역 즉시 갱신, 동일 세대 시트로 교차 확인.