251 lines
8.6 KiB
TypeScript
251 lines
8.6 KiB
TypeScript
|
|
import React, { useState } from "react";
|
|||
|
|
import { FaTimes, FaSave, FaUser, FaUsers } from "react-icons/fa";
|
|||
|
|
import { PmFaultNotification } from "../../../types/pm";
|
|||
|
|
import { mockEmployees } from "../../../mocks/mockEmployees";
|
|||
|
|
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
|||
|
|
|
|||
|
|
interface AssignNotificationModalProps {
|
|||
|
|
isOpen: boolean;
|
|||
|
|
onClose: () => void;
|
|||
|
|
onSave: (assignments: {
|
|||
|
|
notificationIds: string[];
|
|||
|
|
assignedTo?: string;
|
|||
|
|
teamId?: string;
|
|||
|
|
}) => void;
|
|||
|
|
notifications: PmFaultNotification[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
|
|
isOpen,
|
|||
|
|
onClose,
|
|||
|
|
onSave,
|
|||
|
|
notifications,
|
|||
|
|
}) => {
|
|||
|
|
const [assignmentType, setAssignmentType] = useState<"person" | "team">(
|
|||
|
|
"person"
|
|||
|
|
);
|
|||
|
|
const [assignedTo, setAssignedTo] = useState("");
|
|||
|
|
const [teamId, setTeamId] = useState("");
|
|||
|
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
|||
|
|
|
|||
|
|
if (!isOpen || notifications.length === 0) return null;
|
|||
|
|
|
|||
|
|
const validateForm = () => {
|
|||
|
|
const newErrors: Record<string, string> = {};
|
|||
|
|
|
|||
|
|
if (assignmentType === "person" && !assignedTo) {
|
|||
|
|
newErrors.assignedTo = "Kişi seçimi gerekli";
|
|||
|
|
}
|
|||
|
|
if (assignmentType === "team" && !teamId) {
|
|||
|
|
newErrors.teamId = "Ekip seçimi gerekli";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
setErrors(newErrors);
|
|||
|
|
return Object.keys(newErrors).length === 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleSave = () => {
|
|||
|
|
if (validateForm()) {
|
|||
|
|
const assignmentData = {
|
|||
|
|
notificationIds: notifications.map((n) => n.id),
|
|||
|
|
...(assignmentType === "person" ? { assignedTo } : { teamId }),
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
onSave(assignmentData);
|
|||
|
|
onClose();
|
|||
|
|
|
|||
|
|
// Reset form
|
|||
|
|
setAssignmentType("person");
|
|||
|
|
setAssignedTo("");
|
|||
|
|
setTeamId("");
|
|||
|
|
setErrors({});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
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-3 border-b border-gray-200">
|
|||
|
|
<h2 className="text-xl font-bold text-gray-900">Atama Yap</h2>
|
|||
|
|
<button
|
|||
|
|
onClick={onClose}
|
|||
|
|
className="text-gray-400 hover:text-gray-600 transition-colors"
|
|||
|
|
>
|
|||
|
|
<FaTimes className="w-5 h-5" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Content */}
|
|||
|
|
<div className="p-3 space-y-3">
|
|||
|
|
{/* Notification Summary */}
|
|||
|
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-2">
|
|||
|
|
<h4 className="text-xs font-medium text-blue-800 mb-1.5">
|
|||
|
|
Seçili Arıza Bildirimleri ({notifications.length})
|
|||
|
|
</h4>
|
|||
|
|
<div className="space-y-1">
|
|||
|
|
{notifications.map((notification) => (
|
|||
|
|
<div key={notification.id} className="text-sm text-blue-700">
|
|||
|
|
<span className="font-medium">
|
|||
|
|
{notification.notificationCode}
|
|||
|
|
</span>
|
|||
|
|
{" - "}
|
|||
|
|
<span>{notification.title}</span>
|
|||
|
|
{" ("}
|
|||
|
|
<span>{notification.workCenter.code}</span>
|
|||
|
|
{")"}
|
|||
|
|
</div>
|
|||
|
|
))}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Assignment Type */}
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|||
|
|
Atama Türü
|
|||
|
|
</label>
|
|||
|
|
<div className="flex space-x-3">
|
|||
|
|
<label className="flex items-center">
|
|||
|
|
<input
|
|||
|
|
type="radio"
|
|||
|
|
value="person"
|
|||
|
|
checked={assignmentType === "person"}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
setAssignmentType(e.target.value as "person" | "team")
|
|||
|
|
}
|
|||
|
|
className="mr-2"
|
|||
|
|
/>
|
|||
|
|
<FaUser className="w-4 h-4 mr-2 text-gray-500" />
|
|||
|
|
<span>Kişiye Ata</span>
|
|||
|
|
</label>
|
|||
|
|
<label className="flex items-center">
|
|||
|
|
<input
|
|||
|
|
type="radio"
|
|||
|
|
value="team"
|
|||
|
|
checked={assignmentType === "team"}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
setAssignmentType(e.target.value as "person" | "team")
|
|||
|
|
}
|
|||
|
|
className="mr-2"
|
|||
|
|
/>
|
|||
|
|
<FaUsers className="w-4 h-4 mr-2 text-gray-500" />
|
|||
|
|
<span>Ekibe Ata</span>
|
|||
|
|
</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Person Assignment */}
|
|||
|
|
{assignmentType === "person" && (
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|||
|
|
Atanacak Kişi *
|
|||
|
|
</label>
|
|||
|
|
<select
|
|||
|
|
value={assignedTo}
|
|||
|
|
onChange={(e) => setAssignedTo(e.target.value)}
|
|||
|
|
className={`w-full px-2.5 py-1.5 text-sm border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${
|
|||
|
|
errors.assignedTo ? "border-red-500" : "border-gray-300"
|
|||
|
|
}`}
|
|||
|
|
>
|
|||
|
|
<option value="">Kişi seçin</option>
|
|||
|
|
{mockEmployees.map((employee) => (
|
|||
|
|
<option key={employee.id} value={employee.fullName}>
|
|||
|
|
{employee.fullName} - {employee.jobPosition?.name}
|
|||
|
|
</option>
|
|||
|
|
))}
|
|||
|
|
</select>
|
|||
|
|
{errors.assignedTo && (
|
|||
|
|
<p className="text-red-500 text-xs mt-1">{errors.assignedTo}</p>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
{/* Team Assignment */}
|
|||
|
|
{assignmentType === "team" && (
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|||
|
|
Atanacak Ekip *
|
|||
|
|
</label>
|
|||
|
|
<select
|
|||
|
|
value={teamId}
|
|||
|
|
onChange={(e) => setTeamId(e.target.value)}
|
|||
|
|
className={`w-full px-2.5 py-1.5 text-sm border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${
|
|||
|
|
errors.teamId ? "border-red-500" : "border-gray-300"
|
|||
|
|
}`}
|
|||
|
|
>
|
|||
|
|
<option value="">Ekip seçin</option>
|
|||
|
|
{mockMaintenanceTeams
|
|||
|
|
.filter((team) => team.isActive)
|
|||
|
|
.map((team) => (
|
|||
|
|
<option key={team.id} value={team.id}>
|
|||
|
|
{team.name} ({team.code})
|
|||
|
|
</option>
|
|||
|
|
))}
|
|||
|
|
</select>
|
|||
|
|
{errors.teamId && (
|
|||
|
|
<p className="text-red-500 text-xs mt-1">{errors.teamId}</p>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
{/* Assignment Preview */}
|
|||
|
|
{(assignedTo || teamId) && (
|
|||
|
|
<div className="bg-green-50 border border-green-200 rounded-lg p-2">
|
|||
|
|
<h4 className="text-sm font-medium text-green-800 mb-1">
|
|||
|
|
Atama Özeti
|
|||
|
|
</h4>
|
|||
|
|
<div className="text-sm text-green-700">
|
|||
|
|
<p>
|
|||
|
|
<strong>{notifications.length}</strong> arıza bildirimi{" "}
|
|||
|
|
{assignmentType === "person" ? (
|
|||
|
|
<>
|
|||
|
|
<strong>
|
|||
|
|
{
|
|||
|
|
mockEmployees.find(
|
|||
|
|
(emp) => emp.fullName === assignedTo
|
|||
|
|
)?.fullName
|
|||
|
|
}
|
|||
|
|
</strong>
|
|||
|
|
kişisine atanacak
|
|||
|
|
</>
|
|||
|
|
) : (
|
|||
|
|
<>
|
|||
|
|
<strong>
|
|||
|
|
{
|
|||
|
|
mockMaintenanceTeams.find(
|
|||
|
|
(team) => team.id === teamId
|
|||
|
|
)?.name
|
|||
|
|
}
|
|||
|
|
</strong>
|
|||
|
|
ekibine atanacak
|
|||
|
|
</>
|
|||
|
|
)}
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Footer */}
|
|||
|
|
<div className="flex items-center justify-end space-x-2 p-3 border-t border-gray-200">
|
|||
|
|
<button
|
|||
|
|
onClick={onClose}
|
|||
|
|
className="px-3 py-1 text-sm text-gray-600 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
|||
|
|
>
|
|||
|
|
İptal
|
|||
|
|
</button>
|
|||
|
|
<button
|
|||
|
|
onClick={handleSave}
|
|||
|
|
className="px-3 py-1 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center space-x-2"
|
|||
|
|
>
|
|||
|
|
<FaSave className="w-4 h-4" />
|
|||
|
|
<span>Ata</span>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default AssignNotificationModal;
|