erp-platform/ui/src/views/supplychain/components/SupplierCardModal.tsx

371 lines
14 KiB
TypeScript
Raw Normal View History

2025-09-15 21:42:39 +00:00
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'
2025-09-17 09:46:58 +00:00
import { getPaymentTermsText, getSupplierCardTypeText } from '@/utils/erp'
2025-09-15 09:31:47 +00:00
interface SupplierCardModalProps {
2025-09-15 21:42:39 +00:00
isOpen: boolean
onClose: () => void
onSave: (supplierCard: BusinessParty) => void
supplierCard?: BusinessParty | null
mode: 'create' | 'view' | 'edit'
2025-09-15 09:31:47 +00:00
}
const SupplierCardModal: React.FC<SupplierCardModalProps> = ({
isOpen,
onClose,
onSave,
supplierCard,
mode,
}) => {
2025-09-15 21:42:39 +00:00
const [formData, setFormData] = useState<Partial<BusinessParty>>(mockBusinessPartyNew)
2025-09-15 09:31:47 +00:00
useEffect(() => {
2025-09-15 21:42:39 +00:00
if (mode === 'create') {
setFormData(mockBusinessPartyNew)
2025-09-15 09:31:47 +00:00
} else if (supplierCard) {
2025-09-15 21:42:39 +00:00
setFormData(supplierCard)
2025-09-15 09:31:47 +00:00
}
2025-09-15 21:42:39 +00:00
}, [supplierCard, mode])
2025-09-15 09:31:47 +00:00
const handleInputChange = (
2025-09-15 21:42:39 +00:00
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
2025-09-15 09:31:47 +00:00
) => {
2025-09-15 21:42:39 +00:00
const { name, value, type } = e.target
2025-09-15 09:31:47 +00:00
setFormData((prev) => ({
...prev,
[name]:
2025-09-15 21:42:39 +00:00
type === 'number'
2025-09-15 09:31:47 +00:00
? parseFloat(value) || 0
2025-09-15 21:42:39 +00:00
: type === 'checkbox'
? (e.target as HTMLInputElement).checked
: value,
}))
}
2025-09-15 09:31:47 +00:00
const handleSpecialConditionsChange = (value: string) => {
2025-09-15 21:42:39 +00:00
const conditions = value.split('\n').filter((condition) => condition.trim() !== '')
2025-09-15 09:31:47 +00:00
setFormData((prev) => ({
...prev,
specialConditions: conditions,
2025-09-15 21:42:39 +00:00
}))
}
2025-09-15 09:31:47 +00:00
const handleSubmit = (e: React.FormEvent) => {
2025-09-15 21:42:39 +00:00
e.preventDefault()
if (mode !== 'view') {
2025-09-15 09:31:47 +00:00
const newSupplierCard: BusinessParty = {
id: supplierCard?.id || `SC-${Date.now()}`,
2025-09-15 21:42:39 +00:00
code: formData.code || '',
2025-09-15 09:31:47 +00:00
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(),
2025-09-17 09:46:58 +00:00
supplierType: SupplierTypeEnum.Distributor,
2025-09-15 21:42:39 +00:00
name: '',
currency: '',
2025-09-15 09:31:47 +00:00
certifications: [],
bankAccounts: [],
contacts: [],
partyType: PartyType.Supplier,
2025-09-15 21:42:39 +00:00
}
onSave(newSupplierCard)
2025-09-15 09:31:47 +00:00
}
2025-09-15 21:42:39 +00:00
onClose()
}
2025-09-15 09:31:47 +00:00
2025-09-15 21:42:39 +00:00
if (!isOpen) return null
2025-09-15 09:31:47 +00:00
2025-09-15 21:42:39 +00:00
const isReadOnly = mode === 'view'
2025-09-15 09:31:47 +00:00
const modalTitle =
2025-09-15 21:42:39 +00:00
mode === 'create'
? 'Yeni Tedarikçi Kartı'
: mode === 'edit'
? 'Tedarikçi Kartını Düzenle'
: 'Tedarikçi Kartı Detayları'
2025-09-15 09:31:47 +00:00
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>
2025-09-15 21:42:39 +00:00
<button onClick={onClose} className="p-1 text-gray-400 hover:text-gray-600">
2025-09-15 09:31:47 +00:00
<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">
2025-09-15 21:42:39 +00:00
<h4 className="text-sm font-medium text-gray-900 border-b pb-1.5">Temel Bilgiler</h4>
2025-09-15 09:31:47 +00:00
<div>
2025-09-15 21:42:39 +00:00
<label className="block text-sm font-medium text-gray-700">Tedarikçi</label>
2025-09-15 09:31:47 +00:00
<input
type="text"
name="customerCode"
2025-09-15 21:42:39 +00:00
value={formData.code || ''}
2025-09-15 09:31:47 +00:00
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>
2025-09-15 21:42:39 +00:00
<label className="block text-sm font-medium text-gray-700">Kart Numarası</label>
2025-09-15 09:31:47 +00:00
<input
type="text"
name="cardNumber"
2025-09-15 21:42:39 +00:00
value={formData.cardNumber || ''}
2025-09-15 09:31:47 +00:00
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>
2025-09-15 21:42:39 +00:00
<label className="block text-sm font-medium text-gray-700">Kart Tipi</label>
2025-09-15 09:31:47 +00:00
<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"
>
2025-09-17 09:46:58 +00:00
{Object.values(SupplierCardTypeEnum).map((type) => (
<option key={type} value={type}>
{getSupplierCardTypeText(type)}
</option>
))}
2025-09-15 09:31:47 +00:00
</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
2025-09-15 21:42:39 +00:00
? new Date(formData.validFrom).toISOString().split('T')[0]
: ''
2025-09-15 09:31:47 +00:00
}
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>
2025-09-15 21:42:39 +00:00
<label className="block text-sm font-medium text-gray-700">Geçerlilik Bitişi</label>
2025-09-15 09:31:47 +00:00
<input
type="date"
name="validTo"
value={
2025-09-15 21:42:39 +00:00
formData.validTo ? new Date(formData.validTo).toISOString().split('T')[0] : ''
2025-09-15 09:31:47 +00:00
}
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">
2025-09-15 21:42:39 +00:00
<h4 className="text-sm font-medium text-gray-900 border-b pb-1.5">Mali Bilgiler</h4>
2025-09-15 09:31:47 +00:00
<div>
2025-09-15 21:42:39 +00:00
<label className="block text-sm font-medium text-gray-700">Kredi Limiti (TL)</label>
2025-09-15 09:31:47 +00:00
<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>
2025-09-15 21:42:39 +00:00
<label className="block text-sm font-medium text-gray-700">Ödeme Koşulları</label>
2025-09-15 09:31:47 +00:00
<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"
>
2025-09-17 09:46:58 +00:00
{Object.values(PaymentTerms).map((term) => (
<option key={term} value={term}>
{getPaymentTermsText(term)}
</option>
))}
2025-09-15 09:31:47 +00:00
</select>
</div>
<div>
2025-09-15 21:42:39 +00:00
<label className="block text-sm font-medium text-gray-700">İndirim Oranı (%)</label>
2025-09-15 09:31:47 +00:00
<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"
/>
2025-09-15 21:42:39 +00:00
<span className="text-sm font-medium text-gray-700">Aktif</span>
2025-09-15 09:31:47 +00:00
</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"
2025-09-15 21:42:39 +00:00
value={formData.specialConditions?.join('\n') || ''}
2025-09-15 09:31:47 +00:00
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 */}
2025-09-15 21:42:39 +00:00
{mode === 'view' && formData.performanceMetrics && (
2025-09-15 09:31:47 +00:00
<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>
2025-09-15 21:42:39 +00:00
<div className="text-sm text-gray-600">Teslimat Performansı</div>
2025-09-15 09:31:47 +00:00
</div>
<div className="text-center">
<div className="text-2xl font-bold text-green-600">
{formData.performanceMetrics.qualityRating}%
</div>
2025-09-15 21:42:39 +00:00
<div className="text-sm text-gray-600">Kalite Değerlendirmesi</div>
2025-09-15 09:31:47 +00:00
</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"
>
2025-09-15 21:42:39 +00:00
{mode === 'view' ? 'Kapat' : 'İptal'}
2025-09-15 09:31:47 +00:00
</button>
2025-09-15 21:42:39 +00:00
{mode !== 'view' && (
2025-09-15 09:31:47 +00:00
<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>
2025-09-15 21:42:39 +00:00
)
}
2025-09-15 09:31:47 +00:00
2025-09-15 21:42:39 +00:00
export default SupplierCardModal