erp-platform/ui/src/views/crm/components/SalesOrderView.tsx

417 lines
18 KiB
TypeScript
Raw Normal View History

2025-09-15 14:13:20 +00:00
import React, { useState, useEffect } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
2025-09-15 09:31:47 +00:00
import {
FaEdit,
FaArrowLeft,
FaPrint,
FaDownload,
FaShoppingCart,
FaCalendar,
FaBuilding,
FaUser,
FaPhone,
FaEnvelope,
FaMapMarkerAlt,
FaClipboardList,
FaCalculator,
FaTruck,
FaFileInvoiceDollar,
FaExclamationCircle,
2025-09-15 14:13:20 +00:00
} from 'react-icons/fa'
import { CrmSalesOrder, CrmSalesOrderItem, SaleOrderItemStatusEnum } from '../../../types/crm'
import { mockSalesOrders } from '../../../mocks/mockSalesOrders'
2025-09-15 09:31:47 +00:00
import {
getSaleOrderItemStatusnfo,
getSaleOrderStatusColor,
getSaleOrderStatusText,
2025-09-15 14:13:20 +00:00
} from '../../../utils/erp'
import { Container } from '@/components/shared'
2025-09-16 12:33:57 +00:00
import { ROUTES_ENUM } from '@/routes/route.constant'
2025-09-15 09:31:47 +00:00
const SalesOrderView: React.FC = () => {
2025-09-15 14:13:20 +00:00
const navigate = useNavigate()
const { id } = useParams()
2025-09-15 09:31:47 +00:00
2025-09-15 14:13:20 +00:00
const [order, setOrder] = useState<CrmSalesOrder | null>(null)
const [loading, setLoading] = useState(true)
2025-09-15 09:31:47 +00:00
useEffect(() => {
if (id) {
// Find the order from mock data
setTimeout(() => {
2025-09-15 14:13:20 +00:00
const foundOrder = mockSalesOrders.find((order) => order.id === id)
2025-09-15 09:31:47 +00:00
if (foundOrder) {
2025-09-15 14:13:20 +00:00
setOrder(foundOrder)
2025-09-15 09:31:47 +00:00
}
2025-09-15 14:13:20 +00:00
setLoading(false)
}, 1000)
2025-09-15 09:31:47 +00:00
}
2025-09-15 14:13:20 +00:00
}, [id])
2025-09-15 09:31:47 +00:00
if (loading) {
return (
<div className="flex items-center justify-center h-64">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
2025-09-15 14:13:20 +00:00
)
2025-09-15 09:31:47 +00:00
}
if (!order) {
return (
<div className="flex flex-col items-center justify-center h-64">
<FaExclamationCircle className="text-4xl text-red-500 mb-4" />
2025-09-15 14:13:20 +00:00
<h2 className="text-xl font-semibold text-gray-900 mb-2">Sipariş Bulunamadı</h2>
2025-09-15 09:31:47 +00:00
<p className="text-gray-600 mb-4">Belirtilen sipariş mevcut değil.</p>
<button
2025-09-16 12:33:57 +00:00
onClick={() => navigate(ROUTES_ENUM.protected.crm.salesOrders)}
2025-09-15 09:31:47 +00:00
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
>
Sipariş Listesine Dön
</button>
</div>
2025-09-15 14:13:20 +00:00
)
2025-09-15 09:31:47 +00:00
}
return (
2025-09-15 14:13:20 +00:00
<Container>
2025-09-15 19:22:43 +00:00
<div className="space-y-2">
2025-09-15 14:13:20 +00:00
{/* Header */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-4">
<button
2025-09-16 12:33:57 +00:00
onClick={() => navigate(ROUTES_ENUM.protected.crm.salesOrders)}
2025-09-15 14:13:20 +00:00
className="flex items-center text-gray-600 hover:text-gray-800 transition-colors"
>
<FaArrowLeft className="mr-2" />
Geri
</button>
<div className="h-6 border-l border-gray-300"></div>
<div>
<h1 className="text-xl font-bold text-gray-900 flex items-center">
<FaShoppingCart className="mr-3 text-blue-600" />
{order.orderNumber}
</h1>
<p className="text-xs text-gray-500 mt-1">Satış Siparişi Detayları</p>
</div>
2025-09-15 09:31:47 +00:00
</div>
2025-09-15 14:13:20 +00:00
<div className="flex items-center space-x-2">
<span
className={`inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium ${getSaleOrderStatusColor(
order.status,
)}`}
>
{getSaleOrderStatusText(order.status)}
</span>
<button
2025-09-16 12:33:57 +00:00
onClick={() =>
navigate(ROUTES_ENUM.protected.crm.salesOrdersEdit.replace(':id', order.id))
}
2025-09-15 14:13:20 +00:00
className="bg-blue-600 text-white px-3 py-1.5 text-sm rounded-lg hover:bg-blue-700 transition-colors flex items-center"
>
<FaEdit className="mr-2" />
Düzenle
</button>
<button className="bg-gray-600 text-white px-3 py-1.5 text-sm rounded-lg hover:bg-gray-700 transition-colors flex items-center">
<FaPrint className="mr-2" />
Yazdır
</button>
<button className="bg-green-600 text-white px-3 py-1.5 text-sm rounded-lg hover:bg-green-700 transition-colors flex items-center">
<FaDownload className="mr-2" />
İndir
</button>
</div>
2025-09-15 09:31:47 +00:00
</div>
2025-09-15 14:13:20 +00:00
{/* Order Info Cards */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-3 mb-4">
<div className="bg-gradient-to-r from-blue-50 to-blue-100 p-3 rounded-lg border border-blue-200">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-blue-800">Sipariş Tarihi</p>
<p className="text-base font-bold text-blue-900">
{new Date(order.orderDate).toLocaleDateString('tr-TR')}
</p>
</div>
<FaCalendar className="text-2xl text-blue-600" />
2025-09-15 09:31:47 +00:00
</div>
</div>
2025-09-15 14:13:20 +00:00
<div className="bg-gradient-to-r from-green-50 to-green-100 p-3 rounded-lg border border-green-200">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-green-800">Talep Edilen Tarih</p>
<p className="text-base font-bold text-green-900">
{new Date(order.requestedDeliveryDate).toLocaleDateString('tr-TR')}
</p>
</div>
<FaCalendar className="text-2xl text-green-600" />
2025-09-15 09:31:47 +00:00
</div>
</div>
2025-09-15 14:13:20 +00:00
<div className="bg-gradient-to-r from-purple-50 to-purple-100 p-3 rounded-lg border border-purple-200">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-purple-800">Onaylanan Tarih</p>
<p className="text-base font-bold text-purple-900">
{order.confirmedDeliveryDate
? new Date(order.confirmedDeliveryDate).toLocaleDateString('tr-TR')
: 'Belirtilmemiş'}
</p>
</div>
<FaCalendar className="text-2xl text-purple-600" />
2025-09-15 09:31:47 +00:00
</div>
</div>
2025-09-15 14:13:20 +00:00
<div className="bg-gradient-to-r from-amber-50 to-amber-100 p-3 rounded-lg border border-amber-200">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-amber-800">Toplam Tutar</p>
<p className="text-base font-bold text-amber-900">
{order.totalAmount.toLocaleString('tr-TR')} {order.currency}
</p>
</div>
<FaCalculator className="text-2xl text-amber-600" />
2025-09-15 09:31:47 +00:00
</div>
</div>
</div>
2025-09-15 14:13:20 +00:00
{/* Additional Info */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 text-sm">
<div>
<span className="font-medium text-gray-700">Ödeme Koşulları:</span>
<p className="text-gray-900">{order.paymentTerms}</p>
</div>
<div>
<span className="font-medium text-gray-700">Para Birimi:</span>
<p className="text-gray-900">{order.currency}</p>
</div>
2025-09-15 09:31:47 +00:00
</div>
2025-09-15 14:13:20 +00:00
<div className="mt-3 pt-3 border-t border-gray-200">
<div className="text-sm text-gray-600">
<span className="font-medium">Son Güncelleme:</span>
<div className="mt-1">
{order.lastModificationTime
? new Date(order.lastModificationTime).toLocaleString('tr-TR')
: 'Güncelleme yok'}
</div>
2025-09-15 09:31:47 +00:00
</div>
</div>
</div>
2025-09-15 14:13:20 +00:00
{/* Customer Information */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
<h2 className="text-lg font-semibold text-gray-900 mb-3 flex items-center">
<FaBuilding className="mr-3 text-blue-600" />
Müşteri Bilgileri
</h2>
2025-09-15 09:31:47 +00:00
2025-09-15 14:13:20 +00:00
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<h3 className="text-base font-medium text-gray-800 mb-2 flex items-center">
<FaUser className="mr-2 text-gray-600" />
Müşteri Detayları
</h3>
<div className="space-y-2">
<div>
<span className="text-sm font-medium text-gray-600">Firma Adı:</span>
<p className="text-sm font-medium text-gray-900">
{order.customer?.name || 'Müşteri bilgisi yok'}
</p>
</div>
<div>
<span className="text-sm font-medium text-gray-600">İletişim Kişisi:</span>
<p className="text-sm text-gray-900">
{order.customer?.primaryContact?.fullName || 'Belirtilmemiş'}
</p>
</div>
<div className="flex items-center text-sm text-gray-600">
<FaPhone className="mr-2" />
<span>{order.customer?.primaryContact?.phone || 'Belirtilmemiş'}</span>
</div>
<div className="flex items-center text-sm text-gray-600">
<FaEnvelope className="mr-2" />
<span>{order.customer?.primaryContact?.email || 'Belirtilmemiş'}</span>
</div>
2025-09-15 09:31:47 +00:00
</div>
</div>
2025-09-15 14:13:20 +00:00
<div>
<h3 className="text-base font-medium text-gray-800 mb-2 flex items-center">
<FaMapMarkerAlt className="mr-2 text-gray-600" />
Teslimat Adresi
</h3>
<div className="text-sm text-gray-900 space-y-1">
<p>{order.deliveryAddress.street}</p>
<p>
{order.deliveryAddress.city}, {order.deliveryAddress.state}
</p>
<p>{order.deliveryAddress.postalCode}</p>
<p>{order.deliveryAddress.country}</p>
</div>
2025-09-15 09:31:47 +00:00
</div>
</div>
</div>
2025-09-15 14:13:20 +00:00
{/* Order Items */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
<h2 className="text-lg font-semibold text-gray-900 mb-3 flex items-center">
<FaClipboardList className="mr-3 text-blue-600" />
Sipariş Kalemleri
</h2>
2025-09-15 09:31:47 +00:00
2025-09-15 14:13:20 +00:00
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Malzeme
</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Miktar
</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Teslim Edilen
</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Birim Fiyat
</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Toplam
</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Durum
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{order.items.map((item: CrmSalesOrderItem) => {
const itemStatus = getSaleOrderItemStatusnfo(item.status)
return (
<tr key={item.id} className="hover:bg-gray-50">
<td className="px-4 py-2 whitespace-nowrap">
<div>
<div className="text-sm font-medium text-gray-900">
{item.material?.code || item.materialId}
</div>
<div className="text-xs text-gray-500">
{item.material?.name || item.description}
</div>
</div>
</td>
<td className="px-4 py-2 whitespace-nowrap text-sm text-gray-900">
{item.quantity} {item.material?.baseUnit?.name || 'Adet'}
</td>
<td className="px-4 py-2 whitespace-nowrap">
<div className="font-medium">
{item.deliveredQuantity} {item.material?.baseUnit?.name || 'Adet'}
2025-09-15 09:31:47 +00:00
</div>
<div className="text-xs text-gray-500">
2025-09-15 14:13:20 +00:00
{((item.deliveredQuantity / item.quantity) * 100).toFixed(1)}% tamamlandı
2025-09-15 09:31:47 +00:00
</div>
2025-09-15 14:13:20 +00:00
</td>
<td className="px-4 py-2 whitespace-nowrap text-sm text-gray-900">
{item.unitPrice.toLocaleString('tr-TR')} {order.currency}
</td>
<td className="px-4 py-2 whitespace-nowrap text-sm font-medium text-gray-900">
{item.totalAmount.toLocaleString('tr-TR')} {order.currency}
</td>
<td className="px-4 py-2 whitespace-nowrap">
<span
className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${itemStatus.color}`}
>
<itemStatus.icon className={`mr-1 ${itemStatus.iconColor}`} />
{itemStatus.label}
</span>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
2025-09-15 09:31:47 +00:00
</div>
2025-09-15 14:13:20 +00:00
{/* Financial Summary */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
<h2 className="text-lg font-semibold text-gray-900 mb-3 flex items-center">
<FaCalculator className="mr-3 text-blue-600" />
Mali Özet
</h2>
2025-09-15 09:31:47 +00:00
2025-09-15 14:13:20 +00:00
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<div className="flex justify-between py-1.5 border-b border-gray-100">
<span className="text-sm font-medium text-gray-600">Ara Toplam:</span>
<span className="text-sm text-gray-900">
{order.subtotal.toLocaleString('tr-TR')} {order.currency}
</span>
</div>
<div className="flex justify-between py-1.5 border-b border-gray-100">
<span className="text-sm font-medium text-gray-600">İndirim:</span>
<span className="text-sm text-gray-900">
-{order.discountAmount.toLocaleString('tr-TR')} {order.currency}
</span>
</div>
<div className="flex justify-between py-1.5 border-b border-gray-100">
<span className="text-sm font-medium text-gray-600">KDV:</span>
<span className="text-sm text-gray-900">
{order.taxAmount.toLocaleString('tr-TR')} {order.currency}
</span>
</div>
<div className="flex justify-between py-2 border-t-2 border-gray-200">
<span className="text-base font-bold text-gray-900">Genel Toplam:</span>
<span className="text-base font-bold text-gray-900">
{order.totalAmount.toLocaleString('tr-TR')} {order.currency}
</span>
</div>
2025-09-15 09:31:47 +00:00
</div>
2025-09-15 14:13:20 +00:00
<div className="space-y-4">
<div className="bg-blue-50 p-4 rounded-lg border border-blue-200">
<h3 className="text-sm font-medium text-blue-800 mb-2 flex items-center">
<FaTruck className="mr-2" />
Teslimat Durumu
</h3>
<div className="text-sm text-blue-900">
<p>Toplam Kalem: {order.items.length}</p>
<p>
Teslim Edilenler:{' '}
{
order.items.filter(
(item) => item.status === SaleOrderItemStatusEnum.Delivered,
).length
}
</p>
<p>
Bekleyenler:{' '}
{
order.items.filter(
(item) => item.status !== SaleOrderItemStatusEnum.Delivered,
).length
}
</p>
</div>
2025-09-15 09:31:47 +00:00
</div>
2025-09-15 14:13:20 +00:00
<div className="bg-green-50 p-4 rounded-lg border border-green-200">
<h3 className="text-sm font-medium text-green-800 mb-2 flex items-center">
<FaFileInvoiceDollar className="mr-2" />
Faturalama Durumu
</h3>
<div className="text-sm text-green-900">
<p>Teslimat Sayısı: {order.deliveries?.length || 0}</p>
</div>
2025-09-15 09:31:47 +00:00
</div>
</div>
</div>
</div>
</div>
2025-09-15 14:13:20 +00:00
</Container>
)
}
2025-09-15 09:31:47 +00:00
2025-09-15 14:13:20 +00:00
export default SalesOrderView