feat: 테스트 데이터(V17) + 실 DB 프로파일 + 프론트 화면 확장

V17 (192.168.0.60:55432/trading_ai/ga 적용 완료):
- insurance_company 5 (삼성/한화/교보/디비/KB)
- product 20 (보험사별 4개)
- organization 7 (본부 1, 지점 3, 팀 3)
- agent 50 (GM 3 / DM 3 / SMGR 6 / SFC 12 / FC 26)
- contract 200 (ACTIVE 190 + LAPSE 10)
- commission_rate 100 (5상품 × 5년차 × 5보험사 = 일부)
  실제 INSERT는 상품 20개 × 1년차 + 20 × 4년차 = 100건
- payout_rule 120 (직급 6 × 보험종류 4 × 연차 5)
- override_rule 10 (FC/SFC/MGR/SMGR/DM 직급 간 오버라이드)
- chargeback_rule 4 (실효월수 구간별 환수율)

운영 DB 프로파일:
- application-trading_ai.yml (ga-api, ga-batch)
- 192.168.0.60:55432 / trading_ai / currentSchema=ga
- Flyway baseline-version=17 (수동 적용 분 인정)

DB 포트 변경 반영: 5432 → 55432 (운영 변경)

프론트 추가 화면:
- DataGrid (AG Grid Community 래퍼; 합계행/셀편집/로딩 오버레이)
- 예외원장 (ExceptionLedger): 승인/반려 액션
- 지급관리 (PaymentList)
- 정산 배치 (BatchRun): 실행 + 진행률 폴링
- 시스템관리:
  * UserList: 비밀번호 초기화 + 잠금 해제
  * RoleList: 좌측 역할 + 우측 권한 매트릭스 (메뉴 × 6권한)
  * CodeList: 좌측 그룹 + 우측 상세 코드, 시스템그룹 경고

설계사 상세/등록 폼:
- AgentDetail: 통계 카드 + 기본정보 + 수정 버튼
- AgentForm: 등록/수정 통합 (id='new' 또는 :id/edit)

라우터:
- /org/agents/{new|:id|:id/edit}
- /ledger/exception, /payments, /settle/batch
- /system/{users|roles|codes}
This commit is contained in:
GA Pro
2026-05-09 22:47:55 +09:00
parent 98eb231950
commit 92079d2385
16 changed files with 1197 additions and 4 deletions
+80
View File
@@ -0,0 +1,80 @@
import { useState } from 'react';
import { Card, Col, Empty, Row, Table, Tag, Typography } from 'antd';
import { useQuery } from '@tanstack/react-query';
import PageContainer from '@/components/common/PageContainer';
import { commonCodeApi, CommonCodeGroup } from '@/api/user';
const { Text } = Typography;
export default function CodeList() {
const [selected, setSelected] = useState<CommonCodeGroup | null>(null);
const { data: groups = [] } = useQuery({
queryKey: ['code', 'groups'],
queryFn: () => commonCodeApi.groups(),
});
const { data: codes = [] } = useQuery({
queryKey: ['code', 'codes', selected?.groupCode],
queryFn: () => commonCodeApi.codes(selected!.groupCode),
enabled: !!selected,
});
return (
<PageContainer title="공통코드 관리">
<Row gutter={16}>
<Col span={10}>
<Card title="그룹" size="small">
<Table
rowKey="groupCode"
dataSource={groups}
size="small"
pagination={false}
scroll={{ y: 600 }}
onRow={(r) => ({ onClick: () => setSelected(r), style: { cursor: 'pointer' } })}
rowClassName={(r) => r.groupCode === selected?.groupCode ? 'ant-table-row-selected' : ''}
columns={[
{ title: '그룹코드', dataIndex: 'groupCode', width: 160 },
{ title: '그룹명', dataIndex: 'groupName' },
{
title: '시스템', dataIndex: 'isSystem', width: 80, align: 'center',
render: (v) => v === 'Y' ? <Tag color="orange"></Tag> : <Tag></Tag>,
},
]}
/>
</Card>
</Col>
<Col span={14}>
<Card title={selected ? `상세 코드 — ${selected.groupName}` : '상세 코드'} size="small">
{!selected ? (
<Empty description="그룹을 선택하세요" />
) : (
<Table
rowKey="codeId"
dataSource={codes}
size="small"
pagination={false}
scroll={{ y: 600 }}
columns={[
{ title: '코드', dataIndex: 'code', width: 120 },
{ title: '코드명', dataIndex: 'codeName' },
{ title: '설명', dataIndex: 'codeDesc' },
{ title: '정렬', dataIndex: 'sortOrder', width: 60, align: 'right' },
{
title: '활성', dataIndex: 'isActive', width: 70, align: 'center',
render: (v) => v === 'Y' ? <Tag color="green">Y</Tag> : <Tag>N</Tag>,
},
]}
/>
)}
{selected?.isSystem === 'Y' && (
<Text type="warning" style={{ display: 'block', marginTop: 8 }}>
/ .
</Text>
)}
</Card>
</Col>
</Row>
</PageContainer>
);
}