feat(frontend): 09_UI_DESIGN 디자인 시스템 전체 적용
테마 / 폰트 / 아이콘: - ConfigProvider theme 토큰 30+ 추가 (브랜드 컬러, 폰트, 라운딩, 메뉴/테이블/버튼) - Pretendard 폰트 link + index.css fontFamily 적용 - @tabler/icons-react 추가, MainLayout 에 적용 레이아웃: - styles/variables.css 신규 (--ga-* 변수 30+) - MainLayout: Header 48px 흰색 + bottom border, Sider 220px + 토글, Content 1400px 중앙 정렬, 정산월 MonthPicker, 햄버거 토글 공통 컴포넌트: - CodeBadge: 4단계 색상 매핑 (success/info/warning/danger), 누락 코드 보강 - MoneyText: 양수 #0F6E56, 음수 #E24B4A, 0 #888 (CSS 변수 사용) - SearchForm: 배경 #fafafa, borderRadius 8, padding 12px 16px - DataGrid: rowHeight 36, headerHeight 38, AG_GRID_LOCALE_KO, 합계행 getRowStyle 회색 배경 + 굵게 페이지 페이지네이션 통일: - utils/tablePagination.ts 신규 (showTotal '총 N건', pageSizeOptions [20/50/100]) - 7개 페이지 (Agent/Contract/Recruit/Exception/Settle/Payment/User) 적용 검증: tsc -b 통과 / vite build 성공 (10s) / 백엔드 단위테스트 76건 영향 없음 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,3 +38,7 @@ target/
|
||||
|
||||
# Claude Code 개인 설정 (Agent Teams 등)
|
||||
.claude/
|
||||
|
||||
# vite/tsc 빌드 산출물
|
||||
ga-frontend/tsconfig.tsbuildinfo
|
||||
ga-frontend/dist/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.css" />
|
||||
<title>GA 수수료 정산 솔루션</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Generated
+27
@@ -12,6 +12,7 @@
|
||||
"@ag-grid-community/core": "^31.3.2",
|
||||
"@ag-grid-community/react": "^31.3.2",
|
||||
"@ag-grid-community/styles": "^31.3.2",
|
||||
"@tabler/icons-react": "^3.5.0",
|
||||
"@tanstack/react-query": "^5.40.0",
|
||||
"antd": "^5.18.0",
|
||||
"axios": "^1.7.2",
|
||||
@@ -1462,6 +1463,32 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@tabler/icons": {
|
||||
"version": "3.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.44.0.tgz",
|
||||
"integrity": "sha512-Wn0AOZG9sg0L+bjfMqq4eNhC6pQjIrk94LvvWYNYkY8KH8wC3YILRzQlrnVJc4FUeMxH/AK97QsYCX35H3LndA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/codecalm"
|
||||
}
|
||||
},
|
||||
"node_modules/@tabler/icons-react": {
|
||||
"version": "3.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.44.0.tgz",
|
||||
"integrity": "sha512-8+rvzBbVm/1Z3sG3x7GUNAaxIKxwgz8xaMhRs23nrCnMTKRFAhEC+82zAIFeAA0seXdrAGX5HFCkaLpGK2rVHg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tabler/icons": "3.44.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/codecalm"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.100.9",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.100.9.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"@ag-grid-community/core": "^31.3.2",
|
||||
"@ag-grid-community/react": "^31.3.2",
|
||||
"@ag-grid-community/styles": "^31.3.2",
|
||||
"@tabler/icons-react": "^3.5.0",
|
||||
"@tanstack/react-query": "^5.40.0",
|
||||
"antd": "^5.18.0",
|
||||
"axios": "^1.7.2",
|
||||
|
||||
@@ -6,11 +6,31 @@ interface Props {
|
||||
value?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 09_UI_DESIGN.md §5 CodeBadge — 4단계 색상 (success/info/warning/danger/default).
|
||||
* antd Tag 의 preset 컬러 사용: success(초록) / processing(파랑) / warning(주황) / error(빨강) / default(회색).
|
||||
*/
|
||||
const COLOR_MAP: Record<string, string> = {
|
||||
ACTIVE: 'green', CONFIRMED: 'green', APPROVED: 'green', PAID: 'green', MATCHED: 'green', DONE: 'green',
|
||||
PENDING: 'blue', CALCULATED: 'blue', SENT: 'blue',
|
||||
HOLD: 'red', SUSPEND: 'red', REJECTED: 'red', FAIL: 'red', LAPSE: 'red', DIFF: 'red', LOCKED: 'red',
|
||||
DRAFT: 'default', NEW: 'default', LEAVE: 'default', RETIRED: 'default',
|
||||
// 초록 — 긍정적 완료
|
||||
CONFIRMED: 'success', APPROVED: 'success', ACTIVE: 'success',
|
||||
PAID: 'success', COMPLETED: 'success', MATCH: 'success',
|
||||
MATCHED: 'success', DONE: 'success', SUCCESS: 'success',
|
||||
|
||||
// 파랑 — 진행 중
|
||||
CALCULATED: 'processing', PENDING: 'processing', PROCESSING: 'processing',
|
||||
REVIEWED: 'processing', NEW: 'processing', SENT: 'processing',
|
||||
|
||||
// 주황 — 주의
|
||||
HOLD: 'warning', SUSPEND: 'warning', MISMATCH: 'warning',
|
||||
PARTIAL: 'warning', DIFF: 'warning',
|
||||
|
||||
// 빨강 — 부정적
|
||||
REJECTED: 'error', LEAVE: 'error', CANCEL: 'error',
|
||||
FAILED: 'error', FAIL: 'error', LAPSED: 'error',
|
||||
LAPSE: 'error', LOCKED: 'error',
|
||||
|
||||
// 회색 — 기본
|
||||
DRAFT: 'default', READY: 'default', RETIRED: 'default',
|
||||
};
|
||||
|
||||
export default function CodeBadge({ groupCode, value }: Props) {
|
||||
|
||||
@@ -8,6 +8,21 @@ import '@ag-grid-community/styles/ag-theme-quartz.css';
|
||||
|
||||
ModuleRegistry.registerModules([ClientSideRowModelModule]);
|
||||
|
||||
/**
|
||||
* AG Grid 한국어 로케일 (자주 쓰이는 항목 위주).
|
||||
* 전체 키 목록은 AG Grid 공식 문서 참조.
|
||||
*/
|
||||
const AG_GRID_LOCALE_KO: Record<string, string> = {
|
||||
page: '페이지', more: '더보기', to: '~', of: '/', next: '다음', last: '마지막',
|
||||
first: '처음', previous: '이전', loadingOoo: '불러오는 중...',
|
||||
selectAll: '전체 선택', searchOoo: '검색...', blanks: '(빈값)',
|
||||
filterOoo: '필터...', equals: '같음', notEqual: '같지않음',
|
||||
contains: '포함', notContains: '포함하지않음', startsWith: '시작',
|
||||
endsWith: '끝', noRowsToShow: '데이터가 없습니다', resetColumns: '컬럼 초기화',
|
||||
pinColumn: '컬럼 고정', valueAggregation: '집계',
|
||||
sum: '합계', min: '최소', max: '최대', count: '건수',
|
||||
};
|
||||
|
||||
interface Props<T> {
|
||||
rows: T[] | undefined;
|
||||
columns: ColDef<T>[];
|
||||
@@ -65,9 +80,18 @@ export default function DataGrid<T extends Record<string, unknown>>({
|
||||
columnDefs={columns}
|
||||
defaultColDef={defaultColDef}
|
||||
animateRows
|
||||
rowHeight={36}
|
||||
headerHeight={38}
|
||||
localeText={AG_GRID_LOCALE_KO}
|
||||
rowSelection="single"
|
||||
getRowId={rowKey ? (p) => String(p.data[rowKey]) : undefined}
|
||||
pinnedBottomRowData={pinnedBottomRow ? [pinnedBottomRow as T] : undefined}
|
||||
getRowStyle={(params) => {
|
||||
if (params.node.rowPinned) {
|
||||
return { background: '#fafafa', fontWeight: 500 };
|
||||
}
|
||||
return undefined;
|
||||
}}
|
||||
onCellValueChanged={(e) => {
|
||||
if (onCellChanged && e.data) {
|
||||
onCellChanged(e.data, String(e.colDef.field), e.newValue);
|
||||
|
||||
@@ -6,15 +6,30 @@ interface Props {
|
||||
fraction?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 09_UI_DESIGN.md §5 MoneyText — 양수 #0F6E56(초록), 음수 #E24B4A(빨강), 0 #888(회색).
|
||||
* 천 단위 콤마, tabular-nums 적용.
|
||||
*/
|
||||
export default function MoneyText({ value, showSign, fraction = 0 }: Props) {
|
||||
if (value === null || value === undefined || value === '') return <span>-</span>;
|
||||
const n = typeof value === 'string' ? Number(value) : value;
|
||||
if (Number.isNaN(n)) return <span>-</span>;
|
||||
|
||||
const formatted = n.toLocaleString(undefined, {
|
||||
minimumFractionDigits: fraction,
|
||||
maximumFractionDigits: fraction,
|
||||
});
|
||||
const sign = n < 0 ? '' : showSign ? '+' : '';
|
||||
const color = n < 0 ? '#cf1322' : n > 0 && showSign ? '#1677ff' : 'inherit';
|
||||
return <span style={{ color, fontVariantNumeric: 'tabular-nums' }}>{sign}{formatted}</span>;
|
||||
|
||||
let color: string;
|
||||
if (n > 0) color = 'var(--ga-money-plus)';
|
||||
else if (n < 0) color = 'var(--ga-money-minus)';
|
||||
else color = 'var(--ga-money-zero)';
|
||||
|
||||
const sign = n < 0 ? '' : showSign && n > 0 ? '+' : '';
|
||||
|
||||
return (
|
||||
<span style={{ color, fontVariantNumeric: 'tabular-nums' }}>
|
||||
{sign}{formatted}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function SearchForm({ conditions, initialValues, onSearch, onRese
|
||||
layout="vertical"
|
||||
initialValues={initialValues}
|
||||
onFinish={handleSearch}
|
||||
style={{ background: '#fff', padding: 16, marginBottom: 16, borderRadius: 4 }}
|
||||
style={{ background: '#fafafa', padding: '12px 16px', marginBottom: 16, borderRadius: 8 }}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
{conditions.map((c) => (
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
@import './styles/variables.css';
|
||||
|
||||
html, body, #root { height: 100%; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, "Apple SD Gothic Neo", "Malgun Gothic", "맑은 고딕", sans-serif;
|
||||
font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo",
|
||||
"Malgun Gothic", "맑은 고딕", system-ui, sans-serif;
|
||||
background: #f5f5f5;
|
||||
font-size: 13px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* 숫자 셀에 등폭 숫자 (정렬 일관) */
|
||||
.ga-tabular { font-variant-numeric: tabular-nums; }
|
||||
|
||||
/* 레이아웃 콘테이너 */
|
||||
.ga-page-container { max-width: var(--ga-page-max-width); margin: 0 auto; }
|
||||
|
||||
/* AG Grid 합계행 강조 */
|
||||
.ag-theme-quartz .ag-row-pinned {
|
||||
background: #fafafa !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { Layout, Menu, Dropdown, Badge, Space, Avatar, Button } from 'antd';
|
||||
import { useNavigate, Outlet } from 'react-router-dom';
|
||||
import { useState } from 'react';
|
||||
import { Layout, Menu, Dropdown, Badge, Space, Avatar, Button, DatePicker } from 'antd';
|
||||
import { useNavigate, Outlet, useLocation } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { BellOutlined, UserOutlined, LogoutOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
IconMenu2,
|
||||
IconBell,
|
||||
IconUser,
|
||||
IconLogout,
|
||||
} from '@tabler/icons-react';
|
||||
import dayjs from 'dayjs';
|
||||
import { menuApi, MenuResp } from '@/api/menu';
|
||||
import { authApi } from '@/api/auth';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
@@ -18,7 +25,10 @@ function toMenuItems(menus: MenuResp[]): any[] {
|
||||
|
||||
export default function MainLayout() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const auth = useAuthStore();
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [settleMonth, setSettleMonth] = useState(dayjs());
|
||||
|
||||
const { data: menus = [] } = useQuery({
|
||||
queryKey: ['menu', 'my'],
|
||||
@@ -35,36 +45,80 @@ export default function MainLayout() {
|
||||
|
||||
return (
|
||||
<Layout style={{ minHeight: '100vh' }}>
|
||||
<Header style={{ display: 'flex', justifyContent: 'space-between',
|
||||
background: '#001529', color: '#fff' }}>
|
||||
<div style={{ fontSize: 18, fontWeight: 600 }}>GA 수수료 정산 솔루션</div>
|
||||
<Space>
|
||||
<Badge count={0}>
|
||||
<Button type="text" icon={<BellOutlined style={{ color: '#fff' }} />} />
|
||||
<Header
|
||||
style={{
|
||||
height: 48,
|
||||
lineHeight: '48px',
|
||||
background: '#ffffff',
|
||||
borderBottom: '1px solid #f0f0f0',
|
||||
padding: '0 16px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
boxShadow: 'var(--ga-shadow-card)',
|
||||
}}
|
||||
>
|
||||
<Space size={12}>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<IconMenu2 size={18} />}
|
||||
onClick={() => setCollapsed((c) => !c)}
|
||||
/>
|
||||
<span style={{ fontSize: 15, fontWeight: 600, color: '#185FA5' }}>
|
||||
GA 정산시스템
|
||||
</span>
|
||||
</Space>
|
||||
<Space size={12}>
|
||||
<DatePicker
|
||||
picker="month"
|
||||
value={settleMonth}
|
||||
onChange={(d) => d && setSettleMonth(d)}
|
||||
allowClear={false}
|
||||
format="YYYY-MM"
|
||||
size="small"
|
||||
/>
|
||||
<Badge count={0} size="small">
|
||||
<Button type="text" icon={<IconBell size={18} />} />
|
||||
</Badge>
|
||||
<Dropdown menu={{
|
||||
items: [
|
||||
{ key: 'logout', label: '로그아웃', icon: <LogoutOutlined />, onClick: handleLogout },
|
||||
],
|
||||
}}>
|
||||
<Space style={{ color: '#fff', cursor: 'pointer' }}>
|
||||
<Avatar size="small" icon={<UserOutlined />} />
|
||||
{auth.userName ?? auth.loginId}
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: 'logout',
|
||||
label: '로그아웃',
|
||||
icon: <IconLogout size={14} />,
|
||||
onClick: handleLogout,
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
<Space style={{ cursor: 'pointer' }}>
|
||||
<Avatar size="small" icon={<IconUser size={14} />} />
|
||||
<span style={{ fontSize: 13 }}>{auth.userName ?? auth.loginId}</span>
|
||||
</Space>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
</Header>
|
||||
<Layout>
|
||||
<Sider width={240} theme="light">
|
||||
<Sider
|
||||
width={220}
|
||||
collapsedWidth={64}
|
||||
collapsed={collapsed}
|
||||
theme="light"
|
||||
style={{ borderRight: '1px solid #f0f0f0' }}
|
||||
>
|
||||
<Menu
|
||||
mode="inline"
|
||||
items={toMenuItems(menus)}
|
||||
selectedKeys={[location.pathname]}
|
||||
onClick={({ key }) => navigate(String(key))}
|
||||
style={{ borderRight: 0, height: '100%' }}
|
||||
style={{ borderRight: 0, height: '100%', paddingTop: 8 }}
|
||||
/>
|
||||
</Sider>
|
||||
<Content style={{ padding: 24 }}>
|
||||
<Outlet />
|
||||
<Content style={{ padding: 'var(--ga-content-padding)', overflow: 'auto' }}>
|
||||
<div className="ga-page-container">
|
||||
<Outlet />
|
||||
</div>
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { ConfigProvider } from 'antd';
|
||||
import { ConfigProvider, ThemeConfig } from 'antd';
|
||||
import koKR from 'antd/locale/ko_KR';
|
||||
import 'dayjs/locale/ko';
|
||||
import App from './App';
|
||||
@@ -12,9 +12,73 @@ const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: 1, staleTime: 30_000 } },
|
||||
});
|
||||
|
||||
const theme: ThemeConfig = {
|
||||
token: {
|
||||
// 브랜드
|
||||
colorPrimary: '#1677FF',
|
||||
colorSuccess: '#0F6E56',
|
||||
colorWarning: '#BA7517',
|
||||
colorError: '#E24B4A',
|
||||
colorInfo: '#1677FF',
|
||||
|
||||
// 폰트
|
||||
fontFamily: "'Pretendard', -apple-system, BlinkMacSystemFont, sans-serif",
|
||||
fontSize: 13,
|
||||
fontSizeHeading1: 22,
|
||||
fontSizeHeading2: 18,
|
||||
fontSizeHeading3: 16,
|
||||
|
||||
// 라운딩
|
||||
borderRadius: 6,
|
||||
borderRadiusLG: 8,
|
||||
|
||||
// 간격
|
||||
padding: 16,
|
||||
paddingLG: 24,
|
||||
margin: 16,
|
||||
|
||||
// 배경
|
||||
colorBgContainer: '#ffffff',
|
||||
colorBgLayout: '#f5f5f5',
|
||||
colorBgElevated: '#ffffff',
|
||||
},
|
||||
components: {
|
||||
Menu: {
|
||||
itemHeight: 40,
|
||||
itemMarginInline: 8,
|
||||
itemBorderRadius: 6,
|
||||
subMenuItemBg: 'transparent',
|
||||
itemSelectedBg: '#e6f1fb',
|
||||
itemSelectedColor: '#185fa5',
|
||||
},
|
||||
Table: {
|
||||
headerBg: '#fafafa',
|
||||
rowHoverBg: '#f5f7fa',
|
||||
cellPaddingBlock: 10,
|
||||
cellPaddingInline: 12,
|
||||
headerSplitColor: '#f0f0f0',
|
||||
},
|
||||
Button: {
|
||||
controlHeight: 32,
|
||||
controlHeightSM: 28,
|
||||
paddingInline: 16,
|
||||
},
|
||||
Input: { controlHeight: 32, paddingInline: 10 },
|
||||
Select: { controlHeight: 32 },
|
||||
Card: { paddingLG: 16 },
|
||||
Layout: {
|
||||
headerHeight: 48,
|
||||
headerBg: '#ffffff',
|
||||
headerPadding: '0 16px',
|
||||
siderBg: '#ffffff',
|
||||
bodyBg: '#f5f5f5',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<ConfigProvider locale={koKR}>
|
||||
<ConfigProvider locale={koKR} theme={theme}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
|
||||
@@ -6,6 +6,7 @@ import SearchForm from '@/components/common/SearchForm';
|
||||
import CodeBadge from '@/components/common/CodeBadge';
|
||||
import MoneyText from '@/components/common/MoneyText';
|
||||
import PermissionButton from '@/components/common/PermissionButton';
|
||||
import { defaultPagination } from '@/utils/tablePagination';
|
||||
import { exceptionApi, ExceptionLedgerResp } from '@/api/exceptionLedger';
|
||||
|
||||
export default function ExceptionLedger() {
|
||||
@@ -49,8 +50,8 @@ export default function ExceptionLedger() {
|
||||
size="small"
|
||||
scroll={{ x: 1400 }}
|
||||
pagination={{
|
||||
...defaultPagination,
|
||||
current: param.pageNum, pageSize: param.pageSize, total: data?.total,
|
||||
showSizeChanger: true,
|
||||
onChange: (p, s) => setParam({ ...param, pageNum: p, pageSize: s }),
|
||||
}}
|
||||
columns={[
|
||||
|
||||
@@ -6,6 +6,7 @@ import SearchForm from '@/components/common/SearchForm';
|
||||
import MoneyText from '@/components/common/MoneyText';
|
||||
import CodeBadge from '@/components/common/CodeBadge';
|
||||
import ExcelExportButton from '@/components/common/ExcelExportButton';
|
||||
import { defaultPagination } from '@/utils/tablePagination';
|
||||
import { ledgerApi, LedgerResp, LedgerSearchParam } from '@/api/ledger';
|
||||
|
||||
export default function RecruitLedger() {
|
||||
@@ -41,7 +42,8 @@ export default function RecruitLedger() {
|
||||
size="small"
|
||||
scroll={{ x: 1500 }}
|
||||
pagination={{
|
||||
current: param.pageNum, pageSize: param.pageSize, total: data?.total, showSizeChanger: true,
|
||||
...defaultPagination,
|
||||
current: param.pageNum, pageSize: param.pageSize, total: data?.total,
|
||||
onChange: (p, s) => setParam({ ...param, pageNum: p, pageSize: s }),
|
||||
}}
|
||||
columns={[
|
||||
|
||||
@@ -8,6 +8,7 @@ import CodeBadge from '@/components/common/CodeBadge';
|
||||
import MoneyText from '@/components/common/MoneyText';
|
||||
import PermissionButton from '@/components/common/PermissionButton';
|
||||
import ExcelExportButton from '@/components/common/ExcelExportButton';
|
||||
import { defaultPagination } from '@/utils/tablePagination';
|
||||
import { agentApi, AgentResp, AgentSearchParam } from '@/api/agent';
|
||||
|
||||
export default function AgentList() {
|
||||
@@ -46,10 +47,10 @@ export default function AgentList() {
|
||||
loading={isLoading}
|
||||
dataSource={data?.list}
|
||||
pagination={{
|
||||
...defaultPagination,
|
||||
current: param.pageNum,
|
||||
pageSize: param.pageSize,
|
||||
total: data?.total,
|
||||
showSizeChanger: true,
|
||||
onChange: (page, size) => setParam({ ...param, pageNum: page, pageSize: size }),
|
||||
}}
|
||||
onRow={(r) => ({ onClick: () => navigate(`/org/agents/${r.agentId}`) })}
|
||||
|
||||
@@ -5,6 +5,7 @@ import PageContainer from '@/components/common/PageContainer';
|
||||
import SearchForm from '@/components/common/SearchForm';
|
||||
import CodeBadge from '@/components/common/CodeBadge';
|
||||
import MoneyText from '@/components/common/MoneyText';
|
||||
import { defaultPagination } from '@/utils/tablePagination';
|
||||
import { contractApi, ContractResp, ContractSearchParam } from '@/api/contract';
|
||||
|
||||
export default function ContractList() {
|
||||
@@ -33,7 +34,8 @@ export default function ContractList() {
|
||||
loading={isLoading}
|
||||
dataSource={data?.list}
|
||||
pagination={{
|
||||
current: param.pageNum, pageSize: param.pageSize, total: data?.total, showSizeChanger: true,
|
||||
...defaultPagination,
|
||||
current: param.pageNum, pageSize: param.pageSize, total: data?.total,
|
||||
onChange: (p, s) => setParam({ ...param, pageNum: p, pageSize: s }),
|
||||
}}
|
||||
columns={[
|
||||
|
||||
@@ -5,6 +5,7 @@ import PageContainer from '@/components/common/PageContainer';
|
||||
import SearchForm from '@/components/common/SearchForm';
|
||||
import CodeBadge from '@/components/common/CodeBadge';
|
||||
import MoneyText from '@/components/common/MoneyText';
|
||||
import { defaultPagination } from '@/utils/tablePagination';
|
||||
import { paymentApi, PaymentResp } from '@/api/payment';
|
||||
|
||||
export default function PaymentList() {
|
||||
@@ -34,8 +35,8 @@ export default function PaymentList() {
|
||||
dataSource={data?.list}
|
||||
size="small"
|
||||
pagination={{
|
||||
...defaultPagination,
|
||||
current: param.pageNum, pageSize: param.pageSize, total: data?.total,
|
||||
showSizeChanger: true,
|
||||
onChange: (p, s) => setParam({ ...param, pageNum: p, pageSize: s }),
|
||||
}}
|
||||
columns={[
|
||||
|
||||
@@ -6,6 +6,7 @@ import SearchForm from '@/components/common/SearchForm';
|
||||
import CodeBadge from '@/components/common/CodeBadge';
|
||||
import MoneyText from '@/components/common/MoneyText';
|
||||
import PermissionButton from '@/components/common/PermissionButton';
|
||||
import { defaultPagination } from '@/utils/tablePagination';
|
||||
import { settleApi, SettleResp, SettleSearchParam } from '@/api/settle';
|
||||
|
||||
export default function SettleList() {
|
||||
@@ -67,7 +68,8 @@ export default function SettleList() {
|
||||
size="small"
|
||||
scroll={{ x: 1600 }}
|
||||
pagination={{
|
||||
current: param.pageNum, pageSize: param.pageSize, total: data?.total, showSizeChanger: true,
|
||||
...defaultPagination,
|
||||
current: param.pageNum, pageSize: param.pageSize, total: data?.total,
|
||||
onChange: (p, s) => setParam({ ...param, pageNum: p, pageSize: s }),
|
||||
}}
|
||||
columns={[
|
||||
|
||||
@@ -5,6 +5,7 @@ import PageContainer from '@/components/common/PageContainer';
|
||||
import SearchForm from '@/components/common/SearchForm';
|
||||
import CodeBadge from '@/components/common/CodeBadge';
|
||||
import PermissionButton from '@/components/common/PermissionButton';
|
||||
import { defaultPagination } from '@/utils/tablePagination';
|
||||
import { userApi, UserResp } from '@/api/user';
|
||||
|
||||
export default function UserList() {
|
||||
@@ -49,7 +50,8 @@ export default function UserList() {
|
||||
loading={isLoading}
|
||||
dataSource={data?.list}
|
||||
pagination={{
|
||||
current: param.pageNum, pageSize: param.pageSize, total: data?.total, showSizeChanger: true,
|
||||
...defaultPagination,
|
||||
current: param.pageNum, pageSize: param.pageSize, total: data?.total,
|
||||
onChange: (p, s) => setParam({ ...param, pageNum: p, pageSize: s }),
|
||||
}}
|
||||
columns={[
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
:root {
|
||||
/* 브랜드 */
|
||||
--ga-primary: #1677FF;
|
||||
--ga-primary-light: #E6F1FB;
|
||||
--ga-primary-dark: #0C447C;
|
||||
|
||||
/* 상태 색상 (CodeBadge 등) */
|
||||
--ga-success: #0F6E56;
|
||||
--ga-success-bg: #E1F5EE;
|
||||
--ga-warning: #BA7517;
|
||||
--ga-warning-bg: #FAEEDA;
|
||||
--ga-danger: #E24B4A;
|
||||
--ga-danger-bg: #FCEBEB;
|
||||
--ga-info: #185FA5;
|
||||
--ga-info-bg: #E6F1FB;
|
||||
|
||||
/* 금액 */
|
||||
--ga-money-plus: #0F6E56;
|
||||
--ga-money-minus: #E24B4A;
|
||||
--ga-money-zero: #888888;
|
||||
|
||||
/* 레이아웃 */
|
||||
--ga-sider-width: 220px;
|
||||
--ga-sider-collapsed-width: 64px;
|
||||
--ga-header-height: 48px;
|
||||
--ga-content-padding: 20px;
|
||||
--ga-page-max-width: 1400px;
|
||||
|
||||
/* 간격 단위 */
|
||||
--ga-spacing-xs: 4px;
|
||||
--ga-spacing-sm: 8px;
|
||||
--ga-spacing-md: 12px;
|
||||
--ga-spacing-lg: 16px;
|
||||
--ga-spacing-xl: 24px;
|
||||
|
||||
/* 그림자 (최소한만 사용) */
|
||||
--ga-shadow-card: 0 1px 2px rgba(0, 0, 0, 0.03);
|
||||
--ga-shadow-dropdown: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { TablePaginationConfig } from 'antd';
|
||||
|
||||
/**
|
||||
* 09_UI_DESIGN.md §5 DataTable — 표준 페이지네이션 설정.
|
||||
* 사용: pagination={{ ...defaultPagination, current, pageSize, total, onChange }}
|
||||
*/
|
||||
export const defaultPagination: TablePaginationConfig = {
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ['20', '50', '100'],
|
||||
defaultPageSize: 20,
|
||||
showTotal: (total) => `총 ${total.toLocaleString()}건`,
|
||||
};
|
||||
Reference in New Issue
Block a user