erp-platform/ui/src/views/maintenance/components/StartWorkOrderModal.tsx
2025-09-16 00:29:07 +03:00

307 lines
12 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, { useState } from 'react'
import { FaTimes, FaPlay, FaCalendar, FaUser } from 'react-icons/fa'
import { PmMaintenanceWorkOrder, WorkOrderStatusEnum } from '../../../types/pm'
import { mockEmployees } from '../../../mocks/mockEmployees'
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
interface StartWorkOrderModalProps {
isOpen: boolean
onClose: () => void
onStart: (workOrders: PmMaintenanceWorkOrder[], startData: StartWorkOrderData) => void
workOrders: PmMaintenanceWorkOrder[]
}
interface StartWorkOrderData {
actualStart: Date
assignedTo?: string
maintenanceTeamId?: string
notes?: string
}
const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
isOpen,
onClose,
onStart,
workOrders,
}) => {
const [formData, setFormData] = useState({
actualStart: new Date().toISOString().slice(0, 16),
assignedTo: '',
maintenanceTeamId: '',
notes: '',
})
const [errors, setErrors] = useState<Record<string, string>>({})
const validateForm = () => {
const newErrors: Record<string, string> = {}
if (!formData.actualStart) {
newErrors.actualStart = 'Başlangıç tarihi zorunludur'
}
const startDate = new Date(formData.actualStart)
const now = new Date()
if (startDate > now) {
newErrors.actualStart = 'Başlangıç tarihi gelecekte olamaz'
}
setErrors(newErrors)
return Object.keys(newErrors).length === 0
}
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
if (!validateForm()) return
const startData: StartWorkOrderData = {
actualStart: new Date(formData.actualStart),
assignedTo: formData.assignedTo || undefined,
maintenanceTeamId: formData.maintenanceTeamId || undefined,
notes: formData.notes || undefined,
}
onStart(workOrders, startData)
onClose()
}
const canStartWorkOrders = workOrders.every(
(wo) =>
wo.status === WorkOrderStatusEnum.Created ||
wo.status === WorkOrderStatusEnum.Planned ||
wo.status === WorkOrderStatusEnum.Released,
)
const inProgressWorkOrders = workOrders.filter(
(wo) => wo.status === WorkOrderStatusEnum.InProgress,
)
if (!isOpen) return null
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg p-4 w-full max-w-lg mx-4 max-h-[90vh] overflow-y-auto">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<FaPlay className="w-5 h-5 mr-2 text-green-600" />
İş Emirlerini Başlat
</h3>
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
<FaTimes className="w-5 h-5" />
</button>
</div>
{/* Work Orders Summary */}
<div className="mb-4">
<h4 className="text-base font-medium text-gray-900 mb-2">
Seçilen İş Emirleri ({workOrders.length})
</h4>
<div className="max-h-32 overflow-y-auto border border-gray-200 rounded-lg">
{workOrders.map((workOrder) => (
<div key={workOrder.id} className="p-3 border-b border-gray-100 last:border-b-0">
<div className="flex items-center justify-between">
<div>
<p className="font-medium text-sm text-gray-900">{workOrder.workOrderNumber}</p>
<p className="text-xs text-gray-600 truncate">{workOrder.description}</p>
</div>
<div className="text-right">
<span
className={`px-2 py-1 rounded-full text-xs font-medium ${
workOrder.status === WorkOrderStatusEnum.Created
? 'bg-gray-100 text-gray-800'
: workOrder.status === WorkOrderStatusEnum.Planned
? 'bg-blue-100 text-blue-800'
: workOrder.status === WorkOrderStatusEnum.Released
? 'bg-purple-100 text-purple-800'
: workOrder.status === WorkOrderStatusEnum.InProgress
? 'bg-orange-100 text-orange-800'
: 'bg-gray-100 text-gray-800'
}`}
>
{workOrder.status === WorkOrderStatusEnum.Created && 'Oluşturuldu'}
{workOrder.status === WorkOrderStatusEnum.Planned && 'Planlandı'}
{workOrder.status === WorkOrderStatusEnum.Released && 'Serbest Bırakıldı'}
{workOrder.status === WorkOrderStatusEnum.InProgress && 'Devam Ediyor'}
{workOrder.status === WorkOrderStatusEnum.OnHold && 'Beklemede'}
{workOrder.status === WorkOrderStatusEnum.Completed && 'Tamamlandı'}
{workOrder.status === WorkOrderStatusEnum.Cancelled && 'İptal Edildi'}
</span>
</div>
</div>
</div>
))}
</div>
</div>
{/* Validation Messages */}
{!canStartWorkOrders && (
<div className="mb-4 p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
<div className="flex items-start space-x-2">
<FaTimes className="w-5 h-5 text-yellow-600 mt-0.5" />
<div>
<h5 className="font-medium text-yellow-800 mb-1">Uyarı</h5>
<p className="text-yellow-700 text-sm">
Bazı emirleri başlatılamaz durumda. Sadece "Oluşturuldu", "Planlandı" veya
"Serbest Bırakıldı" durumundaki emirleri başlatılabilir.
</p>
</div>
</div>
</div>
)}
{inProgressWorkOrders.length > 0 && (
<div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
<div className="flex items-start space-x-2">
<FaPlay className="w-5 h-5 text-blue-600 mt-0.5" />
<div>
<h5 className="font-medium text-blue-800 mb-1">Bilgi</h5>
<p className="text-blue-700 text-sm">
{inProgressWorkOrders.length} emri zaten devam ediyor durumunda. Bu emirleri
için sadece atama ve not güncellemesi yapılacak.
</p>
</div>
</div>
</div>
)}
<form onSubmit={handleSubmit} className="space-y-4">
{/* Start DateTime */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<FaCalendar className="w-4 h-4 inline mr-2" />
Başlangıç Tarihi ve Saati *
</label>
<input
type="datetime-local"
value={formData.actualStart}
onChange={(e) => setFormData({ ...formData, actualStart: e.target.value })}
className={`w-full px-3 py-1.5 text-sm border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${
errors.actualStart ? 'border-red-500' : 'border-gray-300'
}`}
/>
{errors.actualStart && (
<p className="mt-1 text-xs text-red-600">{errors.actualStart}</p>
)}
</div>
{/* Assignment */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<FaUser className="w-4 h-4 inline mr-2" />
Atanan Kişi
</label>
<select
value={formData.assignedTo}
onChange={(e) => setFormData({ ...formData, assignedTo: e.target.value })}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="">Kişi Seçin</option>
{mockEmployees.map((employee) => (
<option key={employee.id} value={employee.fullName}>
{employee.fullName} - {employee.jobPosition?.name}
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Bakım Ekibi</label>
<select
value={formData.maintenanceTeamId}
onChange={(e) =>
setFormData({
...formData,
maintenanceTeamId: e.target.value,
})
}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="">Ekip Seçin</option>
{mockMaintenanceTeams.map((team) => (
<option key={team.id} value={team.id}>
{team.name}
</option>
))}
</select>
</div>
</div>
{/* Notes */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Başlangıç Notları
</label>
<textarea
value={formData.notes}
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
rows={2}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="İş emirlerinin başlatılması ile ilgili notlar..."
/>
</div>
{/* Summary */}
<div className="bg-gray-50 rounded-lg p-3">
<h5 className="font-medium text-gray-900 mb-2">Özet</h5>
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<span className="text-gray-600">Toplam İş Emri:</span>
<span className="ml-2 font-medium">{workOrders.length}</span>
</div>
<div>
<span className="text-gray-600">Başlatılabilir:</span>
<span className="ml-2 font-medium text-green-600">
{
workOrders.filter(
(wo) =>
wo.status === WorkOrderStatusEnum.Created ||
wo.status === WorkOrderStatusEnum.Planned ||
wo.status === WorkOrderStatusEnum.Released,
).length
}
</span>
</div>
<div>
<span className="text-gray-600">Zaten Başlamış:</span>
<span className="ml-2 font-medium text-blue-600">
{inProgressWorkOrders.length}
</span>
</div>
<div>
<span className="text-gray-600">Başlangıç Tarihi:</span>
<span className="ml-2 font-medium">
{new Date(formData.actualStart).toLocaleDateString('tr-TR')}{' '}
{new Date(formData.actualStart).toLocaleTimeString('tr-TR', {
hour: '2-digit',
minute: '2-digit',
})}
</span>
</div>
</div>
</div>
{/* Form Actions */}
<div className="flex justify-end space-x-2 pt-4 border-t border-gray-200">
<button
type="button"
onClick={onClose}
className="px-4 py-1.5 text-sm text-gray-600 border border-gray-300 rounded-lg hover:bg-gray-50"
>
İptal
</button>
<button
type="submit"
className="px-4 py-1.5 text-sm bg-green-600 text-white rounded-lg hover:bg-green-700 flex items-center space-x-2"
>
<FaPlay className="w-4 h-4" />
<span>İş Emirlerini Başlat</span>
</button>
</div>
</form>
</div>
</div>
)
}
export default StartWorkOrderModal