412 lines
16 KiB
TypeScript
412 lines
16 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";
|
|||
|
|
|
|||
|
|
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.Material,
|
|||
|
|
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"
|
|||
|
|
>
|
|||
|
|
<option value={SupplierCardTypeEnum.Standard}>
|
|||
|
|
Standart
|
|||
|
|
</option>
|
|||
|
|
<option value={SupplierCardTypeEnum.Premium}>Premium</option>
|
|||
|
|
<option value={SupplierCardTypeEnum.Strategic}>
|
|||
|
|
Stratejik
|
|||
|
|
</option>
|
|||
|
|
<option value={SupplierCardTypeEnum.Preferred}>
|
|||
|
|
Tercihli
|
|||
|
|
</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"
|
|||
|
|
>
|
|||
|
|
<option value={PaymentTerms.Net15}>15 Gün Vadeli</option>
|
|||
|
|
<option value={PaymentTerms.Net30}>30 Gün Vadeli</option>
|
|||
|
|
<option value={PaymentTerms.Net45}>45 Gün Vadeli</option>
|
|||
|
|
<option value={PaymentTerms.Net60}>60 Gün Vadeli</option>
|
|||
|
|
<option value={PaymentTerms.Net90}>90 Gün Vadeli</option>
|
|||
|
|
<option value={PaymentTerms.COD}>Kapıda Ödeme</option>
|
|||
|
|
<option value={PaymentTerms.Prepaid}>Peşin</option>
|
|||
|
|
<option value={PaymentTerms.Cash}>Nakit</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;
|