424 lines
16 KiB
TypeScript
424 lines
16 KiB
TypeScript
import React, { useState, useEffect } from 'react'
|
||
import { FaTimes, FaUser, FaBuilding, FaCreditCard, FaPhoneAlt } from 'react-icons/fa'
|
||
import { FiCurrentAccount, AccountTypeEnum, RiskGroupEnum } from '../../../types/fi'
|
||
import { mockBusinessParties } from '../../../mocks/mockBusinessParties'
|
||
import { getAccountTypeText, getRiskGroupText } from '../../../utils/erp'
|
||
import { mockCurrencies } from '@/mocks/mockCurrencies'
|
||
|
||
interface CurrentAccountFormProps {
|
||
account?: FiCurrentAccount
|
||
isOpen: boolean
|
||
onClose: () => void
|
||
onSave: (account: Partial<FiCurrentAccount>) => void
|
||
}
|
||
|
||
const CurrentAccountForm: React.FC<CurrentAccountFormProps> = ({
|
||
account,
|
||
isOpen,
|
||
onClose,
|
||
onSave,
|
||
}) => {
|
||
const [formData, setFormData] = useState<
|
||
Partial<FiCurrentAccount> & {
|
||
id?: string
|
||
}
|
||
>({
|
||
accountCode: '',
|
||
businessPartyId: '',
|
||
type: AccountTypeEnum.Customer,
|
||
contactPerson: '',
|
||
phoneNumber: '',
|
||
email: '',
|
||
address: '',
|
||
taxNumber: '',
|
||
taxOffice: '',
|
||
creditLimit: 0,
|
||
currency: 'TRY',
|
||
riskGroup: RiskGroupEnum.Low,
|
||
paymentTerm: 30,
|
||
isActive: true,
|
||
})
|
||
|
||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||
|
||
useEffect(() => {
|
||
if (account) {
|
||
setFormData({
|
||
...account,
|
||
creditLimit: account.creditLimit || 0,
|
||
paymentTerm: account.paymentTerm || 30,
|
||
})
|
||
} else {
|
||
setFormData({
|
||
accountCode: '',
|
||
businessPartyId: '',
|
||
type: AccountTypeEnum.Customer,
|
||
contactPerson: '',
|
||
phoneNumber: '',
|
||
email: '',
|
||
address: '',
|
||
taxNumber: '',
|
||
taxOffice: '',
|
||
creditLimit: 0,
|
||
currency: 'TRY',
|
||
riskGroup: RiskGroupEnum.Low,
|
||
paymentTerm: 30,
|
||
isActive: true,
|
||
})
|
||
}
|
||
setErrors({})
|
||
}, [account, isOpen])
|
||
|
||
const handleInputChange = (
|
||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||
) => {
|
||
const { name, value, type } = e.target
|
||
let parsedValue: string | number | boolean = value
|
||
|
||
if (type === 'number') {
|
||
parsedValue = value === '' ? 0 : parseFloat(value)
|
||
} else if (type === 'checkbox') {
|
||
parsedValue = (e.target as HTMLInputElement).checked
|
||
}
|
||
|
||
setFormData((prev) => ({
|
||
...prev,
|
||
[name]: parsedValue,
|
||
}))
|
||
|
||
// Clear error when user starts typing
|
||
if (errors[name]) {
|
||
setErrors((prev) => ({
|
||
...prev,
|
||
[name]: '',
|
||
}))
|
||
}
|
||
}
|
||
|
||
const validateForm = () => {
|
||
const newErrors: Record<string, string> = {}
|
||
|
||
if (!formData.accountCode?.trim()) {
|
||
newErrors.accountCode = 'Hesap kodu zorunludur'
|
||
}
|
||
if (!formData.businessPartyId?.trim()) {
|
||
newErrors.businessPartyId = 'Ünvan zorunludur'
|
||
}
|
||
if (formData.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
|
||
newErrors.email = 'Geçerli bir e-posta adresi giriniz'
|
||
}
|
||
if (formData.creditLimit && formData.creditLimit < 0) {
|
||
newErrors.creditLimit = 'Kredi limiti negatif olamaz'
|
||
}
|
||
if (formData.paymentTerm && formData.paymentTerm < 0) {
|
||
newErrors.paymentTerm = 'Ödeme vadesi negatif olamaz'
|
||
}
|
||
|
||
setErrors(newErrors)
|
||
return Object.keys(newErrors).length === 0
|
||
}
|
||
|
||
const handleSubmit = (e: React.FormEvent) => {
|
||
e.preventDefault()
|
||
if (validateForm()) {
|
||
onSave(formData)
|
||
onClose()
|
||
}
|
||
}
|
||
|
||
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 w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
||
<div className="flex items-center justify-between p-4 border-b">
|
||
<h2 className="text-lg font-semibold text-gray-900">
|
||
{account ? 'Cari Hesap Düzenle' : 'Yeni Cari Hesap'}
|
||
</h2>
|
||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||
<FaTimes className="w-5 h-5" />
|
||
</button>
|
||
</div>
|
||
|
||
<form onSubmit={handleSubmit} className="p-4">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
{/* Temel Bilgiler */}
|
||
<div className="space-y-3">
|
||
<h3 className="text-base font-medium text-gray-900 flex items-center gap-2">
|
||
<FaUser className="w-4 h-4 text-blue-600" />
|
||
Temel Bilgiler
|
||
</h3>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Hesap Kodu *</label>
|
||
<input
|
||
type="text"
|
||
name="accountCode"
|
||
value={formData.accountCode || ''}
|
||
onChange={handleInputChange}
|
||
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||
errors.accountCode ? 'border-red-500' : 'border-gray-300'
|
||
}`}
|
||
placeholder="CA001"
|
||
/>
|
||
{errors.accountCode && (
|
||
<p className="text-red-500 text-xs mt-1">{errors.accountCode}</p>
|
||
)}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Ünvan *</label>
|
||
|
||
<select
|
||
name="businessPartyId"
|
||
value={formData.businessPartyId || ''}
|
||
onChange={handleInputChange}
|
||
className="mt-1 block w-full border border-gray-300 rounded-md px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||
required
|
||
>
|
||
<option value="">Tedarikçi Seçiniz</option>
|
||
{mockBusinessParties.map((businessParty) => (
|
||
<option key={businessParty.id} value={businessParty.id}>
|
||
{businessParty.name} ({businessParty.code})
|
||
</option>
|
||
))}
|
||
</select>
|
||
{errors.title && <p className="text-red-500 text-xs mt-1">{errors.title}</p>}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Hesap Türü</label>
|
||
<select
|
||
name="type"
|
||
value={formData.type}
|
||
onChange={handleInputChange}
|
||
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(AccountTypeEnum).map((type) => (
|
||
<option key={type} value={type}>
|
||
{getAccountTypeText(type)}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Yetkili Kişi</label>
|
||
<input
|
||
type="text"
|
||
name="contactPerson"
|
||
value={formData.contactPerson || ''}
|
||
onChange={handleInputChange}
|
||
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="Yetkili kişi adı"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* İletişim Bilgileri */}
|
||
<div className="space-y-3">
|
||
<h3 className="text-base font-medium text-gray-900 flex items-center gap-2">
|
||
<FaPhoneAlt className="w-4 h-4 text-green-600" />
|
||
İletişim Bilgileri
|
||
</h3>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Telefon</label>
|
||
<input
|
||
type="text"
|
||
name="phoneNumber"
|
||
value={formData.phoneNumber || ''}
|
||
onChange={handleInputChange}
|
||
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="+90 212 555 1234"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">E-posta</label>
|
||
<input
|
||
type="email"
|
||
name="email"
|
||
value={formData.email || ''}
|
||
onChange={handleInputChange}
|
||
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||
errors.email ? 'border-red-500' : 'border-gray-300'
|
||
}`}
|
||
placeholder="info@company.com"
|
||
/>
|
||
{errors.email && <p className="text-red-500 text-xs mt-1">{errors.email}</p>}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Adres</label>
|
||
<textarea
|
||
name="address"
|
||
value={formData.address || ''}
|
||
onChange={handleInputChange}
|
||
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="Tam adres"
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-3">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Vergi Numarası
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="taxNumber"
|
||
value={formData.taxNumber || ''}
|
||
onChange={handleInputChange}
|
||
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="1234567890"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Vergi Dairesi
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="taxOffice"
|
||
value={formData.taxOffice || ''}
|
||
onChange={handleInputChange}
|
||
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="Beylikdüzü V.D."
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Mali Bilgiler */}
|
||
<div className="space-y-3">
|
||
<h3 className="text-base font-medium text-gray-900 flex items-center gap-2">
|
||
<FaCreditCard className="w-4 h-4 text-purple-600" />
|
||
Mali Bilgiler
|
||
</h3>
|
||
|
||
<div className="grid grid-cols-2 gap-3">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Kredi Limiti
|
||
</label>
|
||
<input
|
||
type="number"
|
||
name="creditLimit"
|
||
value={formData.creditLimit || 0}
|
||
onChange={handleInputChange}
|
||
min="0"
|
||
step="0.01"
|
||
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||
errors.creditLimit ? 'border-red-500' : 'border-gray-300'
|
||
}`}
|
||
placeholder="0.00"
|
||
/>
|
||
{errors.creditLimit && (
|
||
<p className="text-red-500 text-xs mt-1">{errors.creditLimit}</p>
|
||
)}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Para Birimi
|
||
</label>
|
||
<select
|
||
name="currency"
|
||
value={formData.currency}
|
||
onChange={handleInputChange}
|
||
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>
|
||
|
||
<div className="grid grid-cols-2 gap-3">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Risk Grubu</label>
|
||
<select
|
||
name="riskGroup"
|
||
value={formData.riskGroup}
|
||
onChange={handleInputChange}
|
||
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(RiskGroupEnum).map((risk) => (
|
||
<option key={risk} value={risk}>
|
||
{getRiskGroupText(risk)}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Ödeme Vadesi (Gün)
|
||
</label>
|
||
<input
|
||
type="number"
|
||
name="paymentTerm"
|
||
value={formData.paymentTerm || 30}
|
||
onChange={handleInputChange}
|
||
min="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.paymentTerm ? 'border-red-500' : 'border-gray-300'
|
||
}`}
|
||
placeholder="30"
|
||
/>
|
||
{errors.paymentTerm && (
|
||
<p className="text-red-500 text-xs mt-1">{errors.paymentTerm}</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Durum */}
|
||
<div className="space-y-3">
|
||
<h3 className="text-base font-medium text-gray-900 flex items-center gap-2">
|
||
<FaBuilding className="w-4 h-4 text-orange-600" />
|
||
Durum
|
||
</h3>
|
||
|
||
<div className="flex items-center">
|
||
<input
|
||
type="checkbox"
|
||
name="isActive"
|
||
checked={formData.isActive || false}
|
||
onChange={handleInputChange}
|
||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
||
/>
|
||
<label className="ml-2 block text-sm text-gray-700">Aktif hesap</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Form Actions */}
|
||
<div className="flex justify-end gap-3 mt-6 pt-4 border-t">
|
||
<button
|
||
type="button"
|
||
onClick={onClose}
|
||
className="px-4 py-1.5 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 transition-colors"
|
||
>
|
||
İptal
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="px-4 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
|
||
>
|
||
{account ? 'Güncelle' : 'Ekle'}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default CurrentAccountForm
|