feat(WEB): 리스트 페이지 UI 레이아웃 표준화

- 공통 레이아웃 패턴 적용: [달력] → [프리셋] → [검색창] → [버튼들]
- beforeTableContent → headerActions + createButton 마이그레이션
- DateRangeSelector extraActions prop 활용하여 검색창 통합
- PricingListClient 테이블 행 클릭 → 상세 이동 기능 추가
- 회계 관련 페이지 (입금/출금/매입/매출/어음/카드/예상지출 등) 정리
- 건설 관련 페이지 검색 영역 정리
- 부모 메뉴 리다이렉트 컴포넌트 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-26 22:04:36 +09:00
parent ff93ab7fa2
commit 1f6b592b9f
65 changed files with 1974 additions and 503 deletions

View File

@@ -73,14 +73,13 @@ import { toast } from 'sonner';
// ===== 테이블 컬럼 정의 =====
const tableColumns = [
{ key: 'withdrawalDate', label: '출금일' },
{ key: 'accountName', label: '출금계좌' },
{ key: 'recipientName', label: '수취인명' },
{ key: 'withdrawalAmount', label: '출금금액', className: 'text-right' },
{ key: 'vendorName', label: '거래처' },
{ key: 'note', label: '적요' },
{ key: 'withdrawalType', label: '출금유형', className: 'text-center' },
{ key: 'actions', label: '작업', className: 'text-center w-[80px]' },
{ key: 'withdrawalDate', label: '출금일', className: 'w-[100px]' },
{ key: 'accountName', label: '출금계좌', className: 'min-w-[120px]' },
{ key: 'recipientName', label: '수취인명', className: 'min-w-[100px]' },
{ key: 'withdrawalAmount', label: '출금금액', className: 'text-right w-[110px]' },
{ key: 'vendorName', label: '거래처', className: 'min-w-[100px]' },
{ key: 'note', label: '적요', className: 'min-w-[150px]' },
{ key: 'withdrawalType', label: '출금유형', className: 'text-center w-[90px]' },
];
// ===== 컴포넌트 Props =====
@@ -112,6 +111,9 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
// 상단 계정과목명 선택 (저장용)
const [selectedAccountSubject, setSelectedAccountSubject] = useState<string>('unset');
// 검색어 상태 (헤더에서 직접 관리)
const [searchQuery, setSearchQuery] = useState('');
// 로딩 상태
const [isRefreshing, setIsRefreshing] = useState(false);
@@ -297,17 +299,23 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
},
filterTitle: '출금 필터',
// 헤더 액션 (등록 버튼)
headerActions: () => (
<Button className="ml-auto" onClick={() => router.push('/ko/accounting/withdrawals?mode=new')}>
<Plus className="w-4 h-4 mr-2" />
</Button>
),
// 검색창 숨김 (dateRangeSelector extraActions로 렌더링)
hideSearch: true,
// 커스텀 필터 함수
// 커스텀 필터 함수 (검색 + 필터)
customFilterFn: (items) => {
return items.filter((item) => {
// 검색어 필터
if (searchQuery) {
const search = searchQuery.toLowerCase();
const matchesSearch =
item.recipientName.toLowerCase().includes(search) ||
item.accountName.toLowerCase().includes(search) ||
item.note.toLowerCase().includes(search) ||
item.vendorName.toLowerCase().includes(search);
if (!matchesSearch) return false;
}
// 거래처 필터
if (vendorFilter !== 'all' && item.vendorName !== vendorFilter) {
return false;
@@ -342,23 +350,30 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
return sorted;
},
// 날짜 범위 선택기
// 검색창 (공통 컴포넌트에서 자동 생성)
hideSearch: true,
searchValue: searchQuery,
onSearchChange: setSearchQuery,
// 날짜 범위 선택기 (달력 | 프리셋버튼 | 검색창(자동) - 한 줄)
dateRangeSelector: {
enabled: true,
showPresets: true,
startDate,
endDate,
onStartDateChange: setStartDate,
onEndDateChange: setEndDate,
},
// beforeTableContent: 계정과목명 + 저장 + 새로고침
beforeTableContent: (
<div className="flex items-center justify-between w-full">
// 헤더 액션: 계정과목명 Select + 저장 + 새로고침
headerActions: ({ selectedItems }) => {
const selectedArray = withdrawalData.filter(item => selectedItems.has(item.id));
return (
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-700"></span>
<span className="text-sm font-medium text-gray-700 whitespace-nowrap"></span>
<Select value={selectedAccountSubject} onValueChange={setSelectedAccountSubject}>
<SelectTrigger className="w-[150px]">
<SelectValue placeholder="계정과목명 선택" />
<SelectTrigger className="w-[120px]">
<SelectValue placeholder="선택" />
</SelectTrigger>
<SelectContent>
{ACCOUNT_SUBJECT_OPTIONS.map((option) => (
@@ -368,29 +383,33 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
))}
</SelectContent>
</Select>
<Button onClick={() => handleSaveAccountSubject(selectedArray)} size="sm">
<Save className="h-4 w-4 mr-1" />
</Button>
<Button
variant="outline"
size="sm"
onClick={handleRefresh}
disabled={isRefreshing}
>
<RefreshCw className={`h-4 w-4 mr-1 ${isRefreshing ? 'animate-spin' : ''}`} />
{isRefreshing ? '조회중...' : '새로고침'}
</Button>
</div>
<Button
variant="outline"
onClick={handleRefresh}
disabled={isRefreshing}
>
<RefreshCw className={`h-4 w-4 mr-2 ${isRefreshing ? 'animate-spin' : ''}`} />
{isRefreshing ? '조회중...' : '새로고침'}
</Button>
</div>
),
);
},
// tableHeaderActions: 저장 버튼 + 인라인 필터들
tableHeaderActions: ({ selectedItems }) => (
// 등록 버튼
createButton: {
label: '출금등록',
icon: Plus,
onClick: () => router.push('/ko/accounting/withdrawals?mode=new'),
},
// tableHeaderActions: 필터만 (거래처, 출금유형, 정렬)
tableHeaderActions: () => (
<div className="flex items-center gap-2 flex-wrap">
<Button
onClick={() => handleSaveAccountSubject(selectedItems)}
className="bg-blue-500 hover:bg-blue-600"
>
<Save className="h-4 w-4 mr-2" />
</Button>
{/* 거래처 필터 */}
<Select value={vendorFilter} onValueChange={setVendorFilter}>
<SelectTrigger className="w-[140px]">
@@ -446,7 +465,6 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
</TableRow>
),
@@ -506,29 +524,6 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
{WITHDRAWAL_TYPE_LABELS[item.withdrawalType]}
</Badge>
</TableCell>
{/* 작업 */}
<TableCell className="text-center" onClick={(e) => e.stopPropagation()}>
{handlers.isSelected && (
<div className="flex items-center justify-center gap-1">
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-gray-600 hover:text-gray-700 hover:bg-gray-50"
onClick={() => handleEdit(item)}
>
<Pencil className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-red-500 hover:text-red-600 hover:bg-red-50"
onClick={() => handlers.onDelete?.(item)}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
)}
</TableCell>
</TableRow>
);
},
@@ -576,9 +571,11 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
}),
[
initialData,
withdrawalData,
stats,
startDate,
endDate,
searchQuery,
vendorFilter,
withdrawalTypeFilter,
sortOption,