feat:법인카드 관리 필드 추가 (이용자명, 유효기간, CVC, 실사용자)

- 사용자 → 실사용자로 명칭 변경
- 이용자명(명의자) 입력 필드 추가
- 유효기간 YY/MM 형식 입력 필드 추가 (자동 포맷팅)
- CVC 3자리 입력 필드 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
pro
2026-01-30 17:58:54 +09:00
parent 440342ff1f
commit cc15a26234

View File

@@ -61,7 +61,10 @@ function CorporateCardsManagement() {
paymentDay: 15,
creditLimit: 10000000,
currentUsage: 3500000,
holder: '김대표',
cardHolderName: '(주)코드브릿지',
actualUser: '김대표',
expiryDate: '28/12',
cvc: '123',
status: 'active',
memo: '일반 업무용'
},
@@ -74,7 +77,10 @@ function CorporateCardsManagement() {
paymentDay: 25,
creditLimit: 5000000,
currentUsage: 2100000,
holder: '박마케팅',
cardHolderName: '(주)코드브릿지',
actualUser: '박마케팅',
expiryDate: '27/06',
cvc: '456',
status: 'active',
memo: '마케팅/광고비 전용'
},
@@ -87,7 +93,10 @@ function CorporateCardsManagement() {
paymentDay: 0,
creditLimit: 0,
currentUsage: 850000,
holder: '이개발',
cardHolderName: '(주)코드브릿지',
actualUser: '이개발',
expiryDate: '29/03',
cvc: '789',
status: 'active',
memo: '개발 장비/소프트웨어 구매'
},
@@ -100,7 +109,10 @@ function CorporateCardsManagement() {
paymentDay: 10,
creditLimit: 3000000,
currentUsage: 0,
holder: '최관리',
cardHolderName: '(주)코드브릿지',
actualUser: '최관리',
expiryDate: '26/09',
cvc: '321',
status: 'inactive',
memo: '비상용'
}
@@ -120,7 +132,10 @@ function CorporateCardsManagement() {
cardType: 'credit',
paymentDay: 15,
creditLimit: '',
holder: '',
cardHolderName: '',
actualUser: '',
expiryDate: '',
cvc: '',
status: 'active',
memo: ''
};
@@ -158,7 +173,8 @@ function CorporateCardsManagement() {
const filteredCards = cards.filter(card => {
const matchesSearch = card.cardName.toLowerCase().includes(searchTerm.toLowerCase()) ||
card.cardCompany.toLowerCase().includes(searchTerm.toLowerCase()) ||
card.holder.toLowerCase().includes(searchTerm.toLowerCase());
card.actualUser.toLowerCase().includes(searchTerm.toLowerCase()) ||
(card.cardHolderName && card.cardHolderName.toLowerCase().includes(searchTerm.toLowerCase()));
const matchesStatus = filterStatus === 'all' || card.status === filterStatus;
return matchesSearch && matchesStatus;
});
@@ -181,7 +197,10 @@ function CorporateCardsManagement() {
cardType: card.cardType,
paymentDay: card.paymentDay,
creditLimit: card.creditLimit,
holder: card.holder,
cardHolderName: card.cardHolderName || '',
actualUser: card.actualUser || '',
expiryDate: card.expiryDate || '',
cvc: card.cvc || '',
status: card.status,
memo: card.memo
});
@@ -190,7 +209,7 @@ function CorporateCardsManagement() {
// 카드 저장
const handleSaveCard = () => {
if (!formData.cardName || !formData.cardNumber || !formData.holder) {
if (!formData.cardName || !formData.cardNumber || !formData.cardHolderName || !formData.actualUser) {
alert('필수 항목을 입력해주세요.');
return;
}
@@ -312,7 +331,7 @@ className="flex items-center gap-2 px-4 py-2 bg-violet-600 hover:bg-violet-700 t
<Search className="w-5 h-5 text-gray-400 absolute left-3 top-1/2 -translate-y-1/2" />
<input
type="text"
placeholder="카드명, 카드사, 사용자 검색..."
placeholder="카드명, 카드사, 이용자명, 실사용자 검색..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500 focus:border-violet-500"
@@ -380,8 +399,16 @@ className={`bg-white rounded-xl border-2 p-6 cursor-pointer transition-all hover
<div className="grid grid-cols-2 gap-4 mb-4 text-sm">
<div>
<p className="text-gray-500">용자</p>
<p className="font-medium text-gray-900">{card.holder}</p>
<p className="text-gray-500">용자</p>
<p className="font-medium text-gray-900">{card.cardHolderName || '-'}</p>
</div>
<div>
<p className="text-gray-500">실사용자</p>
<p className="font-medium text-gray-900">{card.actualUser}</p>
</div>
<div>
<p className="text-gray-500">유효기간</p>
<p className="font-medium text-gray-900">{card.expiryDate || '-'}</p>
</div>
<div>
<p className="text-gray-500">카드종류</p>
@@ -487,28 +514,60 @@ className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">카드번호 *</label>
<input
type="text"
value={formData.cardNumber}
onChange={(e) => setFormData(prev => ({ ...prev, cardNumber: e.target.value }))}
placeholder="1234-5678-9012-3456"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500 font-mono"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">사용자 *</label>
<label className="block text-sm font-medium text-gray-700 mb-1">카드번호 *</label>
<input
type="text"
value={formData.holder}
onChange={(e) => setFormData(prev => ({ ...prev, holder: e.target.value }))}
placeholder="카드 사용자명"
value={formData.cardNumber}
onChange={(e) => setFormData(prev => ({ ...prev, cardNumber: e.target.value }))}
placeholder="1234-5678-9012-3456"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500 font-mono"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">이용자명 *</label>
<input
type="text"
value={formData.cardHolderName}
onChange={(e) => setFormData(prev => ({ ...prev, cardHolderName: e.target.value }))}
placeholder="카드 명의자 (법인명)"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500"
/>
</div>
</div>
<div className="grid grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">유효기간</label>
<input
type="text"
value={formData.expiryDate}
onChange={(e) => {
let val = e.target.value.replace(/[^\d]/g, '');
if (val.length > 4) val = val.slice(0, 4);
if (val.length >= 2) val = val.slice(0, 2) + '/' + val.slice(2);
setFormData(prev => ({ ...prev, expiryDate: val }));
}}
placeholder="YY/MM"
maxLength={5}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500 font-mono text-center"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">CVC</label>
<input
type="text"
value={formData.cvc}
onChange={(e) => {
const val = e.target.value.replace(/[^\d]/g, '').slice(0, 3);
setFormData(prev => ({ ...prev, cvc: val }));
}}
placeholder="3자리"
maxLength={3}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500 font-mono text-center"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">상태</label>
<select
@@ -522,6 +581,17 @@ className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">실사용자 *</label>
<input
type="text"
value={formData.actualUser}
onChange={(e) => setFormData(prev => ({ ...prev, actualUser: e.target.value }))}
placeholder="실제 카드 사용자명"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500"
/>
</div>
{formData.cardType === 'credit' && (
<div className="grid grid-cols-2 gap-4">
<div>