390 lines
15 KiB
TypeScript
390 lines
15 KiB
TypeScript
import React, { useState, useEffect } from 'react'
|
||
import { FaCreditCard, FaSave, FaTimes } from 'react-icons/fa'
|
||
import { FiCheck, TypeEnum, CheckStatusEnum, FiCurrentAccount } from '../../../types/fi'
|
||
import { getCheckStatusText, getTypeText } from '@/utils/erp'
|
||
import { mockCurrencies } from '@/mocks/mockCurrencies'
|
||
|
||
interface CheckFormProps {
|
||
check?: FiCheck
|
||
currentAccounts: FiCurrentAccount[]
|
||
onSave: (check: Omit<FiCheck, 'id' | 'creationTime' | 'lastModificationTime'>) => void
|
||
onCancel: () => void
|
||
isOpen: boolean
|
||
}
|
||
|
||
const CheckForm: React.FC<CheckFormProps> = ({
|
||
check,
|
||
currentAccounts,
|
||
onSave,
|
||
onCancel,
|
||
isOpen,
|
||
}) => {
|
||
const [formData, setFormData] = useState({
|
||
checkNumber: '',
|
||
bankName: '',
|
||
branchName: '',
|
||
accountNumber: '',
|
||
drawerName: '',
|
||
payeeName: '',
|
||
currentAccountId: '',
|
||
issueDate: new Date().toISOString().split('T')[0],
|
||
dueDate: new Date().toISOString().split('T')[0],
|
||
amount: 0,
|
||
currency: 'TRY',
|
||
status: CheckStatusEnum.InHand,
|
||
type: TypeEnum.Received,
|
||
notes: '',
|
||
})
|
||
|
||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||
|
||
useEffect(() => {
|
||
if (check) {
|
||
setFormData({
|
||
checkNumber: check.checkNumber,
|
||
bankName: check.bankName,
|
||
branchName: check.branchName,
|
||
accountNumber: check.accountNumber,
|
||
drawerName: check.drawerName,
|
||
payeeName: check.payeeName,
|
||
currentAccountId: check.currentAccountId || '',
|
||
issueDate: new Date(check.issueDate).toISOString().split('T')[0],
|
||
dueDate: new Date(check.dueDate).toISOString().split('T')[0],
|
||
amount: check.amount,
|
||
currency: check.currency,
|
||
status: check.status,
|
||
type: check.type,
|
||
notes: check.notes || '',
|
||
})
|
||
} else {
|
||
setFormData({
|
||
checkNumber: '',
|
||
bankName: '',
|
||
branchName: '',
|
||
accountNumber: '',
|
||
drawerName: '',
|
||
payeeName: '',
|
||
currentAccountId: '',
|
||
issueDate: new Date().toISOString().split('T')[0],
|
||
dueDate: new Date().toISOString().split('T')[0],
|
||
amount: 0,
|
||
currency: 'TRY',
|
||
status: CheckStatusEnum.InHand,
|
||
type: TypeEnum.Received,
|
||
notes: '',
|
||
})
|
||
}
|
||
setErrors({})
|
||
}, [check, isOpen])
|
||
|
||
const validateForm = () => {
|
||
const newErrors: Record<string, string> = {}
|
||
|
||
if (!formData.checkNumber.trim()) {
|
||
newErrors.checkNumber = 'Çek numarası gereklidir'
|
||
}
|
||
if (!formData.bankName.trim()) {
|
||
newErrors.bankName = 'Banka adı gereklidir'
|
||
}
|
||
if (!formData.drawerName.trim()) {
|
||
newErrors.drawerName = 'Keşideci adı gereklidir'
|
||
}
|
||
if (!formData.payeeName.trim()) {
|
||
newErrors.payeeName = 'Lehtar adı gereklidir'
|
||
}
|
||
if (formData.amount <= 0) {
|
||
newErrors.amount = "Tutar 0'dan büyük olmalıdır"
|
||
}
|
||
if (new Date(formData.dueDate) < new Date(formData.issueDate)) {
|
||
newErrors.dueDate = 'Vade tarihi düzenleme tarihinden sonra olmalıdır'
|
||
}
|
||
|
||
setErrors(newErrors)
|
||
return Object.keys(newErrors).length === 0
|
||
}
|
||
|
||
const handleSubmit = (e: React.FormEvent) => {
|
||
e.preventDefault()
|
||
if (validateForm()) {
|
||
const checkData = {
|
||
...formData,
|
||
issueDate: new Date(formData.issueDate),
|
||
dueDate: new Date(formData.dueDate),
|
||
currentAccount: formData.currentAccountId
|
||
? currentAccounts.find((acc) => acc.id === formData.currentAccountId)
|
||
: undefined,
|
||
}
|
||
onSave(checkData)
|
||
}
|
||
}
|
||
|
||
const handleInputChange = (
|
||
field: string,
|
||
value: string | number | TypeEnum | CheckStatusEnum,
|
||
) => {
|
||
setFormData((prev) => ({ ...prev, [field]: value }))
|
||
if (errors[field]) {
|
||
setErrors((prev) => ({ ...prev, [field]: '' }))
|
||
}
|
||
}
|
||
|
||
if (!isOpen) return null
|
||
|
||
return (
|
||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||
<div className="bg-white rounded-lg shadow-xl w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
||
<div className="flex items-center justify-between p-4 border-b">
|
||
<div className="flex items-center gap-2.5">
|
||
<FaCreditCard className="w-5 h-5 text-blue-600" />
|
||
<h2 className="text-lg font-semibold text-gray-900">
|
||
{check ? 'Çek Düzenle' : 'Yeni Çek'}
|
||
</h2>
|
||
</div>
|
||
<button onClick={onCancel} className="p-2 hover:bg-gray-100 rounded-md">
|
||
<FaTimes className="w-5 h-5 text-gray-500" />
|
||
</button>
|
||
</div>
|
||
|
||
<form onSubmit={handleSubmit} className="p-4 space-y-4">
|
||
{/* Çek Bilgileri */}
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Çek Numarası *</label>
|
||
<input
|
||
type="text"
|
||
value={formData.checkNumber}
|
||
onChange={(e) => handleInputChange('checkNumber', e.target.value)}
|
||
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||
errors.checkNumber ? 'border-red-500' : 'border-gray-300'
|
||
}`}
|
||
placeholder="Çek numarasını giriniz"
|
||
/>
|
||
{errors.checkNumber && (
|
||
<p className="text-red-500 text-xs mt-1">{errors.checkNumber}</p>
|
||
)}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Tür *</label>
|
||
<select
|
||
value={formData.type}
|
||
onChange={(e) => handleInputChange('type', e.target.value as TypeEnum)}
|
||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
>
|
||
{Object.values(TypeEnum).map((type) => (
|
||
<option key={type} value={type}>
|
||
{getTypeText(type)}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Durum</label>
|
||
<select
|
||
value={formData.status}
|
||
onChange={(e) => handleInputChange('status', e.target.value as CheckStatusEnum)}
|
||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
>
|
||
{Object.values(CheckStatusEnum).map((status) => (
|
||
<option key={status} value={status}>
|
||
{getCheckStatusText(status)}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Banka Bilgileri */}
|
||
<div className="bg-gray-50 p-3 rounded-lg">
|
||
<h3 className="text-base font-medium text-gray-900 mb-3">Banka Bilgileri</h3>
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Banka Adı *</label>
|
||
<input
|
||
type="text"
|
||
value={formData.bankName}
|
||
onChange={(e) => handleInputChange('bankName', e.target.value)}
|
||
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||
errors.bankName ? 'border-red-500' : 'border-gray-300'
|
||
}`}
|
||
placeholder="Banka adını giriniz"
|
||
/>
|
||
{errors.bankName && <p className="text-red-500 text-xs mt-1">{errors.bankName}</p>}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Şube Adı</label>
|
||
<input
|
||
type="text"
|
||
value={formData.branchName}
|
||
onChange={(e) => handleInputChange('branchName', e.target.value)}
|
||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
placeholder="Şube adını giriniz"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Hesap Numarası
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={formData.accountNumber}
|
||
onChange={(e) => handleInputChange('accountNumber', e.target.value)}
|
||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
placeholder="Hesap numarasını giriniz"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Taraf Bilgileri */}
|
||
<div className="bg-gray-50 p-3 rounded-lg">
|
||
<h3 className="text-base font-medium text-gray-900 mb-3">Taraf Bilgileri</h3>
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Keşideci *</label>
|
||
<input
|
||
type="text"
|
||
value={formData.drawerName}
|
||
onChange={(e) => handleInputChange('drawerName', e.target.value)}
|
||
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||
errors.drawerName ? 'border-red-500' : 'border-gray-300'
|
||
}`}
|
||
placeholder="Keşideci adını giriniz"
|
||
/>
|
||
{errors.drawerName && (
|
||
<p className="text-red-500 text-xs mt-1">{errors.drawerName}</p>
|
||
)}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Lehtar *</label>
|
||
<input
|
||
type="text"
|
||
value={formData.payeeName}
|
||
onChange={(e) => handleInputChange('payeeName', e.target.value)}
|
||
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||
errors.payeeName ? 'border-red-500' : 'border-gray-300'
|
||
}`}
|
||
placeholder="Lehtar adını giriniz"
|
||
/>
|
||
{errors.payeeName && (
|
||
<p className="text-red-500 text-xs mt-1">{errors.payeeName}</p>
|
||
)}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Cari Hesap</label>
|
||
<select
|
||
value={formData.currentAccountId}
|
||
onChange={(e) => handleInputChange('currentAccountId', e.target.value)}
|
||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
>
|
||
<option value="">Cari hesap seçiniz</option>
|
||
{currentAccounts.map((account) => (
|
||
<option key={account.id} value={account.id}>
|
||
{account.accountCode} - {account.businessParty?.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Tutar ve Tarih Bilgileri */}
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Tutar *</label>
|
||
<input
|
||
type="number"
|
||
step="0.01"
|
||
min="0"
|
||
value={formData.amount}
|
||
onChange={(e) => handleInputChange('amount', parseFloat(e.target.value) || 0)}
|
||
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||
errors.amount ? 'border-red-500' : 'border-gray-300'
|
||
}`}
|
||
placeholder="0.00"
|
||
/>
|
||
{errors.amount && <p className="text-red-500 text-xs mt-1">{errors.amount}</p>}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Para Birimi</label>
|
||
<select
|
||
value={formData.currency}
|
||
onChange={(e) => handleInputChange('currency', e.target.value)}
|
||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
>
|
||
{mockCurrencies.map((currency) => (
|
||
<option key={currency.value} value={currency.value}>
|
||
{currency.value} - {currency.label}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Düzenleme Tarihi *
|
||
</label>
|
||
<input
|
||
type="date"
|
||
value={formData.issueDate}
|
||
onChange={(e) => handleInputChange('issueDate', e.target.value)}
|
||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Vade Tarihi *</label>
|
||
<input
|
||
type="date"
|
||
value={formData.dueDate}
|
||
onChange={(e) => handleInputChange('dueDate', e.target.value)}
|
||
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||
errors.dueDate ? 'border-red-500' : 'border-gray-300'
|
||
}`}
|
||
/>
|
||
{errors.dueDate && <p className="text-red-500 text-xs mt-1">{errors.dueDate}</p>}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Notlar */}
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Notlar</label>
|
||
<textarea
|
||
value={formData.notes}
|
||
onChange={(e) => handleInputChange('notes', e.target.value)}
|
||
rows={3}
|
||
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
placeholder="Ek notlar..."
|
||
/>
|
||
</div>
|
||
|
||
{/* Butonlar */}
|
||
<div className="flex justify-end gap-3 pt-4 border-t">
|
||
<button
|
||
type="button"
|
||
onClick={onCancel}
|
||
className="px-4 py-1.5 text-sm border border-gray-300 text-gray-700 rounded-md hover:bg-gray-50 transition-colors"
|
||
>
|
||
İptal
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="flex items-center gap-2 px-4 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
|
||
>
|
||
<FaSave className="w-4 h-4" />
|
||
{check ? 'Güncelle' : 'Kaydet'}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default CheckForm
|