refactor: 리스트 컴포넌트 UI 및 레이아웃 일관성 개선
- 여러 관리 페이지(영업, 회계, 인사, 결재, 게시판, 설정)의 리스트 UI 통일 - IntegratedListTemplateV2 기반 레이아웃 정리 - PricingHistoryDialog 개선 - 공통 컴포넌트 추출 계획 문서 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -307,18 +307,18 @@ export function BillManagementClient({
|
||||
|
||||
// ===== 헤더 액션 =====
|
||||
const headerActions = (
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
extraActions={
|
||||
<Button onClick={() => router.push('/ko/accounting/bills/new')}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
어음 등록
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
/>
|
||||
<Button className="ml-auto" onClick={() => router.push('/ko/accounting/bills/new')}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
어음 등록
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
|
||||
// ===== 거래처 목록 (필터용) =====
|
||||
|
||||
@@ -452,18 +452,18 @@ export function SalesManagement() {
|
||||
|
||||
// ===== 헤더 액션 =====
|
||||
const headerActions = (
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
extraActions={
|
||||
<Button onClick={handleNewSales}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
매출 등록
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
/>
|
||||
<Button className="ml-auto" onClick={handleNewSales}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
매출 등록
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
|
||||
// ===== 계정과목명 저장 핸들러 =====
|
||||
|
||||
@@ -276,22 +276,23 @@ export function VendorLedger() {
|
||||
|
||||
// ===== 헤더 액션 =====
|
||||
const headerActions = (
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
extraActions={
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleExcelDownload}
|
||||
>
|
||||
<Download className="mr-2 h-4 w-4" />
|
||||
엑셀 다운로드
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
/>
|
||||
<Button
|
||||
className="ml-auto"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleExcelDownload}
|
||||
>
|
||||
<Download className="mr-2 h-4 w-4" />
|
||||
엑셀 다운로드
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
|
||||
// ===== 테이블 하단 합계 행 =====
|
||||
|
||||
@@ -463,26 +463,26 @@ export function ApprovalBox() {
|
||||
|
||||
// ===== 헤더 액션 (DateRangeSelector + 승인/반려 버튼) =====
|
||||
const headerActions = (
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
extraActions={
|
||||
selectedItems.size > 0 && (
|
||||
<>
|
||||
<Button variant="default" onClick={handleApproveClick}>
|
||||
<Check className="h-4 w-4 mr-2" />
|
||||
승인
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={handleRejectClick}>
|
||||
<X className="h-4 w-4 mr-2" />
|
||||
반려
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
/>
|
||||
{selectedItems.size > 0 && (
|
||||
<div className="ml-auto flex gap-2">
|
||||
<Button variant="default" onClick={handleApproveClick}>
|
||||
<Check className="h-4 w-4 mr-2" />
|
||||
승인
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={handleRejectClick}>
|
||||
<X className="h-4 w-4 mr-2" />
|
||||
반려
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
// ===== 테이블 헤더 액션 (필터 + 정렬 셀렉트) =====
|
||||
|
||||
@@ -443,32 +443,32 @@ export function DraftBox() {
|
||||
|
||||
// ===== 헤더 액션 (DateRangeSelector + 버튼들) =====
|
||||
const headerActions = (
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
extraActions={
|
||||
<>
|
||||
{selectedItems.size > 0 && (
|
||||
<>
|
||||
<Button variant="default" onClick={handleSubmit}>
|
||||
<Send className="h-4 w-4 mr-2" />
|
||||
상신
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={handleDelete}>
|
||||
<Trash2 className="h-4 w-4 mr-2" />
|
||||
삭제
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button onClick={handleNewDocument}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
문서 작성
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
/>
|
||||
<div className="ml-auto flex gap-2">
|
||||
{selectedItems.size > 0 && (
|
||||
<>
|
||||
<Button variant="default" onClick={handleSubmit}>
|
||||
<Send className="h-4 w-4 mr-2" />
|
||||
상신
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={handleDelete}>
|
||||
<Trash2 className="h-4 w-4 mr-2" />
|
||||
삭제
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button onClick={handleNewDocument}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
문서 작성
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
// ===== 테이블 헤더 액션 (필터 + 정렬 셀렉트) =====
|
||||
|
||||
@@ -372,26 +372,26 @@ export function ReferenceBox() {
|
||||
|
||||
// ===== 헤더 액션 (DateRangeSelector + 열람/미열람 버튼) =====
|
||||
const headerActions = (
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
extraActions={
|
||||
selectedItems.size > 0 && (
|
||||
<>
|
||||
<Button variant="default" onClick={handleMarkReadClick}>
|
||||
<Eye className="h-4 w-4 mr-2" />
|
||||
열람
|
||||
</Button>
|
||||
<Button variant="outline" onClick={handleMarkUnreadClick}>
|
||||
<EyeOff className="h-4 w-4 mr-2" />
|
||||
미열람
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
/>
|
||||
{selectedItems.size > 0 && (
|
||||
<div className="ml-auto flex gap-2">
|
||||
<Button variant="default" onClick={handleMarkReadClick}>
|
||||
<Eye className="h-4 w-4 mr-2" />
|
||||
열람
|
||||
</Button>
|
||||
<Button variant="outline" onClick={handleMarkUnreadClick}>
|
||||
<EyeOff className="h-4 w-4 mr-2" />
|
||||
미열람
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
// ===== 테이블 헤더 액션 (필터 + 정렬 셀렉트) =====
|
||||
|
||||
@@ -378,18 +378,18 @@ export function BoardList() {
|
||||
|
||||
// ===== 헤더 액션 =====
|
||||
const headerActions = (
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
extraActions={
|
||||
<Button onClick={handleNewPost}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
게시글 등록
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
/>
|
||||
<Button className="ml-auto" onClick={handleNewPost}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
게시글 등록
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
|
||||
// ===== 테이블 헤더 액션 (필터 1개만: 게시판) =====
|
||||
|
||||
@@ -396,7 +396,7 @@ export function BoardManagement() {
|
||||
|
||||
// 헤더 액션
|
||||
const headerActions = (
|
||||
<Button onClick={handleAddBoard}>
|
||||
<Button className="ml-auto" onClick={handleAddBoard}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
게시판 등록
|
||||
</Button>
|
||||
|
||||
@@ -254,18 +254,18 @@ export function InquiryList() {
|
||||
description="1:1 문의를 등록하고 답변을 확인합니다."
|
||||
icon={MessageSquare}
|
||||
headerActions={
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<>
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
/>
|
||||
<Button onClick={handleCreate}>
|
||||
<Button className="ml-auto" onClick={handleCreate}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
문의 등록
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={setSearchValue}
|
||||
|
||||
@@ -426,24 +426,24 @@ export function AttendanceManagement() {
|
||||
|
||||
// 헤더 액션 (DateRangeSelector + 버튼들)
|
||||
const headerActions = (
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
extraActions={
|
||||
<>
|
||||
<Button variant="outline" onClick={handleExcelDownload}>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
엑셀 다운로드
|
||||
</Button>
|
||||
<Button onClick={handleAddAttendance}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
근태 등록
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
/>
|
||||
<div className="ml-auto flex gap-2">
|
||||
<Button variant="outline" onClick={handleExcelDownload}>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
엑셀 다운로드
|
||||
</Button>
|
||||
<Button onClick={handleAddAttendance}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
근태 등록
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
// 테이블 헤더 액션 (필터 + 정렬 셀렉트) - 사원관리와 동일한 위치
|
||||
|
||||
@@ -390,7 +390,7 @@ export function CardManagement() {
|
||||
|
||||
// 헤더 액션
|
||||
const headerActions = (
|
||||
<Button onClick={handleAddCard}>
|
||||
<Button className="ml-auto" onClick={handleAddCard}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
카드 등록
|
||||
</Button>
|
||||
|
||||
@@ -580,31 +580,31 @@ export function EmployeeManagement() {
|
||||
|
||||
// 헤더 액션 (DateRangeSelector + 버튼들)
|
||||
const headerActions = (
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
extraActions={
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setUserInviteOpen(true)}
|
||||
>
|
||||
<Mail className="w-4 h-4 mr-2" />
|
||||
사용자 초대
|
||||
</Button>
|
||||
<Button variant="outline" onClick={handleCSVUpload}>
|
||||
<Upload className="w-4 h-4 mr-2" />
|
||||
CSV 일괄 등록
|
||||
</Button>
|
||||
<Button onClick={handleAddEmployee}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
사원 등록
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
/>
|
||||
<div className="ml-auto flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setUserInviteOpen(true)}
|
||||
>
|
||||
<Mail className="w-4 h-4 mr-2" />
|
||||
사용자 초대
|
||||
</Button>
|
||||
<Button variant="outline" onClick={handleCSVUpload}>
|
||||
<Upload className="w-4 h-4 mr-2" />
|
||||
CSV 일괄 등록
|
||||
</Button>
|
||||
<Button onClick={handleAddEmployee}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
사원 등록
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
// 테이블 헤더 액션 (필터/정렬 셀렉트박스)
|
||||
|
||||
@@ -501,51 +501,51 @@ export function VacationManagement() {
|
||||
|
||||
// ===== 헤더 액션 (DateRangeSelector + 버튼들) =====
|
||||
const headerActions = (
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
extraActions={
|
||||
<>
|
||||
{/* 탭별 액션 버튼 */}
|
||||
{mainTab === 'grant' && (
|
||||
<Button onClick={() => setGrantDialogOpen(true)}>
|
||||
<>
|
||||
<DateRangeSelector
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onStartDateChange={setStartDate}
|
||||
onEndDateChange={setEndDate}
|
||||
/>
|
||||
<div className="ml-auto flex gap-2">
|
||||
{/* 탭별 액션 버튼 */}
|
||||
{mainTab === 'grant' && (
|
||||
<Button onClick={() => setGrantDialogOpen(true)}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
부여등록
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{mainTab === 'request' && (
|
||||
<>
|
||||
{/* 버튼 순서: 승인 → 거절 → 휴가신청 (휴가신청 버튼 위치 고정) */}
|
||||
{selectedItems.size > 0 && (
|
||||
<>
|
||||
<Button variant="default" onClick={handleApproveClick}>
|
||||
<Check className="h-4 w-4 mr-2" />
|
||||
승인
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={handleRejectClick}>
|
||||
<X className="h-4 w-4 mr-2" />
|
||||
거절
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button onClick={() => setRequestDialogOpen(true)}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
부여등록
|
||||
휴가신청
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{mainTab === 'request' && (
|
||||
<>
|
||||
{/* 버튼 순서: 승인 → 거절 → 휴가신청 (휴가신청 버튼 위치 고정) */}
|
||||
{selectedItems.size > 0 && (
|
||||
<>
|
||||
<Button variant="default" onClick={handleApproveClick}>
|
||||
<Check className="h-4 w-4 mr-2" />
|
||||
승인
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={handleRejectClick}>
|
||||
<X className="h-4 w-4 mr-2" />
|
||||
거절
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button onClick={() => setRequestDialogOpen(true)}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
휴가신청
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 엑셀 다운로드 버튼 - 주석처리 */}
|
||||
{/* <Button variant="outline" onClick={() => console.log('엑셀 다운로드')}>
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
엑셀 다운로드
|
||||
</Button> */}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
{/* 엑셀 다운로드 버튼 - 주석처리 */}
|
||||
{/* <Button variant="outline" onClick={() => console.log('엑셀 다운로드')}>
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
엑셀 다운로드
|
||||
</Button> */}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
// ===== 테이블 헤더 액션 (필터 + 정렬 셀렉트) - 사원관리와 동일한 위치 =====
|
||||
|
||||
@@ -117,25 +117,25 @@ export function PricingHistoryDialog({
|
||||
<div>
|
||||
<span className="text-muted-foreground">매입단가:</span>
|
||||
<div>
|
||||
{revision.previousData.purchasePrice?.toLocaleString() || '-'}원
|
||||
{revision.previousData?.purchasePrice?.toLocaleString() || '-'}원
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">가공비:</span>
|
||||
<div>
|
||||
{revision.previousData.processingCost?.toLocaleString() || '-'}원
|
||||
{revision.previousData?.processingCost?.toLocaleString() || '-'}원
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">판매단가:</span>
|
||||
<div>
|
||||
{revision.previousData.salesPrice?.toLocaleString() || '-'}원
|
||||
{revision.previousData?.salesPrice?.toLocaleString() || '-'}원
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">마진율:</span>
|
||||
<div>
|
||||
{revision.previousData.marginRate?.toFixed(1) || '-'}%
|
||||
{revision.previousData?.marginRate?.toFixed(1) || '-'}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -403,7 +403,7 @@ export function PricingListClient({
|
||||
// TODO: API 연동 시 품목 마스터 동기화 로직 구현
|
||||
console.log('품목 마스터 동기화');
|
||||
}}
|
||||
className="gap-2"
|
||||
className="ml-auto gap-2"
|
||||
>
|
||||
<RefreshCw className="h-4 w-4" />
|
||||
품목 마스터 동기화
|
||||
|
||||
@@ -294,7 +294,7 @@ export function AccountManagement() {
|
||||
|
||||
// ===== 헤더 액션 (카드관리와 동일한 패턴) =====
|
||||
const headerActions = (
|
||||
<Button onClick={handleCreate}>
|
||||
<Button className="ml-auto" onClick={handleCreate}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
계좌 등록
|
||||
</Button>
|
||||
|
||||
@@ -411,7 +411,7 @@ export function PermissionManagement() {
|
||||
|
||||
// ===== 헤더 액션 =====
|
||||
const headerActions = (
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<div className="flex items-center gap-2 flex-wrap ml-auto">
|
||||
{selectedItems.size > 0 && (
|
||||
<Button variant="destructive" onClick={handleBulkDelete}>
|
||||
<Trash2 className="h-4 w-4 mr-2" />
|
||||
|
||||
@@ -286,7 +286,7 @@ export function PopupList() {
|
||||
description="팝업 목록을 관리합니다."
|
||||
icon={Megaphone}
|
||||
headerActions={
|
||||
<Button onClick={handleCreate}>
|
||||
<Button className="ml-auto" onClick={handleCreate}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
팝업 등록
|
||||
</Button>
|
||||
|
||||
@@ -228,7 +228,7 @@ export function IntegratedListTemplateV2<T = any>({
|
||||
|
||||
{/* 헤더 액션 (달력, 버튼 등) - 타이틀 아래 배치 */}
|
||||
{headerActions && (
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<div className="flex items-center gap-2 flex-wrap w-full">
|
||||
{headerActions}
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user