erp-platform/ui/src/views/accounting/components/CurrentAccountForm.tsx

466 lines
16 KiB
TypeScript
Raw Normal View History

2025-09-15 09:31:47 +00:00
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";
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"
>
<option value="TRY">TRY - Türk Lirası</option>
<option value="USD">USD - Amerikan Doları</option>
<option value="EUR">EUR - Euro</option>
<option value="GBP">GBP - İngiliz Sterlini</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;