erp-platform/ui/src/views/maintenance/components/StartWorkOrderModal.tsx

308 lines
12 KiB
TypeScript
Raw Normal View History

2025-09-15 21:29:07 +00:00
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'
2025-09-15 09:31:47 +00:00
interface StartWorkOrderModalProps {
2025-09-15 21:29:07 +00:00
isOpen: boolean
onClose: () => void
onStart: (workOrders: PmMaintenanceWorkOrder[], startData: StartWorkOrderData) => void
workOrders: PmMaintenanceWorkOrder[]
2025-09-15 09:31:47 +00:00
}
interface StartWorkOrderData {
2025-09-15 21:29:07 +00:00
actualStart: Date
assignedTo?: string
maintenanceTeamId?: string
notes?: string
2025-09-15 09:31:47 +00:00
}
const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
isOpen,
onClose,
onStart,
workOrders,
}) => {
const [formData, setFormData] = useState({
actualStart: new Date().toISOString().slice(0, 16),
2025-09-15 21:29:07 +00:00
assignedTo: '',
maintenanceTeamId: '',
notes: '',
})
2025-09-15 09:31:47 +00:00
2025-09-15 21:29:07 +00:00
const [errors, setErrors] = useState<Record<string, string>>({})
2025-09-15 09:31:47 +00:00
const validateForm = () => {
2025-09-15 21:29:07 +00:00
const newErrors: Record<string, string> = {}
2025-09-15 09:31:47 +00:00
if (!formData.actualStart) {
2025-09-15 21:29:07 +00:00
newErrors.actualStart = 'Başlangıç tarihi zorunludur'
2025-09-15 09:31:47 +00:00
}
2025-09-15 21:29:07 +00:00
const startDate = new Date(formData.actualStart)
const now = new Date()
2025-09-15 09:31:47 +00:00
if (startDate > now) {
2025-09-15 21:29:07 +00:00
newErrors.actualStart = 'Başlangıç tarihi gelecekte olamaz'
2025-09-15 09:31:47 +00:00
}
2025-09-15 21:29:07 +00:00
setErrors(newErrors)
return Object.keys(newErrors).length === 0
}
2025-09-15 09:31:47 +00:00
const handleSubmit = (e: React.FormEvent) => {
2025-09-15 21:29:07 +00:00
e.preventDefault()
if (!validateForm()) return
2025-09-15 09:31:47 +00:00
const startData: StartWorkOrderData = {
actualStart: new Date(formData.actualStart),
assignedTo: formData.assignedTo || undefined,
maintenanceTeamId: formData.maintenanceTeamId || undefined,
notes: formData.notes || undefined,
2025-09-15 21:29:07 +00:00
}
2025-09-15 09:31:47 +00:00
2025-09-15 21:29:07 +00:00
onStart(workOrders, startData)
onClose()
}
2025-09-15 09:31:47 +00:00
const canStartWorkOrders = workOrders.every(
(wo) =>
wo.status === WorkOrderStatusEnum.Created ||
wo.status === WorkOrderStatusEnum.Planned ||
2025-09-15 21:29:07 +00:00
wo.status === WorkOrderStatusEnum.Released,
)
2025-09-15 09:31:47 +00:00
const inProgressWorkOrders = workOrders.filter(
2025-09-15 21:29:07 +00:00
(wo) => wo.status === WorkOrderStatusEnum.InProgress,
)
2025-09-15 09:31:47 +00:00
2025-09-15 21:29:07 +00:00
if (!isOpen) return null
2025-09-15 09:31:47 +00:00
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>
2025-09-15 21:29:07 +00:00
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
2025-09-15 09:31:47 +00:00
<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) => (
2025-09-15 21:29:07 +00:00
<div key={workOrder.id} className="p-3 border-b border-gray-100 last:border-b-0">
2025-09-15 09:31:47 +00:00
<div className="flex items-center justify-between">
<div>
2025-09-15 21:29:07 +00:00
<p className="font-medium text-sm text-gray-900">{workOrder.workOrderNumber}</p>
<p className="text-xs text-gray-600 truncate">{workOrder.description}</p>
2025-09-15 09:31:47 +00:00
</div>
<div className="text-right">
<span
className={`px-2 py-1 rounded-full text-xs font-medium ${
workOrder.status === WorkOrderStatusEnum.Created
2025-09-15 21:29:07 +00:00
? 'bg-gray-100 text-gray-800'
2025-09-15 09:31:47 +00:00
: workOrder.status === WorkOrderStatusEnum.Planned
2025-09-15 21:29:07 +00:00
? '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'
2025-09-15 09:31:47 +00:00
}`}
>
2025-09-15 21:29:07 +00:00
{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'}
2025-09-15 09:31:47 +00:00
</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">
2025-09-15 21:29:07 +00:00
Bazı emirleri başlatılamaz durumda. Sadece "Oluşturuldu", "Planlandı" veya
"Serbest Bırakıldı" durumundaki emirleri başlatılabilir.
2025-09-15 09:31:47 +00:00
</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">
2025-09-15 21:29:07 +00:00
{inProgressWorkOrders.length} emri zaten devam ediyor durumunda. Bu emirleri
için sadece atama ve not güncellemesi yapılacak.
2025-09-15 09:31:47 +00:00
</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}
2025-09-15 21:29:07 +00:00
onChange={(e) => setFormData({ ...formData, actualStart: e.target.value })}
2025-09-15 09:31:47 +00:00
className={`w-full px-3 py-1.5 text-sm border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${
2025-09-15 21:29:07 +00:00
errors.actualStart ? 'border-red-500' : 'border-gray-300'
2025-09-15 09:31:47 +00:00
}`}
/>
{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}
2025-09-15 21:29:07 +00:00
onChange={(e) => setFormData({ ...formData, assignedTo: e.target.value })}
2025-09-15 09:31:47 +00:00
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>
2025-09-15 21:29:07 +00:00
<label className="block text-sm font-medium text-gray-700 mb-2">Bakım Ekibi</label>
2025-09-15 09:31:47 +00:00
<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}
2025-09-15 21:29:07 +00:00
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
2025-09-15 09:31:47 +00:00
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 ||
2025-09-15 21:29:07 +00:00
wo.status === WorkOrderStatusEnum.Released,
2025-09-15 09:31:47 +00:00
).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">
2025-09-15 21:29:07 +00:00
{new Date(formData.actualStart).toLocaleDateString('tr-TR')}{' '}
{new Date(formData.actualStart).toLocaleTimeString('tr-TR', {
hour: '2-digit',
minute: '2-digit',
2025-09-15 09:31:47 +00:00
})}
</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>
2025-09-15 21:29:07 +00:00
)
}
2025-09-15 09:31:47 +00:00
2025-09-15 21:29:07 +00:00
export default StartWorkOrderModal