416 lines
18 KiB
TypeScript
416 lines
18 KiB
TypeScript
import React, { useState, useEffect } from 'react'
|
||
import { useNavigate, useParams } from 'react-router-dom'
|
||
import {
|
||
FaEdit,
|
||
FaArrowLeft,
|
||
FaPrint,
|
||
FaDownload,
|
||
FaShoppingCart,
|
||
FaCalendar,
|
||
FaBuilding,
|
||
FaUser,
|
||
FaPhone,
|
||
FaEnvelope,
|
||
FaMapMarkerAlt,
|
||
FaClipboardList,
|
||
FaCalculator,
|
||
FaTruck,
|
||
FaFileInvoiceDollar,
|
||
FaExclamationCircle,
|
||
} from 'react-icons/fa'
|
||
import { CrmSalesOrder, CrmSalesOrderItem, SaleOrderItemStatusEnum } from '../../../types/crm'
|
||
import { mockSalesOrders } from '../../../mocks/mockSalesOrders'
|
||
import {
|
||
getSaleOrderItemStatusnfo,
|
||
getSaleOrderStatusColor,
|
||
getSaleOrderStatusText,
|
||
} from '../../../utils/erp'
|
||
import { Container } from '@/components/shared'
|
||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||
|
||
const SalesOrderView: React.FC = () => {
|
||
const navigate = useNavigate()
|
||
const { id } = useParams()
|
||
|
||
const [order, setOrder] = useState<CrmSalesOrder | null>(null)
|
||
const [loading, setLoading] = useState(true)
|
||
|
||
useEffect(() => {
|
||
if (id) {
|
||
// Find the order from mock data
|
||
setTimeout(() => {
|
||
const foundOrder = mockSalesOrders.find((order) => order.id === id)
|
||
if (foundOrder) {
|
||
setOrder(foundOrder)
|
||
}
|
||
setLoading(false)
|
||
}, 1000)
|
||
}
|
||
}, [id])
|
||
|
||
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>
|
||
)
|
||
}
|
||
|
||
if (!order) {
|
||
return (
|
||
<div className="flex flex-col items-center justify-center h-64">
|
||
<FaExclamationCircle className="text-4xl text-red-500 mb-4" />
|
||
<h2 className="text-xl font-semibold text-gray-900 mb-2">Sipariş Bulunamadı</h2>
|
||
<p className="text-gray-600 mb-4">Belirtilen sipariş mevcut değil.</p>
|
||
<button
|
||
onClick={() => navigate(ROUTES_ENUM.protected.crm.salesOrders)}
|
||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
||
>
|
||
Sipariş Listesine Dön
|
||
</button>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<Container>
|
||
<div className="space-y-2">
|
||
{/* 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
|
||
onClick={() => navigate(ROUTES_ENUM.protected.crm.salesOrders)}
|
||
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>
|
||
</div>
|
||
|
||
<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
|
||
onClick={() =>
|
||
navigate(ROUTES_ENUM.protected.crm.salesOrdersEdit.replace(':id', order.id))
|
||
}
|
||
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>
|
||
</div>
|
||
|
||
{/* 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" />
|
||
</div>
|
||
</div>
|
||
|
||
<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" />
|
||
</div>
|
||
</div>
|
||
|
||
<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" />
|
||
</div>
|
||
</div>
|
||
|
||
<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" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 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>
|
||
</div>
|
||
|
||
<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>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 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>
|
||
|
||
<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>
|
||
</div>
|
||
</div>
|
||
|
||
<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>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 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>
|
||
|
||
<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'}
|
||
</div>
|
||
<div className="text-xs text-gray-500">
|
||
{((item.deliveredQuantity / item.quantity) * 100).toFixed(1)}% tamamlandı
|
||
</div>
|
||
</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>
|
||
</div>
|
||
|
||
{/* 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>
|
||
|
||
<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>
|
||
</div>
|
||
|
||
<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>
|
||
</div>
|
||
|
||
<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>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Container>
|
||
)
|
||
}
|
||
|
||
export default SalesOrderView
|