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

338 lines
13 KiB
TypeScript
Raw Normal View History

2025-09-15 09:31:47 +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";
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;