erp-platform/ui/src/views/accounting/components/CurrentAccountForm.tsx
2025-09-17 12:46:58 +03:00

424 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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: '',
phone: '',
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: '',
phone: '',
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="phone"
value={formData.phone || ''}
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