erp-platform/ui/src/views/mrp/components/ProductionOrderView.tsx
2025-09-15 22:22:43 +03:00

449 lines
20 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useMemo } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import { useQuery } from '@tanstack/react-query'
import { mockProductionOrders } from '../../../mocks/mockProductionOrders'
import { MrpProductionOrder, MrpProductionOrderMaterial } from '../../../types/mrp'
import {
FaCog,
FaCalendarAlt,
FaClipboardList,
FaTruck,
FaMoneyBillWave,
FaArrowLeft,
FaExclamationTriangle,
} from 'react-icons/fa'
import StatusBadge from '../../../components/common/StatusBadge'
import { getPriorityColor } from '../../../utils/erp'
import { Container } from '@/components/shared'
const ProductionOrderView: React.FC = () => {
const { id } = useParams()
const navigate = useNavigate()
const { data: productionOrder, isLoading } = useQuery({
queryKey: ['production-order', id],
queryFn: async () => {
await new Promise((r) => setTimeout(r, 200))
return mockProductionOrders.find((p) => p.id === id) as MrpProductionOrder | undefined
},
enabled: !!id,
})
const materials: MrpProductionOrderMaterial[] = useMemo(
() => productionOrder?.materials || [],
[productionOrder],
)
if (isLoading) {
return (
<div className="flex justify-center items-center min-h-screen">
<div className="flex flex-col items-center space-y-4">
<div className="animate-spin rounded-full h-16 w-16 border-4 border-blue-200 border-t-blue-600"></div>
<p className="text-gray-600 font-medium">Üretim emri yükleniyor...</p>
</div>
</div>
)
}
if (!productionOrder) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-gray-50 to-gray-100">
<div className="text-center bg-white p-8 rounded-2xl shadow-xl max-w-md">
<div className="mb-6">
<FaExclamationTriangle className="mx-auto h-16 w-16 text-gray-400" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">Üretim Emri Bulunamadı</h2>
<p className="text-gray-600 mb-6">
İstenen üretim emri mevcut değil veya silinmiş olabilir.
</p>
<button
onClick={() => navigate('/admin/mrp/production-orders')}
className="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-600 to-blue-700 text-white font-medium rounded-lg hover:from-blue-700 hover:to-blue-800 transition-all duration-200 shadow-lg hover:shadow-xl"
>
<FaArrowLeft className="mr-2" />
Üretim Emirleri Listesine Dön
</button>
</div>
</div>
)
}
const getProgressPercentage = () => {
if (productionOrder.plannedQuantity === 0) return 0
return Math.round((productionOrder.confirmedQuantity / productionOrder.plannedQuantity) * 100)
}
return (
<Container>
<div className="space-y-2">
{/* Header */}
<div className="mb-6">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<button
onClick={() => navigate('/admin/mrp/production-orders')}
className="inline-flex items-center px-3 py-2 bg-white text-gray-700 font-medium rounded-lg hover:bg-gray-50 transition-all duration-200 shadow-sm border border-gray-200"
>
<FaArrowLeft className="mr-2 text-sm" />
Geri
</button>
<div>
<h1 className="text-2xl font-bold text-gray-900">Üretim Emri Detayları</h1>
<p className="text-gray-600 mt-1 text-sm">
{productionOrder.orderNumber} - Detaylı bilgiler
</p>
</div>
</div>
</div>
</div>
{/* Order Header Card */}
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 mb-4">
<div className="flex flex-wrap justify-between items-start mb-4">
<div className="space-y-2">
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide">
Üretim Emri No
</div>
<div className="text-xl font-bold text-gray-900">{productionOrder.orderNumber}</div>
</div>
<div className="space-y-2 text-right">
<div className="text-sm font-medium text-gray-500 uppercase tracking-wide">Durum</div>
<StatusBadge status={productionOrder.status} />
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<div className="bg-gradient-to-br from-blue-50 to-blue-100 p-4 rounded-xl border border-blue-200">
<div className="text-sm font-medium text-blue-700 mb-2 uppercase tracking-wide">
Öncelik
</div>
<span
className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold border ${getPriorityColor(
productionOrder.priority,
)}`}
>
{productionOrder.priority}
</span>
</div>
<div className="bg-gradient-to-br from-purple-50 to-purple-100 p-4 rounded-xl border border-purple-200">
<div className="text-sm font-medium text-purple-700 mb-2 uppercase tracking-wide">
Tür
</div>
<div className="text-base font-semibold text-purple-900">
{productionOrder.orderType}
</div>
</div>
<div className="bg-gradient-to-br from-orange-50 to-orange-100 p-4 rounded-xl border border-orange-200">
<div className="text-sm font-medium text-orange-700 mb-2 uppercase tracking-wide">
İlerleme
</div>
<div className="text-xl font-bold text-orange-900">{getProgressPercentage()}%</div>
</div>
</div>
{/* Progress Bar */}
<div className="mb-6">
<div className="flex items-center justify-between mb-3">
<div className="text-sm font-medium text-gray-700">Üretim İlerlemesi</div>
<div className="text-sm text-gray-500">
{productionOrder.confirmedQuantity} / {productionOrder.plannedQuantity} adet
</div>
</div>
<div className="w-full bg-gray-200 rounded-full h-3 overflow-hidden">
<div
className="bg-gradient-to-r from-blue-500 to-blue-600 h-3 rounded-full transition-all duration-500 ease-out"
style={{ width: `${getProgressPercentage()}%` }}
></div>
</div>
</div>
{productionOrder.customerRequirement && (
<div className="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl border border-blue-200">
<div className="flex items-center mb-3">
<FaClipboardList className="text-blue-600 mr-2" />
<div className="text-sm font-semibold text-blue-800 uppercase tracking-wide">
Müşteri Talebi
</div>
</div>
<p className="text-blue-700 leading-relaxed">{productionOrder.customerRequirement}</p>
</div>
)}
</div>
{/* Info Cards Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
{/* Quantities Card */}
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
<div className="flex items-center gap-2 mb-2">
<div className="p-1.5 bg-blue-100 rounded-lg">
<FaCog className="text-blue-600 text-base" />
</div>
<h3 className="text-base font-bold text-gray-900">Miktar Bilgileri</h3>
</div>
<div className="space-y-2">
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
<span className="text-gray-600 font-medium">Planlanan:</span>
<span className="font-bold text-gray-900">{productionOrder.plannedQuantity}</span>
</div>
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
<span className="text-gray-600 font-medium">Üretilen:</span>
<span className="font-bold text-green-600">
{productionOrder.confirmedQuantity}
</span>
</div>
<div className="flex justify-between items-center py-1.5 border-b border-gray-100">
<span className="text-gray-600 font-medium">Gereken:</span>
<span className="font-bold text-blue-600">{productionOrder.requiredQuantity}</span>
</div>
<div className="flex justify-between items-center py-2">
<span className="text-gray-600 font-medium">Fire:</span>
<span className="font-bold text-red-600">{productionOrder.scrapQuantity}</span>
</div>
</div>
</div>
{/* Schedule Card */}
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
<div className="flex items-center gap-2 mb-2">
<div className="p-1.5 bg-purple-100 rounded-lg">
<FaCalendarAlt className="text-purple-600 text-base" />
</div>
<h3 className="text-base font-bold text-gray-900">Zamanlama</h3>
</div>
<div className="space-y-2">
<div className="py-1.5 border-b border-gray-100">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Plan Başlangıç
</div>
<div className="font-semibold text-gray-900">
{new Date(productionOrder.plannedStartDate).toLocaleDateString('tr-TR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</div>
</div>
<div className="py-1.5 border-b border-gray-100">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Plan Bitiş
</div>
<div className="font-semibold text-gray-900">
{new Date(productionOrder.plannedEndDate).toLocaleDateString('tr-TR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</div>
</div>
{productionOrder.actualStartDate && (
<div className="py-1.5 border-b border-gray-100">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Gerçek Başlangıç
</div>
<div className="font-semibold text-green-600">
{new Date(productionOrder.actualStartDate).toLocaleDateString('tr-TR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</div>
</div>
)}
{productionOrder.actualEndDate && (
<div className="py-1.5">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Gerçek Bitiş
</div>
<div className="font-semibold text-green-600">
{new Date(productionOrder.actualEndDate).toLocaleDateString('tr-TR', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</div>
</div>
)}
</div>
</div>
{/* Costs Card */}
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
<div className="flex items-center gap-2 mb-2">
<div className="p-1.5 bg-green-100 rounded-lg">
<FaMoneyBillWave className="text-green-600 text-base" />
</div>
<h3 className="text-base font-bold text-gray-900">Maliyet Bilgileri</h3>
</div>
<div className="space-y-2">
<div className="py-1.5 border-b border-gray-100">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Planlanan Maliyet
</div>
<div className="text-lg font-bold text-gray-900">
{new Intl.NumberFormat('tr-TR', {
style: 'currency',
currency: productionOrder.currency,
}).format(productionOrder.plannedCost)}
</div>
</div>
<div className="py-1.5 border-b border-gray-100">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Gerçekleşen Maliyet
</div>
<div className="text-lg font-bold text-green-600">
{new Intl.NumberFormat('tr-TR', {
style: 'currency',
currency: productionOrder.currency,
}).format(productionOrder.actualCost)}
</div>
</div>
<div className="py-1.5">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Para Birimi
</div>
<div className="font-semibold text-gray-900">{productionOrder.currency}</div>
</div>
</div>
</div>
{/* Metadata Card */}
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4 hover:shadow-xl transition-all duration-200">
<div className="flex items-center gap-2 mb-2">
<div className="p-1.5 bg-gray-100 rounded-lg">
<FaClipboardList className="text-gray-600 text-base" />
</div>
<h3 className="text-base font-bold text-gray-900">Sistem Bilgileri</h3>
</div>
<div className="space-y-2">
<div className="py-1.5 border-b border-gray-100">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Oluşturulma
</div>
<div className="font-semibold text-gray-900">
{new Date(productionOrder.creationTime).toLocaleDateString('tr-TR', {
year: 'numeric',
month: 'short',
day: 'numeric',
})}
</div>
</div>
<div className="py-1.5 border-b border-gray-100">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Son Güncelleme
</div>
<div className="font-semibold text-gray-900">
{new Date(productionOrder.lastModificationTime).toLocaleDateString('tr-TR', {
year: 'numeric',
month: 'short',
day: 'numeric',
})}
</div>
</div>
</div>
</div>
</div>
{/* Materials List */}
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-4">
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2">
<div className="p-1.5 bg-indigo-100 rounded-lg">
<FaTruck className="text-indigo-600 text-base" />
</div>
<div>
<h3 className="text-base font-bold text-gray-900">Malzemeler</h3>
<p className="text-gray-600 mt-1 text-sm">Üretim emrine bağlı malzeme satırları</p>
</div>
</div>
<div className="text-right">
<div className="text-lg font-bold text-indigo-600">{materials.length}</div>
<div className="text-sm text-gray-500">Malzeme</div>
</div>
</div>
{materials.length === 0 ? (
<div className="p-8 text-center bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl border-2 border-dashed border-gray-300">
<FaTruck className="mx-auto h-16 w-16 text-gray-400 mb-4" />
<p className="text-gray-500 text-base font-medium">
Bu üretim emrine bağlı malzeme bulunmamaktadır.
</p>
</div>
) : (
<div className="space-y-3 max-h-96 overflow-y-auto">
{materials.map((m: MrpProductionOrderMaterial) => (
<div
key={m.id}
className="flex flex-col lg:flex-row lg:items-center justify-between p-4 border border-gray-200 rounded-xl hover:bg-gradient-to-r hover:from-blue-50 hover:to-indigo-50 hover:border-blue-300 transition-all duration-200 group"
>
<div className="mb-4 lg:mb-0 flex-1">
<div className="flex items-center gap-2 mb-2">
<div className="p-1.5 bg-blue-100 rounded-lg group-hover:bg-blue-200 transition-colors">
<FaTruck className="text-blue-600" />
</div>
<div>
<div className="text-gray-600">
{m.salesOrder?.orderNumber || m.salesOrder?.id}
</div>
<div className="font-bold text-gray-900 text-base">
{m.material?.code || m.materialId}
{' - '}
{m.material?.name || 'Malzeme adı bulunamadı'}
</div>
</div>
</div>
{m.customerRequirement && (
<div className="text-sm text-blue-600 italic rounded-lg mt-3">
"{m.customerRequirement}"
</div>
)}
</div>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 lg:gap-4">
<div className="text-center p-2 bg-gray-50 rounded-lg">
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-1">
Planlanan
</div>
<div className="font-bold text-gray-900 text-base">{m.plannedQuantity}</div>
</div>
<div className="text-center p-2 bg-green-50 rounded-lg">
<div className="text-xs font-medium text-green-600 uppercase tracking-wide mb-1">
Üretilen
</div>
<div className="font-bold text-green-700 text-base">
{m.confirmedQuantity}
</div>
</div>
<div className="text-center p-2 bg-blue-50 rounded-lg">
<div className="text-xs font-medium text-blue-600 uppercase tracking-wide mb-1">
Gereken
</div>
<div className="font-bold text-blue-700 text-base">{m.requiredQuantity}</div>
</div>
<div className="text-center p-2 bg-red-50 rounded-lg">
<div className="text-xs font-medium text-red-600 uppercase tracking-wide mb-1">
Fire
</div>
<div className="font-bold text-red-700 text-base">{m.scrapQuantity}</div>
</div>
</div>
</div>
))}
</div>
)}
</div>
</div>
</Container>
)
}
export default ProductionOrderView