601 lines
29 KiB
PHP
601 lines
29 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('title', '법인카드 등록/조회')
|
|
|
|
@push('styles')
|
|
<style>
|
|
@media print {
|
|
.no-print { display: none !important; }
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
@section('content')
|
|
<div id="corporate-cards-root"></div>
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script src="https://unpkg.com/react@18/umd/react.development.js?v={{ time() }}"></script>
|
|
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js?v={{ time() }}"></script>
|
|
<script src="https://unpkg.com/@babel/standalone/babel.min.js?v={{ time() }}"></script>
|
|
<script src="https://unpkg.com/lucide@latest?v={{ time() }}"></script>
|
|
@verbatim
|
|
<script type="text/babel">
|
|
const { useState, useRef, useEffect } = React;
|
|
|
|
// Lucide 아이콘 래핑
|
|
const createIcon = (name) => ({ className = "w-5 h-5", ...props }) => {
|
|
const ref = useRef(null);
|
|
useEffect(() => {
|
|
if (ref.current && lucide.icons[name]) {
|
|
ref.current.innerHTML = '';
|
|
const svg = lucide.createElement(lucide.icons[name]);
|
|
svg.setAttribute('class', className);
|
|
ref.current.appendChild(svg);
|
|
}
|
|
}, [className]);
|
|
return <span ref={ref} className="inline-flex items-center" {...props} />;
|
|
};
|
|
|
|
const CreditCard = createIcon('credit-card');
|
|
const Plus = createIcon('plus');
|
|
const Search = createIcon('search');
|
|
const Edit = createIcon('edit');
|
|
const Trash2 = createIcon('trash-2');
|
|
const X = createIcon('x');
|
|
const Building = createIcon('building');
|
|
const Calendar = createIcon('calendar');
|
|
const DollarSign = createIcon('dollar-sign');
|
|
const CheckCircle = createIcon('check-circle');
|
|
const XCircle = createIcon('x-circle');
|
|
|
|
function CorporateCardsManagement() {
|
|
// 카드 목록 데이터
|
|
const [cards, setCards] = useState([
|
|
{
|
|
id: 1,
|
|
cardName: '업무용 법인카드',
|
|
cardCompany: '삼성카드',
|
|
cardNumber: '9410-1234-5678-9012',
|
|
cardType: 'credit',
|
|
paymentDay: 15,
|
|
creditLimit: 10000000,
|
|
currentUsage: 3500000,
|
|
holder: '김대표',
|
|
status: 'active',
|
|
memo: '일반 업무용'
|
|
},
|
|
{
|
|
id: 2,
|
|
cardName: '마케팅 법인카드',
|
|
cardCompany: '현대카드',
|
|
cardNumber: '5412-9876-5432-1098',
|
|
cardType: 'credit',
|
|
paymentDay: 25,
|
|
creditLimit: 5000000,
|
|
currentUsage: 2100000,
|
|
holder: '박마케팅',
|
|
status: 'active',
|
|
memo: '마케팅/광고비 전용'
|
|
},
|
|
{
|
|
id: 3,
|
|
cardName: '개발팀 체크카드',
|
|
cardCompany: '국민카드',
|
|
cardNumber: '4532-1111-2222-3333',
|
|
cardType: 'debit',
|
|
paymentDay: 0,
|
|
creditLimit: 0,
|
|
currentUsage: 850000,
|
|
holder: '이개발',
|
|
status: 'active',
|
|
memo: '개발 장비/소프트웨어 구매'
|
|
},
|
|
{
|
|
id: 4,
|
|
cardName: '예비 법인카드',
|
|
cardCompany: '신한카드',
|
|
cardNumber: '9876-5555-4444-3333',
|
|
cardType: 'credit',
|
|
paymentDay: 10,
|
|
creditLimit: 3000000,
|
|
currentUsage: 0,
|
|
holder: '최관리',
|
|
status: 'inactive',
|
|
memo: '비상용'
|
|
}
|
|
]);
|
|
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
const [filterStatus, setFilterStatus] = useState('all');
|
|
const [showModal, setShowModal] = useState(false);
|
|
const [modalMode, setModalMode] = useState('add'); // 'add' or 'edit'
|
|
const [editingCard, setEditingCard] = useState(null);
|
|
|
|
// 새 카드 폼 초기값
|
|
const initialFormState = {
|
|
cardName: '',
|
|
cardCompany: '삼성카드',
|
|
cardNumber: '',
|
|
cardType: 'credit',
|
|
paymentDay: 15,
|
|
creditLimit: '',
|
|
holder: '',
|
|
status: 'active',
|
|
memo: ''
|
|
};
|
|
const [formData, setFormData] = useState(initialFormState);
|
|
|
|
// 카드사 목록
|
|
const cardCompanies = ['삼성카드', '현대카드', '국민카드', '신한카드', '롯데카드', 'BC카드', '하나카드', '우리카드', 'NH농협카드'];
|
|
|
|
// 금액 포맷
|
|
const formatCurrency = (num) => {
|
|
if (!num) return '0';
|
|
return num.toLocaleString();
|
|
};
|
|
|
|
// 입력용 금액 포맷 (3자리 콤마)
|
|
const formatInputCurrency = (value) => {
|
|
if (!value && value !== 0) return '';
|
|
const num = String(value).replace(/[^\d]/g, '');
|
|
if (!num) return '';
|
|
return Number(num).toLocaleString();
|
|
};
|
|
|
|
// 입력값에서 숫자만 추출
|
|
const parseInputCurrency = (value) => {
|
|
return String(value).replace(/[^\d]/g, '');
|
|
};
|
|
|
|
// 카드번호 포맷
|
|
const formatCardNumber = (num) => {
|
|
if (!num) return '';
|
|
return num.replace(/(.{4})/g, '$1-').slice(0, -1);
|
|
};
|
|
|
|
// 필터링된 카드 목록
|
|
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());
|
|
const matchesStatus = filterStatus === 'all' || card.status === filterStatus;
|
|
return matchesSearch && matchesStatus;
|
|
});
|
|
|
|
// 카드 추가 모달 열기
|
|
const handleAddCard = () => {
|
|
setModalMode('add');
|
|
setFormData(initialFormState);
|
|
setShowModal(true);
|
|
};
|
|
|
|
// 카드 수정 모달 열기
|
|
const handleEditCard = (card) => {
|
|
setModalMode('edit');
|
|
setEditingCard(card);
|
|
setFormData({
|
|
cardName: card.cardName,
|
|
cardCompany: card.cardCompany,
|
|
cardNumber: card.cardNumber,
|
|
cardType: card.cardType,
|
|
paymentDay: card.paymentDay,
|
|
creditLimit: card.creditLimit,
|
|
holder: card.holder,
|
|
status: card.status,
|
|
memo: card.memo
|
|
});
|
|
setShowModal(true);
|
|
};
|
|
|
|
// 카드 저장
|
|
const handleSaveCard = () => {
|
|
if (!formData.cardName || !formData.cardNumber || !formData.holder) {
|
|
alert('필수 항목을 입력해주세요.');
|
|
return;
|
|
}
|
|
|
|
if (modalMode === 'add') {
|
|
const newCard = {
|
|
id: Date.now(),
|
|
...formData,
|
|
creditLimit: parseInt(formData.creditLimit) || 0,
|
|
currentUsage: 0
|
|
};
|
|
setCards(prev => [...prev, newCard]);
|
|
} else {
|
|
setCards(prev => prev.map(card =>
|
|
card.id === editingCard.id
|
|
? { ...card, ...formData, creditLimit: parseInt(formData.creditLimit) || 0 }
|
|
: card
|
|
));
|
|
}
|
|
|
|
setShowModal(false);
|
|
setEditingCard(null);
|
|
};
|
|
|
|
// 카드 삭제
|
|
const handleDeleteCard = (id) => {
|
|
if (confirm('정말 삭제하시겠습니까?')) {
|
|
setCards(prev => prev.filter(card => card.id !== id));
|
|
if (showModal) {
|
|
setShowModal(false);
|
|
setEditingCard(null);
|
|
}
|
|
}
|
|
};
|
|
|
|
// 사용률 계산
|
|
const getUsagePercent = (usage, limit) => {
|
|
if (!limit) return 0;
|
|
return Math.round((usage / limit) * 100);
|
|
};
|
|
|
|
// 사용률 색상
|
|
const getUsageColor = (percent) => {
|
|
if (percent >= 80) return 'bg-red-500';
|
|
if (percent >= 50) return 'bg-yellow-500';
|
|
return 'bg-emerald-500';
|
|
};
|
|
|
|
// 총 한도 및 사용액
|
|
const totalLimit = cards.filter(c => c.status === 'active' && c.cardType === 'credit').reduce((sum, c) => sum + c.creditLimit, 0);
|
|
const totalUsage = cards.filter(c => c.status === 'active').reduce((sum, c) => sum + c.currentUsage, 0);
|
|
|
|
return (
|
|
<div className="bg-gray-50 min-h-screen">
|
|
{/* 헤더 */}
|
|
<header className="bg-white border-b border-gray-200 rounded-t-xl mb-6">
|
|
<div className="px-6 py-4">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-4">
|
|
<div className="p-2 bg-violet-100 rounded-xl">
|
|
<CreditCard className="w-6 h-6 text-violet-600" />
|
|
</div>
|
|
<div>
|
|
<h1 className="text-xl font-bold text-gray-900">법인카드 등록/조회</h1>
|
|
<p className="text-sm text-gray-500">Corporate Card Management</p>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={handleAddCard}
|
|
className="flex items-center gap-2 px-4 py-2 bg-violet-600 hover:bg-violet-700 text-white rounded-lg transition-colors"
|
|
>
|
|
<Plus className="w-4 h-4" />
|
|
<span className="text-sm font-medium">카드 등록</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
{/* 요약 카드 */}
|
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
|
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-sm text-gray-500">등록 카드</span>
|
|
<CreditCard className="w-5 h-5 text-gray-400" />
|
|
</div>
|
|
<p className="text-2xl font-bold text-gray-900">{cards.length}장</p>
|
|
<p className="text-xs text-gray-400 mt-1">활성 {cards.filter(c => c.status === 'active').length}장</p>
|
|
</div>
|
|
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-sm text-gray-500">총 한도</span>
|
|
<DollarSign className="w-5 h-5 text-gray-400" />
|
|
</div>
|
|
<p className="text-2xl font-bold text-gray-900">{formatCurrency(totalLimit)}원</p>
|
|
<p className="text-xs text-gray-400 mt-1">신용카드 기준</p>
|
|
</div>
|
|
<div className="bg-white rounded-xl border border-violet-200 p-6 bg-violet-50/30">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-sm text-violet-700">이번 달 사용</span>
|
|
<CreditCard className="w-5 h-5 text-violet-500" />
|
|
</div>
|
|
<p className="text-2xl font-bold text-violet-600">{formatCurrency(totalUsage)}원</p>
|
|
<p className="text-xs text-violet-500 mt-1">{totalLimit > 0 ? getUsagePercent(totalUsage, totalLimit) : 0}% 사용</p>
|
|
</div>
|
|
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-sm text-gray-500">잔여 한도</span>
|
|
<DollarSign className="w-5 h-5 text-emerald-500" />
|
|
</div>
|
|
<p className="text-2xl font-bold text-emerald-600">{formatCurrency(totalLimit - totalUsage)}원</p>
|
|
<p className="text-xs text-gray-400 mt-1">사용 가능</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 검색 및 필터 */}
|
|
<div className="bg-white rounded-xl border border-gray-200 p-4 mb-6">
|
|
<div className="flex flex-col md:flex-row gap-4">
|
|
<div className="flex-1 relative">
|
|
<Search className="w-5 h-5 text-gray-400 absolute left-3 top-1/2 -translate-y-1/2" />
|
|
<input
|
|
type="text"
|
|
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"
|
|
/>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<button
|
|
onClick={() => setFilterStatus('all')}
|
|
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
|
filterStatus === 'all' ? 'bg-violet-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
|
}`}
|
|
>
|
|
전체
|
|
</button>
|
|
<button
|
|
onClick={() => setFilterStatus('active')}
|
|
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
|
filterStatus === 'active' ? 'bg-emerald-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
|
}`}
|
|
>
|
|
활성
|
|
</button>
|
|
<button
|
|
onClick={() => setFilterStatus('inactive')}
|
|
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
|
filterStatus === 'inactive' ? 'bg-gray-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
|
}`}
|
|
>
|
|
비활성
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 카드 목록 */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{filteredCards.map(card => (
|
|
<div
|
|
key={card.id}
|
|
onClick={() => handleEditCard(card)}
|
|
className={`bg-white rounded-xl border-2 p-6 cursor-pointer transition-all hover:shadow-lg ${
|
|
card.status === 'active' ? 'border-gray-200 hover:border-violet-300' : 'border-gray-200 opacity-60'
|
|
}`}
|
|
>
|
|
<div className="flex items-start justify-between mb-4">
|
|
<div className="flex items-center gap-3">
|
|
<div className={`p-2 rounded-lg ${card.status === 'active' ? 'bg-violet-100' : 'bg-gray-100'}`}>
|
|
<CreditCard className={`w-5 h-5 ${card.status === 'active' ? 'text-violet-600' : 'text-gray-400'}`} />
|
|
</div>
|
|
<div>
|
|
<h3 className="font-bold text-gray-900">{card.cardName}</h3>
|
|
<p className="text-sm text-gray-500">{card.cardCompany}</p>
|
|
</div>
|
|
</div>
|
|
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
|
|
card.status === 'active' ? 'bg-emerald-100 text-emerald-700' : 'bg-gray-100 text-gray-500'
|
|
}`}>
|
|
{card.status === 'active' ? '활성' : '비활성'}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="mb-4">
|
|
<p className="text-lg font-mono text-gray-700 tracking-wider">{card.cardNumber}</p>
|
|
</div>
|
|
|
|
<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>
|
|
</div>
|
|
<div>
|
|
<p className="text-gray-500">카드종류</p>
|
|
<p className="font-medium text-gray-900">{card.cardType === 'credit' ? '신용카드' : '체크카드'}</p>
|
|
</div>
|
|
{card.cardType === 'credit' && (
|
|
<>
|
|
<div>
|
|
<p className="text-gray-500">결제일</p>
|
|
<p className="font-medium text-gray-900">매월 {card.paymentDay}일</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-gray-500">한도</p>
|
|
<p className="font-medium text-gray-900">{formatCurrency(card.creditLimit)}원</p>
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
|
|
{card.cardType === 'credit' && card.creditLimit > 0 && (
|
|
<div>
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-sm text-gray-500">사용현황</span>
|
|
<span className="text-sm font-medium text-gray-900">
|
|
{formatCurrency(card.currentUsage)}원 / {formatCurrency(card.creditLimit)}원
|
|
</span>
|
|
</div>
|
|
<div className="w-full bg-gray-200 rounded-full h-2">
|
|
<div
|
|
className={`h-2 rounded-full ${getUsageColor(getUsagePercent(card.currentUsage, card.creditLimit))}`}
|
|
style={{ width: `${Math.min(getUsagePercent(card.currentUsage, card.creditLimit), 100)}%` }}
|
|
></div>
|
|
</div>
|
|
<p className="text-xs text-gray-400 mt-1 text-right">{getUsagePercent(card.currentUsage, card.creditLimit)}% 사용</p>
|
|
</div>
|
|
)}
|
|
|
|
{card.memo && (
|
|
<p className="text-xs text-gray-400 mt-3 pt-3 border-t border-gray-100">{card.memo}</p>
|
|
)}
|
|
</div>
|
|
))}
|
|
|
|
{filteredCards.length === 0 && (
|
|
<div className="col-span-full text-center py-12 text-gray-400">
|
|
<CreditCard className="w-12 h-12 mx-auto mb-4 opacity-50" />
|
|
<p>등록된 카드가 없습니다.</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* 등록/수정 모달 */}
|
|
{showModal && (
|
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
|
<div className="bg-white rounded-xl p-6 w-full max-w-lg mx-4 max-h-[90vh] overflow-y-auto">
|
|
<div className="flex items-center justify-between mb-6">
|
|
<h3 className="text-lg font-bold text-gray-900">
|
|
{modalMode === 'add' ? '법인카드 등록' : '법인카드 수정'}
|
|
</h3>
|
|
<button
|
|
onClick={() => { setShowModal(false); setEditingCard(null); }}
|
|
className="p-1 hover:bg-gray-100 rounded-lg"
|
|
>
|
|
<X className="w-5 h-5 text-gray-500" />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">카드명 *</label>
|
|
<input
|
|
type="text"
|
|
value={formData.cardName}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, cardName: 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 className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">카드사</label>
|
|
<select
|
|
value={formData.cardCompany}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, cardCompany: e.target.value }))}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500"
|
|
>
|
|
{cardCompanies.map(company => (
|
|
<option key={company} value={company}>{company}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">카드종류</label>
|
|
<select
|
|
value={formData.cardType}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, cardType: e.target.value }))}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500"
|
|
>
|
|
<option value="credit">신용카드</option>
|
|
<option value="debit">체크카드</option>
|
|
</select>
|
|
</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>
|
|
<input
|
|
type="text"
|
|
value={formData.holder}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, holder: 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>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">상태</label>
|
|
<select
|
|
value={formData.status}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, status: e.target.value }))}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500"
|
|
>
|
|
<option value="active">활성</option>
|
|
<option value="inactive">비활성</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{formData.cardType === 'credit' && (
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">결제일</label>
|
|
<select
|
|
value={formData.paymentDay}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, paymentDay: parseInt(e.target.value) }))}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500"
|
|
>
|
|
{[1, 5, 10, 14, 15, 20, 25, 27].map(day => (
|
|
<option key={day} value={day}>매월 {day}일</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">사용한도</label>
|
|
<input
|
|
type="text"
|
|
value={formatInputCurrency(formData.creditLimit)}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, creditLimit: parseInputCurrency(e.target.value) }))}
|
|
placeholder="0"
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500 text-right"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">메모</label>
|
|
<textarea
|
|
value={formData.memo}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, memo: e.target.value }))}
|
|
placeholder="카드 용도나 특이사항..."
|
|
rows={2}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500 resize-none"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex gap-3 mt-6">
|
|
{modalMode === 'edit' && (
|
|
<button
|
|
onClick={() => handleDeleteCard(editingCard.id)}
|
|
className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg flex items-center gap-2"
|
|
>
|
|
<span>🗑️</span> 삭제
|
|
</button>
|
|
)}
|
|
<button
|
|
onClick={() => { setShowModal(false); setEditingCard(null); }}
|
|
className="flex-1 px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 flex items-center justify-center gap-2"
|
|
>
|
|
<span>✕</span> 취소
|
|
</button>
|
|
<button
|
|
onClick={handleSaveCard}
|
|
className="flex-1 px-4 py-2 bg-violet-600 hover:bg-violet-700 text-white rounded-lg flex items-center justify-center gap-2"
|
|
>
|
|
<span>✓</span> {modalMode === 'add' ? '등록' : '저장'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// React 앱 마운트
|
|
const rootElement = document.getElementById('corporate-cards-root');
|
|
if (rootElement) {
|
|
ReactDOM.createRoot(rootElement).render(<CorporateCardsManagement />);
|
|
}
|
|
</script>
|
|
@endverbatim
|
|
@endpush
|