248 lines
8.2 KiB
TypeScript
248 lines
8.2 KiB
TypeScript
|
|
import React, { useState } from "react";
|
|||
|
|
import { FaTimes, FaSave, FaPlus } from "react-icons/fa";
|
|||
|
|
import {
|
|||
|
|
PmWorkCenter,
|
|||
|
|
MaintenancePlanTypeEnum,
|
|||
|
|
FrequencyUnitEnum,
|
|||
|
|
PmMaintenancePlan,
|
|||
|
|
} from "../../../types/pm";
|
|||
|
|
import { PriorityEnum } from "../../../types/common";
|
|||
|
|
|
|||
|
|
interface MaintenancePlanModalProps {
|
|||
|
|
isOpen: boolean;
|
|||
|
|
onClose: () => void;
|
|||
|
|
onSave: (planData: PmMaintenancePlan[]) => void;
|
|||
|
|
selectedWorkCenters: PmWorkCenter[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
|||
|
|
isOpen,
|
|||
|
|
onClose,
|
|||
|
|
onSave,
|
|||
|
|
selectedWorkCenters,
|
|||
|
|
}) => {
|
|||
|
|
const [planData, setPlanData] = useState({
|
|||
|
|
planType: MaintenancePlanTypeEnum.Preventive,
|
|||
|
|
description: "",
|
|||
|
|
frequency: 1,
|
|||
|
|
frequencyUnit: FrequencyUnitEnum.Months,
|
|||
|
|
estimatedDuration: 60,
|
|||
|
|
priority: PriorityEnum.Normal,
|
|||
|
|
instructions: "",
|
|||
|
|
requiredSkills: [] as string[],
|
|||
|
|
nextDue: new Date(),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const [newSkill, setNewSkill] = useState("");
|
|||
|
|
|
|||
|
|
if (!isOpen) return null;
|
|||
|
|
|
|||
|
|
const handleInputChange = (
|
|||
|
|
e: React.ChangeEvent<
|
|||
|
|
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
|||
|
|
>
|
|||
|
|
) => {
|
|||
|
|
const { name, value, type } = e.target;
|
|||
|
|
setPlanData((prev) => ({
|
|||
|
|
...prev,
|
|||
|
|
[name]:
|
|||
|
|
type === "date"
|
|||
|
|
? new Date(value)
|
|||
|
|
: type === "number"
|
|||
|
|
? Number(value)
|
|||
|
|
: value,
|
|||
|
|
}));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const addSkill = () => {
|
|||
|
|
if (newSkill.trim() && !planData.requiredSkills.includes(newSkill.trim())) {
|
|||
|
|
setPlanData((prev) => ({
|
|||
|
|
...prev,
|
|||
|
|
requiredSkills: [...prev.requiredSkills, newSkill.trim()],
|
|||
|
|
}));
|
|||
|
|
setNewSkill("");
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const removeSkill = (skillToRemove: string) => {
|
|||
|
|
setPlanData((prev) => ({
|
|||
|
|
...prev,
|
|||
|
|
requiredSkills: prev.requiredSkills.filter(
|
|||
|
|
(skill) => skill !== skillToRemove
|
|||
|
|
),
|
|||
|
|
}));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleSave = () => {
|
|||
|
|
const plansToCreate = selectedWorkCenters.map((workCenter) => ({
|
|||
|
|
id: `MP${Date.now()}-${workCenter.id}`,
|
|||
|
|
planCode: `MP-${workCenter.code}-${Date.now()}`,
|
|||
|
|
workCenterId: workCenter.id,
|
|||
|
|
workCenter: workCenter,
|
|||
|
|
...planData,
|
|||
|
|
requiredMaterials: [],
|
|||
|
|
isActive: true,
|
|||
|
|
creationTime: new Date(),
|
|||
|
|
lastModificationTime: new Date(),
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
onSave(plansToCreate);
|
|||
|
|
onClose();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|||
|
|
<div className="bg-white rounded-lg w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto">
|
|||
|
|
{/* Header */}
|
|||
|
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
|||
|
|
<h2 className="text-lg font-semibold text-gray-900">
|
|||
|
|
Bakım Planı Oluştur ({selectedWorkCenters.length} iş merkezi)
|
|||
|
|
</h2>
|
|||
|
|
<button
|
|||
|
|
onClick={onClose}
|
|||
|
|
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
|
|||
|
|
>
|
|||
|
|
<FaTimes className="w-5 h-5 text-gray-500" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Content */}
|
|||
|
|
<div className="p-4 space-y-4">
|
|||
|
|
{/* Selected Work Center List */}
|
|||
|
|
<div>
|
|||
|
|
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
|||
|
|
Seçili İş Merkezleri
|
|||
|
|
</h3>
|
|||
|
|
<div className="max-h-28 overflow-y-auto bg-gray-50 rounded-lg p-2">
|
|||
|
|
{selectedWorkCenters.map((workCenter) => (
|
|||
|
|
<div
|
|||
|
|
key={workCenter.id}
|
|||
|
|
className="flex items-center justify-between py-0.5"
|
|||
|
|
>
|
|||
|
|
<span className="text-sm text-gray-700">
|
|||
|
|
{workCenter.code} - {workCenter.name}
|
|||
|
|
</span>
|
|||
|
|
<span className="text-xs text-gray-500">
|
|||
|
|
{workCenter.location}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
))}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Plan Details */}
|
|||
|
|
<div>
|
|||
|
|
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
|||
|
|
Plan Detayları
|
|||
|
|
</h3>
|
|||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Plan Tipi
|
|||
|
|
</label>
|
|||
|
|
<select
|
|||
|
|
name="planType"
|
|||
|
|
value={planData.planType}
|
|||
|
|
onChange={handleInputChange}
|
|||
|
|
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={MaintenancePlanTypeEnum.Preventive}>
|
|||
|
|
Önleyici Bakım
|
|||
|
|
</option>
|
|||
|
|
<option value={MaintenancePlanTypeEnum.Predictive}>
|
|||
|
|
Kestirimci Bakım
|
|||
|
|
</option>
|
|||
|
|
<option value={MaintenancePlanTypeEnum.Corrective}>
|
|||
|
|
Düzeltici Bakım
|
|||
|
|
</option>
|
|||
|
|
<option value={MaintenancePlanTypeEnum.Condition}>
|
|||
|
|
Durum Bazlı Bakım
|
|||
|
|
</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Öncelik
|
|||
|
|
</label>
|
|||
|
|
<select
|
|||
|
|
name="priority"
|
|||
|
|
value={planData.priority}
|
|||
|
|
onChange={handleInputChange}
|
|||
|
|
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={PriorityEnum.Low}>Düşük</option>
|
|||
|
|
<option value={PriorityEnum.Normal}>Normal</option>
|
|||
|
|
<option value={PriorityEnum.High}>Yüksek</option>
|
|||
|
|
<option value={PriorityEnum.Urgent}>Acil</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Required Skills */}
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Gerekli Yetenekler
|
|||
|
|
</label>
|
|||
|
|
<div className="flex space-x-2 mb-2">
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={newSkill}
|
|||
|
|
onChange={(e) => setNewSkill(e.target.value)}
|
|||
|
|
placeholder="Yetenek ekle..."
|
|||
|
|
className="flex-1 px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|||
|
|
onKeyPress={(e) => e.key === "Enter" && addSkill()}
|
|||
|
|
/>
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
onClick={addSkill}
|
|||
|
|
className="px-3 py-1.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
|
|||
|
|
>
|
|||
|
|
<FaPlus className="w-4 h-4" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div className="flex flex-wrap gap-2">
|
|||
|
|
{planData.requiredSkills.map((skill, index) => (
|
|||
|
|
<span
|
|||
|
|
key={index}
|
|||
|
|
className="inline-flex items-center px-2 py-1 bg-blue-100 text-blue-800 rounded-full text-xs"
|
|||
|
|
>
|
|||
|
|
{skill}
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
onClick={() => removeSkill(skill)}
|
|||
|
|
className="ml-2 text-blue-600 hover:text-blue-800"
|
|||
|
|
>
|
|||
|
|
×
|
|||
|
|
</button>
|
|||
|
|
</span>
|
|||
|
|
))}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Footer */}
|
|||
|
|
<div className="flex items-center justify-end space-x-3 p-4 border-t border-gray-200">
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
onClick={onClose}
|
|||
|
|
className="px-3 py-1.5 text-sm text-gray-600 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
|||
|
|
>
|
|||
|
|
İptal
|
|||
|
|
</button>
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
onClick={handleSave}
|
|||
|
|
className="flex items-center space-x-2 px-3 py-1.5 text-sm bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors"
|
|||
|
|
>
|
|||
|
|
<FaSave className="w-4 h-4" />
|
|||
|
|
<span>Bakım Planı Oluştur</span>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default MaintenancePlanModal;
|