refactor: [ecard] 카드사용내역 상단 UI를 계좌입출금내역 스타일로 통일
- x-barobill-tenant-header 보라색 그라디언트 Blade 컴포넌트 제거 - StatCard 4칸 그리드 → CompactStat 인라인 배지로 변경 - CardSelector 버튼 그룹 → select 드롭다운으로 변경 - 날짜 필터/액션 버튼을 TransactionTable에서 App 레벨로 이동 - border-stone-100 → border-gray-200로 통일
This commit is contained in:
@@ -3,16 +3,6 @@
|
||||
@section('title', '카드 사용내역')
|
||||
|
||||
@section('content')
|
||||
<!-- 현재 테넌트 정보 카드 (React 외부) -->
|
||||
<x-barobill-tenant-header
|
||||
:currentTenant="$currentTenant"
|
||||
:barobillMember="$barobillMember"
|
||||
:isTestMode="$isTestMode"
|
||||
gradientFrom="#7c3aed"
|
||||
gradientTo="#8b5cf6"
|
||||
:icon="'<svg class="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" /></svg>'"
|
||||
/>
|
||||
|
||||
<div id="ecard-root"></div>
|
||||
@endsection
|
||||
|
||||
@@ -80,57 +70,22 @@
|
||||
}
|
||||
};
|
||||
|
||||
// StatCard Component
|
||||
const StatCard = ({ title, value, subtext, icon, color = 'purple' }) => {
|
||||
// CompactStat Component (계좌입출금 스타일 통계 배지)
|
||||
const CompactStat = ({ label, value, color = 'stone' }) => {
|
||||
const colorClasses = {
|
||||
purple: 'bg-purple-50 text-purple-600',
|
||||
green: 'bg-green-50 text-green-600',
|
||||
red: 'bg-red-50 text-red-600',
|
||||
stone: 'bg-stone-50 text-stone-600'
|
||||
purple: 'text-purple-600',
|
||||
green: 'text-green-600',
|
||||
red: 'text-red-600',
|
||||
stone: 'text-stone-700'
|
||||
};
|
||||
return (
|
||||
<div className="bg-white rounded-xl p-6 shadow-sm border border-stone-100 hover:shadow-md transition-shadow">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<h3 className="text-sm font-medium text-stone-500">{title}</h3>
|
||||
<div className={`p-2 rounded-lg ${colorClasses[color] || colorClasses.purple}`}>
|
||||
{icon}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-2xl font-bold text-stone-900 mb-1">{value}</div>
|
||||
{subtext && <div className="text-xs text-stone-400">{subtext}</div>}
|
||||
<div className="flex items-center gap-3 px-6 py-4 bg-white rounded-xl border border-gray-200 shadow-sm">
|
||||
<span className="text-base text-stone-500 font-medium">{label}</span>
|
||||
<span className={`text-xl font-bold ${colorClasses[color]}`}>{value}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// CardSelector Component
|
||||
const CardSelector = ({ cards, selectedCard, onSelect }) => (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button
|
||||
onClick={() => onSelect('')}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
selectedCard === ''
|
||||
? 'bg-purple-600 text-white'
|
||||
: 'bg-white border border-stone-200 text-stone-700 hover:bg-stone-50'
|
||||
}`}
|
||||
>
|
||||
전체 카드
|
||||
</button>
|
||||
{cards.map(card => (
|
||||
<button
|
||||
key={card.cardNum}
|
||||
onClick={() => onSelect(card.cardNum)}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
selectedCard === card.cardNum
|
||||
? 'bg-purple-600 text-white'
|
||||
: 'bg-white border border-stone-200 text-stone-700 hover:bg-stone-50'
|
||||
}`}
|
||||
>
|
||||
{card.cardBrand} {card.cardNum ? '****' + card.cardNum.slice(-4) : ''}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
// AccountCodeSelect Component (검색 가능한 드롭다운)
|
||||
const AccountCodeSelect = ({ value, onChange, accountCodes }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
@@ -1010,32 +965,18 @@ className="flex-1 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 t
|
||||
const TransactionTable = ({
|
||||
logs,
|
||||
loading,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
onDateFromChange,
|
||||
onDateToChange,
|
||||
onThisMonth,
|
||||
onLastMonth,
|
||||
onMonthOffset,
|
||||
onSearch,
|
||||
totalCount,
|
||||
accountCodes,
|
||||
onAccountCodeChange,
|
||||
onFieldChange,
|
||||
onSave,
|
||||
onExport,
|
||||
saving,
|
||||
hasChanges,
|
||||
splits,
|
||||
onOpenSplitModal,
|
||||
onDeleteSplits,
|
||||
onManualNew,
|
||||
onManualEdit,
|
||||
onManualDelete,
|
||||
onHide,
|
||||
showHidden,
|
||||
hiddenLogs,
|
||||
onToggleHidden,
|
||||
onRestore,
|
||||
loadingHidden,
|
||||
}) => {
|
||||
@@ -1050,143 +991,7 @@ className="flex-1 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 t
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-xl shadow-sm border border-stone-100 overflow-hidden min-h-[calc(100vh-200px)]">
|
||||
<div className="p-6 border-b border-stone-100">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
|
||||
<h2 className="text-lg font-bold text-stone-900">카드 사용내역</h2>
|
||||
{/* 기간 조회 필터 */}
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm text-stone-500">기간</label>
|
||||
<input
|
||||
type="date"
|
||||
value={dateFrom}
|
||||
onChange={(e) => onDateFromChange(e.target.value)}
|
||||
className="rounded-lg border border-stone-200 px-3 py-1.5 text-sm focus:ring-2 focus:ring-purple-500 outline-none"
|
||||
/>
|
||||
<span className="text-stone-400">~</span>
|
||||
<input
|
||||
type="date"
|
||||
value={dateTo}
|
||||
onChange={(e) => onDateToChange(e.target.value)}
|
||||
className="rounded-lg border border-stone-200 px-3 py-1.5 text-sm focus:ring-2 focus:ring-purple-500 outline-none"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={onThisMonth}
|
||||
className="px-3 py-1.5 text-sm bg-purple-50 text-purple-600 rounded-lg hover:bg-purple-100 transition-colors font-medium"
|
||||
>
|
||||
이번 달
|
||||
</button>
|
||||
<button
|
||||
onClick={onLastMonth}
|
||||
className="px-3 py-1.5 text-sm bg-stone-100 text-stone-600 rounded-lg hover:bg-stone-200 transition-colors font-medium"
|
||||
>
|
||||
지난달
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onMonthOffset(-2)}
|
||||
className="px-3 py-1.5 text-sm bg-stone-100 text-stone-600 rounded-lg hover:bg-stone-200 transition-colors font-medium"
|
||||
>
|
||||
D-2월
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onMonthOffset(-3)}
|
||||
className="px-3 py-1.5 text-sm bg-stone-100 text-stone-600 rounded-lg hover:bg-stone-200 transition-colors font-medium"
|
||||
>
|
||||
D-3월
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onMonthOffset(-4)}
|
||||
className="px-3 py-1.5 text-sm bg-stone-100 text-stone-600 rounded-lg hover:bg-stone-200 transition-colors font-medium"
|
||||
>
|
||||
D-4월
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onMonthOffset(-5)}
|
||||
className="px-3 py-1.5 text-sm bg-stone-100 text-stone-600 rounded-lg hover:bg-stone-200 transition-colors font-medium"
|
||||
>
|
||||
D-5월
|
||||
</button>
|
||||
<button
|
||||
onClick={onSearch}
|
||||
className="px-4 py-1.5 text-sm bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors font-medium flex items-center gap-2"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
조회
|
||||
</button>
|
||||
</div>
|
||||
<span className="text-sm text-stone-500 ml-2">
|
||||
조회: <span className="font-semibold text-stone-700">{logs.length}</span>건
|
||||
{totalCount !== logs.length && (
|
||||
<span className="text-stone-400"> / 전체 {totalCount}건</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* 저장/수동입력/엑셀 버튼 */}
|
||||
<div className="flex items-center gap-2 mt-4">
|
||||
<button
|
||||
onClick={onSave}
|
||||
disabled={saving || logs.length === 0}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
hasChanges
|
||||
? 'bg-purple-600 text-white hover:bg-purple-700'
|
||||
: 'bg-purple-100 text-purple-700 hover:bg-purple-200'
|
||||
} disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||
>
|
||||
{saving ? (
|
||||
<svg className="animate-spin w-4 h-4" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" />
|
||||
</svg>
|
||||
)}
|
||||
{hasChanges ? '변경사항 저장' : '저장'}
|
||||
</button>
|
||||
<button
|
||||
onClick={onManualNew}
|
||||
className="flex items-center gap-2 px-4 py-2 bg-green-100 text-green-700 rounded-lg text-sm font-medium hover:bg-green-200 transition-colors"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
수동입력
|
||||
</button>
|
||||
<button
|
||||
onClick={onExport}
|
||||
disabled={logs.length === 0}
|
||||
className="flex items-center gap-2 px-4 py-2 bg-blue-100 text-blue-700 rounded-lg text-sm font-medium hover:bg-blue-200 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
엑셀 다운로드
|
||||
</button>
|
||||
<button
|
||||
onClick={onToggleHidden}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
showHidden
|
||||
? 'bg-red-600 text-white hover:bg-red-700'
|
||||
: 'bg-red-100 text-red-700 hover:bg-red-200'
|
||||
}`}
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21" />
|
||||
</svg>
|
||||
{showHidden ? '삭제데이터 닫기' : '삭제데이터 보기'}
|
||||
{showHidden && hiddenLogs.length > 0 && (
|
||||
<span className="bg-white text-red-600 text-xs px-1.5 py-0.5 rounded-full font-bold">{hiddenLogs.length}</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden min-h-[calc(100vh-200px)]">
|
||||
<div className="overflow-x-auto" style={ {minHeight: '500px', overflowY: 'auto'} }>
|
||||
<table className="w-full text-left text-sm text-stone-600">
|
||||
<thead className="bg-stone-50 text-xs uppercase font-medium text-stone-500 sticky top-0">
|
||||
@@ -2195,12 +2000,19 @@ className="px-3 py-1 bg-green-500 text-white text-xs rounded-lg hover:bg-green-6
|
||||
const formatCurrency = (val) => new Intl.NumberFormat('ko-KR').format(val || 0) + '원';
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* Page Header */}
|
||||
<div className="space-y-6">
|
||||
{/* Page Header - 계좌입출금 스타일 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-stone-900">카드 사용내역</h1>
|
||||
<p className="text-stone-500 mt-1">바로빌 API를 통한 카드 사용내역 조회 및 계정과목 관리</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-2.5 bg-purple-100 rounded-xl">
|
||||
<svg className="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-stone-900">카드 사용내역</h1>
|
||||
<p className="text-stone-500 mt-1">바로빌 API를 통한 카드 사용내역 조회 및 계정과목 관리</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
@if($isTestMode)
|
||||
@@ -2221,62 +2033,175 @@ className="px-3 py-1 bg-green-500 text-white text-xs rounded-lg hover:bg-green-6
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Dashboard */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<StatCard
|
||||
title="총 사용금액"
|
||||
value={formatCurrency(summary.totalAmount)}
|
||||
subtext={`승인 ${(summary.approvalCount || 0).toLocaleString()}건 / 취소 ${(summary.cancelCount || 0).toLocaleString()}건`}
|
||||
icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/></svg>}
|
||||
color="purple"
|
||||
/>
|
||||
<div className="bg-white rounded-xl p-6 shadow-sm border border-stone-100 hover:shadow-md transition-shadow">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<h3 className="text-sm font-medium text-stone-500">공제</h3>
|
||||
<div className="p-2 rounded-lg bg-green-50 text-green-600">
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-2xl font-bold text-stone-900 mb-2">{formatCurrency(summary.deductibleAmount)}</div>
|
||||
<div className="space-y-1 pt-2 border-t border-stone-100">
|
||||
<div className="flex justify-between text-xs">
|
||||
<span className="text-stone-400">공급가액</span>
|
||||
<span className="font-medium text-stone-600">{formatCurrency(summary.deductibleSupply)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-xs">
|
||||
<span className="text-stone-400">세액</span>
|
||||
<span className="font-medium text-stone-600">{formatCurrency(summary.deductibleTax)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-stone-400 mt-2">{(summary.deductibleCount || 0).toLocaleString()}건</div>
|
||||
{/* 통계 + 카드 선택 (한 줄) - 계좌입출금 스타일 */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-4">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<CompactStat label="사용액" value={formatCurrency(summary.totalAmount)} color="purple" />
|
||||
<CompactStat label="공제" value={formatCurrency(summary.deductibleAmount)} color="green" />
|
||||
<CompactStat label="불공제" value={formatCurrency(summary.nonDeductibleAmount)} color="red" />
|
||||
<CompactStat label="카드" value={`${cards.length}개`} color="stone" />
|
||||
<CompactStat label="거래" value={`${(summary.count || logs.length).toLocaleString()}건`} color="stone" />
|
||||
|
||||
{/* 구분선 */}
|
||||
{cards.length > 0 && <div className="w-px h-6 bg-stone-200 mx-1"></div>}
|
||||
|
||||
{/* 카드 선택 드롭다운 */}
|
||||
{cards.length > 0 && (
|
||||
<>
|
||||
<span className="text-xs text-stone-500">카드:</span>
|
||||
<select
|
||||
value={selectedCard}
|
||||
onChange={(e) => setSelectedCard(e.target.value)}
|
||||
className="px-3 py-2 text-sm border border-gray-200 rounded-lg bg-white text-stone-700 focus:ring-2 focus:ring-purple-500 outline-none"
|
||||
>
|
||||
<option value="">전체 카드</option>
|
||||
{cards.map(card => (
|
||||
<option key={card.cardNum} value={card.cardNum}>
|
||||
{card.cardBrand} ****{card.cardNum ? card.cardNum.slice(-4) : ''}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<StatCard
|
||||
title="불공제"
|
||||
value={formatCurrency(summary.nonDeductibleAmount)}
|
||||
subtext={`${(summary.nonDeductibleCount || 0).toLocaleString()}건`}
|
||||
icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"/></svg>}
|
||||
color="red"
|
||||
/>
|
||||
<StatCard
|
||||
title="등록된 카드"
|
||||
value={`${cards.length}개`}
|
||||
subtext="사용 가능한 카드"
|
||||
icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/></svg>}
|
||||
color="stone"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Card Filter */}
|
||||
{cards.length > 0 && (
|
||||
<div className="bg-white rounded-xl shadow-sm border border-stone-100 p-6">
|
||||
<h2 className="text-sm font-medium text-stone-700 mb-3">카드 선택</h2>
|
||||
<CardSelector
|
||||
cards={cards}
|
||||
selectedCard={selectedCard}
|
||||
onSelect={setSelectedCard}
|
||||
/>
|
||||
{/* 필터 영역 - 날짜/조회/액션 버튼 통합 */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-4">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
|
||||
{/* 기간 조회 필터 */}
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm text-stone-500">기간</label>
|
||||
<input
|
||||
type="date"
|
||||
value={dateFrom}
|
||||
onChange={(e) => setDateFrom(e.target.value)}
|
||||
className="rounded-lg border border-gray-200 px-3 py-1.5 text-sm focus:ring-2 focus:ring-purple-500 outline-none"
|
||||
/>
|
||||
<span className="text-stone-400">~</span>
|
||||
<input
|
||||
type="date"
|
||||
value={dateTo}
|
||||
onChange={(e) => setDateTo(e.target.value)}
|
||||
className="rounded-lg border border-gray-200 px-3 py-1.5 text-sm focus:ring-2 focus:ring-purple-500 outline-none"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={handleThisMonth}
|
||||
className="px-3 py-1.5 text-sm bg-purple-50 text-purple-600 rounded-lg hover:bg-purple-100 transition-colors font-medium"
|
||||
>
|
||||
이번 달
|
||||
</button>
|
||||
<button
|
||||
onClick={handleLastMonth}
|
||||
className="px-3 py-1.5 text-sm bg-stone-100 text-stone-600 rounded-lg hover:bg-stone-200 transition-colors font-medium"
|
||||
>
|
||||
지난달
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleMonthOffset(-2)}
|
||||
className="px-3 py-1.5 text-sm bg-stone-100 text-stone-600 rounded-lg hover:bg-stone-200 transition-colors font-medium"
|
||||
>
|
||||
D-2월
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleMonthOffset(-3)}
|
||||
className="px-3 py-1.5 text-sm bg-stone-100 text-stone-600 rounded-lg hover:bg-stone-200 transition-colors font-medium"
|
||||
>
|
||||
D-3월
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleMonthOffset(-4)}
|
||||
className="px-3 py-1.5 text-sm bg-stone-100 text-stone-600 rounded-lg hover:bg-stone-200 transition-colors font-medium"
|
||||
>
|
||||
D-4월
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleMonthOffset(-5)}
|
||||
className="px-3 py-1.5 text-sm bg-stone-100 text-stone-600 rounded-lg hover:bg-stone-200 transition-colors font-medium"
|
||||
>
|
||||
D-5월
|
||||
</button>
|
||||
<button
|
||||
onClick={() => loadTransactions()}
|
||||
className="px-4 py-1.5 text-sm bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors font-medium flex items-center gap-2"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
조회
|
||||
</button>
|
||||
</div>
|
||||
<span className="text-sm text-stone-500 ml-2">
|
||||
조회: <span className="font-semibold text-stone-700">{logs.length}</span>건
|
||||
{(summary.count || logs.length) !== logs.length && (
|
||||
<span className="text-stone-400"> / 전체 {summary.count}건</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
{/* 액션 버튼들 */}
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={saving || logs.length === 0}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
hasChanges
|
||||
? 'bg-purple-600 text-white hover:bg-purple-700'
|
||||
: 'bg-purple-100 text-purple-700 hover:bg-purple-200'
|
||||
} disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||
>
|
||||
{saving ? (
|
||||
<svg className="animate-spin w-4 h-4" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" />
|
||||
</svg>
|
||||
)}
|
||||
{hasChanges ? '변경사항 저장' : '저장'}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleManualNew}
|
||||
className="flex items-center gap-2 px-4 py-2 bg-green-100 text-green-700 rounded-lg text-sm font-medium hover:bg-green-200 transition-colors"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
수동입력
|
||||
</button>
|
||||
<button
|
||||
onClick={handleExport}
|
||||
disabled={logs.length === 0}
|
||||
className="flex items-center gap-2 px-4 py-2 bg-blue-100 text-blue-700 rounded-lg text-sm font-medium hover:bg-blue-200 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
엑셀
|
||||
</button>
|
||||
<button
|
||||
onClick={handleToggleHidden}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
showHidden
|
||||
? 'bg-red-600 text-white hover:bg-red-700'
|
||||
: 'bg-red-100 text-red-700 hover:bg-red-200'
|
||||
}`}
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21" />
|
||||
</svg>
|
||||
{showHidden ? '삭제데이터 닫기' : '삭제데이터'}
|
||||
{showHidden && hiddenLogs.length > 0 && (
|
||||
<span className="bg-white text-red-600 text-xs px-1.5 py-0.5 rounded-full font-bold">{hiddenLogs.length}</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Error Display */}
|
||||
{error && (
|
||||
@@ -2306,32 +2231,18 @@ className="px-3 py-1 bg-green-500 text-white text-xs rounded-lg hover:bg-green-6
|
||||
<TransactionTable
|
||||
logs={logs}
|
||||
loading={loading}
|
||||
dateFrom={dateFrom}
|
||||
dateTo={dateTo}
|
||||
onDateFromChange={setDateFrom}
|
||||
onDateToChange={setDateTo}
|
||||
onThisMonth={handleThisMonth}
|
||||
onLastMonth={handleLastMonth}
|
||||
onMonthOffset={handleMonthOffset}
|
||||
onSearch={() => loadTransactions()}
|
||||
totalCount={summary.count || logs.length}
|
||||
accountCodes={accountCodes}
|
||||
onAccountCodeChange={handleAccountCodeChange}
|
||||
onFieldChange={handleFieldChange}
|
||||
onSave={handleSave}
|
||||
onExport={handleExport}
|
||||
saving={saving}
|
||||
hasChanges={hasChanges}
|
||||
splits={splits}
|
||||
onOpenSplitModal={handleOpenSplitModal}
|
||||
onDeleteSplits={handleDeleteSplits}
|
||||
onManualNew={handleManualNew}
|
||||
onManualEdit={handleManualEdit}
|
||||
onManualDelete={handleManualDelete}
|
||||
onHide={handleHide}
|
||||
showHidden={showHidden}
|
||||
hiddenLogs={hiddenLogs}
|
||||
onToggleHidden={handleToggleHidden}
|
||||
onRestore={handleRestore}
|
||||
loadingHidden={loadingHidden}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user