370 lines
14 KiB
TypeScript
370 lines
14 KiB
TypeScript
import React, { useState, useEffect } from 'react'
|
||
import { FaTimes, FaSave, FaCreditCard, FaStar } from 'react-icons/fa'
|
||
import { SupplierCardTypeEnum, SupplierTypeEnum } from '../../../types/mm'
|
||
import { mockBusinessPartyNew } from '../../../mocks/mockBusinessParties'
|
||
import { BusinessParty, PartyType, PaymentTerms } from '../../../types/common'
|
||
import { getPaymentTermsText, getSupplierCardTypeText } from '@/utils/erp'
|
||
|
||
interface SupplierCardModalProps {
|
||
isOpen: boolean
|
||
onClose: () => void
|
||
onSave: (supplierCard: BusinessParty) => void
|
||
supplierCard?: BusinessParty | null
|
||
mode: 'create' | 'view' | 'edit'
|
||
}
|
||
|
||
const SupplierCardModal: React.FC<SupplierCardModalProps> = ({
|
||
isOpen,
|
||
onClose,
|
||
onSave,
|
||
supplierCard,
|
||
mode,
|
||
}) => {
|
||
const [formData, setFormData] = useState<Partial<BusinessParty>>(mockBusinessPartyNew)
|
||
|
||
useEffect(() => {
|
||
if (mode === 'create') {
|
||
setFormData(mockBusinessPartyNew)
|
||
} else if (supplierCard) {
|
||
setFormData(supplierCard)
|
||
}
|
||
}, [supplierCard, mode])
|
||
|
||
const handleInputChange = (
|
||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||
) => {
|
||
const { name, value, type } = e.target
|
||
setFormData((prev) => ({
|
||
...prev,
|
||
[name]:
|
||
type === 'number'
|
||
? parseFloat(value) || 0
|
||
: type === 'checkbox'
|
||
? (e.target as HTMLInputElement).checked
|
||
: value,
|
||
}))
|
||
}
|
||
|
||
const handleSpecialConditionsChange = (value: string) => {
|
||
const conditions = value.split('\n').filter((condition) => condition.trim() !== '')
|
||
setFormData((prev) => ({
|
||
...prev,
|
||
specialConditions: conditions,
|
||
}))
|
||
}
|
||
|
||
const handleSubmit = (e: React.FormEvent) => {
|
||
e.preventDefault()
|
||
if (mode !== 'view') {
|
||
const newSupplierCard: BusinessParty = {
|
||
id: supplierCard?.id || `SC-${Date.now()}`,
|
||
code: formData.code || '',
|
||
cardNumber:
|
||
formData.cardNumber ||
|
||
`SC-${new Date().getFullYear()}-${Date.now().toString().slice(-3)}`,
|
||
cardType: formData.cardType || SupplierCardTypeEnum.Standard,
|
||
validFrom: formData.validFrom || new Date(),
|
||
validTo: formData.validTo,
|
||
creditLimit: formData.creditLimit || 0,
|
||
currentBalance: formData.currentBalance || 0,
|
||
paymentTerms: formData.paymentTerms || PaymentTerms.Net30,
|
||
discountRate: formData.discountRate,
|
||
specialConditions: formData.specialConditions || [],
|
||
isActive: formData.isActive ?? true,
|
||
lastOrderDate: formData.lastOrderDate,
|
||
performanceMetrics: formData.performanceMetrics || {
|
||
deliveryPerformance: 0,
|
||
qualityRating: 0,
|
||
priceCompetitiveness: 0,
|
||
responsiveness: 0,
|
||
complianceRating: 0,
|
||
overallScore: 0,
|
||
lastEvaluationDate: new Date(),
|
||
},
|
||
creationTime: supplierCard?.creationTime || new Date(),
|
||
lastModificationTime: new Date(),
|
||
supplierType: SupplierTypeEnum.Distributor,
|
||
name: '',
|
||
currency: '',
|
||
certifications: [],
|
||
bankAccounts: [],
|
||
contacts: [],
|
||
partyType: PartyType.Supplier,
|
||
}
|
||
onSave(newSupplierCard)
|
||
}
|
||
onClose()
|
||
}
|
||
|
||
if (!isOpen) return null
|
||
|
||
const isReadOnly = mode === 'view'
|
||
const modalTitle =
|
||
mode === 'create'
|
||
? 'Yeni Tedarikçi Kartı'
|
||
: mode === 'edit'
|
||
? 'Tedarikçi Kartını Düzenle'
|
||
: 'Tedarikçi Kartı Detayları'
|
||
|
||
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">
|
||
<h3 className="text-base font-semibold text-gray-900 flex items-center">
|
||
<FaCreditCard className="mr-2 text-blue-600" />
|
||
{modalTitle}
|
||
</h3>
|
||
<button onClick={onClose} className="p-1 text-gray-400 hover:text-gray-600">
|
||
<FaTimes />
|
||
</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">
|
||
<h4 className="text-sm font-medium text-gray-900 border-b pb-1.5">Temel Bilgiler</h4>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700">Tedarikçi</label>
|
||
<input
|
||
type="text"
|
||
name="customerCode"
|
||
value={formData.code || ''}
|
||
onChange={handleInputChange}
|
||
readOnly={isReadOnly}
|
||
className="mt-1 block w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700">Kart Numarası</label>
|
||
<input
|
||
type="text"
|
||
name="cardNumber"
|
||
value={formData.cardNumber || ''}
|
||
onChange={handleInputChange}
|
||
readOnly={isReadOnly}
|
||
className="mt-1 block w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700">Kart Tipi</label>
|
||
<select
|
||
name="cardType"
|
||
value={formData.cardType || SupplierCardTypeEnum.Standard}
|
||
onChange={handleInputChange}
|
||
disabled={isReadOnly}
|
||
className="mt-1 block w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||
>
|
||
{Object.values(SupplierCardTypeEnum).map((type) => (
|
||
<option key={type} value={type}>
|
||
{getSupplierCardTypeText(type)}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700">
|
||
Geçerlilik Başlangıcı
|
||
</label>
|
||
<input
|
||
type="date"
|
||
name="validFrom"
|
||
value={
|
||
formData.validFrom
|
||
? new Date(formData.validFrom).toISOString().split('T')[0]
|
||
: ''
|
||
}
|
||
onChange={handleInputChange}
|
||
readOnly={isReadOnly}
|
||
className="mt-1 block w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700">Geçerlilik Bitişi</label>
|
||
<input
|
||
type="date"
|
||
name="validTo"
|
||
value={
|
||
formData.validTo ? new Date(formData.validTo).toISOString().split('T')[0] : ''
|
||
}
|
||
onChange={handleInputChange}
|
||
readOnly={isReadOnly}
|
||
className="mt-1 block w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Mali Bilgiler */}
|
||
<div className="space-y-3">
|
||
<h4 className="text-sm font-medium text-gray-900 border-b pb-1.5">Mali Bilgiler</h4>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700">Kredi Limiti (TL)</label>
|
||
<input
|
||
type="number"
|
||
name="creditLimit"
|
||
value={formData.creditLimit || 0}
|
||
onChange={handleInputChange}
|
||
readOnly={isReadOnly}
|
||
className="mt-1 block w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700">
|
||
Mevcut Bakiye (TL)
|
||
</label>
|
||
<input
|
||
type="number"
|
||
name="currentBalance"
|
||
value={formData.currentBalance || 0}
|
||
onChange={handleInputChange}
|
||
readOnly={isReadOnly}
|
||
className="mt-1 block w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700">Ödeme Koşulları</label>
|
||
<select
|
||
name="paymentTerms"
|
||
value={formData.paymentTerms || PaymentTerms.Net30}
|
||
onChange={handleInputChange}
|
||
disabled={isReadOnly}
|
||
className="mt-1 block w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||
>
|
||
{Object.values(PaymentTerms).map((term) => (
|
||
<option key={term} value={term}>
|
||
{getPaymentTermsText(term)}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700">İndirim Oranı (%)</label>
|
||
<input
|
||
type="number"
|
||
name="discountRate"
|
||
value={formData.discountRate || 0}
|
||
onChange={handleInputChange}
|
||
readOnly={isReadOnly}
|
||
min="0"
|
||
max="100"
|
||
step="0.1"
|
||
className="mt-1 block w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="flex items-center">
|
||
<input
|
||
type="checkbox"
|
||
name="isActive"
|
||
checked={formData.isActive || false}
|
||
onChange={handleInputChange}
|
||
disabled={isReadOnly}
|
||
className="mr-2"
|
||
/>
|
||
<span className="text-sm font-medium text-gray-700">Aktif</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Özel Koşullar */}
|
||
<div className="mt-4">
|
||
<h4 className="text-sm font-medium text-gray-900 border-b pb-1.5 mb-2">
|
||
Özel Koşullar
|
||
</h4>
|
||
<textarea
|
||
name="specialConditions"
|
||
value={formData.specialConditions?.join('\n') || ''}
|
||
onChange={(e) => handleSpecialConditionsChange(e.target.value)}
|
||
readOnly={isReadOnly}
|
||
rows={3}
|
||
placeholder="Her satıra bir özel koşul yazın..."
|
||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
|
||
{/* Performans Metrikleri - Sadece görüntüleme modu */}
|
||
{mode === 'view' && formData.performanceMetrics && (
|
||
<div className="mt-4">
|
||
<h4 className="text-sm font-medium text-gray-900 border-b pb-1.5 mb-3 flex items-center">
|
||
<FaStar className="mr-2 text-yellow-500" />
|
||
Performans Metrikleri
|
||
</h4>
|
||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||
<div className="text-center">
|
||
<div className="text-2xl font-bold text-blue-600">
|
||
{formData.performanceMetrics.deliveryPerformance}%
|
||
</div>
|
||
<div className="text-sm text-gray-600">Teslimat Performansı</div>
|
||
</div>
|
||
<div className="text-center">
|
||
<div className="text-2xl font-bold text-green-600">
|
||
{formData.performanceMetrics.qualityRating}%
|
||
</div>
|
||
<div className="text-sm text-gray-600">Kalite Değerlendirmesi</div>
|
||
</div>
|
||
<div className="text-center">
|
||
<div className="text-2xl font-bold text-purple-600">
|
||
{formData.performanceMetrics.priceCompetitiveness}%
|
||
</div>
|
||
<div className="text-sm text-gray-600">Fiyat Rekabeti</div>
|
||
</div>
|
||
<div className="text-center">
|
||
<div className="text-2xl font-bold text-indigo-600">
|
||
{formData.performanceMetrics.responsiveness}%
|
||
</div>
|
||
<div className="text-sm text-gray-600">Yanıt Hızı</div>
|
||
</div>
|
||
<div className="text-center">
|
||
<div className="text-2xl font-bold text-red-600">
|
||
{formData.performanceMetrics.complianceRating}%
|
||
</div>
|
||
<div className="text-sm text-gray-600">Uyum Derecesi</div>
|
||
</div>
|
||
<div className="text-center">
|
||
<div className="text-2xl font-bold text-yellow-600">
|
||
{formData.performanceMetrics.overallScore}%
|
||
</div>
|
||
<div className="text-sm text-gray-600">Genel Puan</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Action Buttons */}
|
||
<div className="flex justify-end space-x-2 mt-4 pt-3 border-t">
|
||
<button
|
||
type="button"
|
||
onClick={onClose}
|
||
className="px-3 py-1.5 border border-gray-300 rounded-md text-sm text-gray-700 hover:bg-gray-50"
|
||
>
|
||
{mode === 'view' ? 'Kapat' : 'İptal'}
|
||
</button>
|
||
{mode !== 'view' && (
|
||
<button
|
||
type="submit"
|
||
className="px-3 py-1.5 bg-blue-600 text-white rounded-md text-sm hover:bg-blue-700 flex items-center"
|
||
>
|
||
<FaSave className="mr-2" />
|
||
Kaydet
|
||
</button>
|
||
)}
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default SupplierCardModal
|