336 lines
13 KiB
TypeScript
336 lines
13 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
||
import {
|
||
FaUsers,
|
||
FaAward,
|
||
FaSave,
|
||
FaTimesCircle,
|
||
FaCalendarAlt,
|
||
} from "react-icons/fa";
|
||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
||
import { mockBadges } from "../../../mocks/mockBadges";
|
||
|
||
export interface BadgeAssignmentFormData {
|
||
employeeId: string;
|
||
badgeId: string;
|
||
reason?: string;
|
||
earnedDate: string;
|
||
expiryDate?: string;
|
||
notes?: string;
|
||
}
|
||
|
||
interface BadgeAssignmentModalProps {
|
||
isOpen: boolean;
|
||
onClose: () => void;
|
||
onSubmit: (assignmentData: BadgeAssignmentFormData) => void;
|
||
preSelectedEmployeeId?: string;
|
||
preSelectedBadgeId?: string;
|
||
}
|
||
|
||
const BadgeAssignmentModal: React.FC<BadgeAssignmentModalProps> = ({
|
||
isOpen,
|
||
onClose,
|
||
onSubmit,
|
||
preSelectedEmployeeId,
|
||
preSelectedBadgeId,
|
||
}) => {
|
||
const [formData, setFormData] = useState<BadgeAssignmentFormData>({
|
||
employeeId: "",
|
||
badgeId: "",
|
||
reason: "",
|
||
earnedDate: new Date().toISOString().split("T")[0],
|
||
expiryDate: "",
|
||
notes: "",
|
||
});
|
||
|
||
const [searchEmployee, setSearchEmployee] = useState("");
|
||
const [searchBadge, setSearchBadge] = useState("");
|
||
|
||
useEffect(() => {
|
||
if (isOpen) {
|
||
setFormData({
|
||
employeeId: preSelectedEmployeeId || "",
|
||
badgeId: preSelectedBadgeId || "",
|
||
reason: "",
|
||
earnedDate: new Date().toISOString().split("T")[0],
|
||
expiryDate: "",
|
||
notes: "",
|
||
});
|
||
setSearchEmployee("");
|
||
setSearchBadge("");
|
||
}
|
||
}, [isOpen, preSelectedEmployeeId, preSelectedBadgeId]);
|
||
|
||
const handleInputChange = (
|
||
e: React.ChangeEvent<
|
||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
||
>
|
||
) => {
|
||
const { name, value } = e.target;
|
||
setFormData((prev) => ({
|
||
...prev,
|
||
[name]: value,
|
||
}));
|
||
};
|
||
|
||
const handleSubmit = (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
onSubmit(formData);
|
||
onClose();
|
||
};
|
||
|
||
const filteredEmployees = mockEmployees.filter(
|
||
(employee) =>
|
||
employee.fullName.toLowerCase().includes(searchEmployee.toLowerCase()) ||
|
||
employee.code.toLowerCase().includes(searchEmployee.toLowerCase())
|
||
);
|
||
|
||
const filteredBadges = mockBadges.filter(
|
||
(badge) =>
|
||
badge.isActive &&
|
||
(badge.name.toLowerCase().includes(searchBadge.toLowerCase()) ||
|
||
badge.description.toLowerCase().includes(searchBadge.toLowerCase()))
|
||
);
|
||
|
||
const selectedEmployee = mockEmployees.find(
|
||
(emp) => emp.id === formData.employeeId
|
||
);
|
||
const selectedBadge = mockBadges.find(
|
||
(badge) => badge.id === formData.badgeId
|
||
);
|
||
|
||
return (
|
||
isOpen && (
|
||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||
<div className="bg-white rounded-xl shadow-2xl 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 bg-gradient-to-r from-green-50 to-emerald-50 rounded-t-xl">
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-10 h-10 bg-green-600 rounded-lg flex items-center justify-center">
|
||
<FaAward className="w-5 h-5 text-white" />
|
||
</div>
|
||
<div>
|
||
<h3 className="text-xl font-bold text-gray-900">Rozet Ata</h3>
|
||
<p className="text-gray-600">
|
||
Personele başarı rozeti atayın
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={onClose}
|
||
className="text-gray-400 hover:text-gray-600 p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
||
>
|
||
<FaTimesCircle className="w-6 h-6" />
|
||
</button>
|
||
</div>
|
||
|
||
{/* Form */}
|
||
<form onSubmit={handleSubmit} className="p-3">
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||
{/* Sol Kolon - Personel Seçimi */}
|
||
<div className="space-y-4">
|
||
<div className="bg-gray-50 p-2 rounded-lg">
|
||
<h4 className="text-sm font-semibold text-gray-800 mb-2 flex items-center gap-2">
|
||
<FaUsers className="w-5 h-5 text-blue-600" />
|
||
Personel Seçimi
|
||
</h4>
|
||
|
||
<div className="space-y-4">
|
||
<div>
|
||
<select
|
||
name="employeeId"
|
||
value={formData.employeeId}
|
||
onChange={handleInputChange}
|
||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
|
||
required
|
||
>
|
||
<option value="">Personel seçin...</option>
|
||
{filteredEmployees.map((employee) => (
|
||
<option key={employee.id} value={employee.id}>
|
||
{employee.fullName} - {employee.code}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
{selectedEmployee && (
|
||
<div className="bg-blue-50 p-2 rounded-lg border border-blue-200 text-xs">
|
||
<div className="flex items-start justify-between">
|
||
<div className="flex-1">
|
||
<h4 className="font-medium text-blue-900 mb-1">
|
||
{selectedEmployee.fullName}
|
||
</h4>
|
||
<p className="text-xs text-blue-700 mb-1">
|
||
{selectedEmployee.code} •{" "}
|
||
{selectedEmployee.department?.name}
|
||
</p>
|
||
<div className="text-xs text-blue-600">
|
||
<span className="font-medium">Email:</span>{" "}
|
||
{selectedEmployee.email}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Sağ Kolon - Rozet Seçimi */}
|
||
<div className="space-y-4">
|
||
<div className="bg-gray-50 p-2 rounded-lg">
|
||
<h4 className="text-sm font-semibold text-gray-800 mb-2 flex items-center gap-2">
|
||
<FaAward className="w-5 h-5 text-purple-600" />
|
||
Rozet Seçimi
|
||
</h4>
|
||
|
||
<div className="space-y-4">
|
||
<div>
|
||
<select
|
||
name="badgeId"
|
||
value={formData.badgeId}
|
||
onChange={handleInputChange}
|
||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
|
||
required
|
||
>
|
||
<option value="">Rozet seçin...</option>
|
||
{filteredBadges.map((badge) => (
|
||
<option key={badge.id} value={badge.id}>
|
||
{badge.icon} {badge.name} ({badge.points} puan)
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
{selectedBadge && (
|
||
<div className="bg-purple-50 p-2 rounded-lg border border-purple-200 text-xs">
|
||
<div className="flex items-start gap-3">
|
||
<div
|
||
className="w-10 h-10 rounded-full flex items-center justify-center text-lg flex-shrink-0"
|
||
style={{
|
||
backgroundColor: selectedBadge.backgroundColor,
|
||
border: `2px solid ${selectedBadge.color}`,
|
||
}}
|
||
>
|
||
{selectedBadge.icon}
|
||
</div>
|
||
<div className="flex-1">
|
||
<h4 className="font-medium text-purple-900 mb-1">
|
||
{selectedBadge.name}
|
||
</h4>
|
||
<p className="text-xs text-purple-700 mb-1">
|
||
{selectedBadge.description}
|
||
</p>
|
||
<div className="grid grid-cols-2 gap-2 text-xs text-purple-600">
|
||
<div>
|
||
<span className="font-medium">Puan:</span>{" "}
|
||
{selectedBadge.points}
|
||
</div>
|
||
<div>
|
||
<span className="font-medium">Kategori:</span>{" "}
|
||
{selectedBadge.category}
|
||
</div>
|
||
</div>
|
||
<div className="mt-2 text-xs text-purple-600">
|
||
<span className="font-medium">Kriter:</span>{" "}
|
||
{selectedBadge.criteria}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Atama Detayları */}
|
||
<div className="mt-3 bg-gray-50 p-2 rounded-lg">
|
||
<h4 className="text-sm font-semibold text-gray-800 mb-2 flex items-center gap-2">
|
||
<FaCalendarAlt className="w-5 h-5 text-green-600" />
|
||
Atama Detayları
|
||
</h4>
|
||
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
|
||
<div>
|
||
<label className="block text-xs font-medium text-gray-700 mb-1">
|
||
Kazanılma Tarihi <span className="text-red-500">*</span>
|
||
</label>
|
||
<input
|
||
type="date"
|
||
name="earnedDate"
|
||
value={formData.earnedDate}
|
||
onChange={handleInputChange}
|
||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-xs font-medium text-gray-700 mb-1">
|
||
Geçerlilik Süresi (Opsiyonel)
|
||
</label>
|
||
<input
|
||
type="date"
|
||
name="expiryDate"
|
||
value={formData.expiryDate}
|
||
onChange={handleInputChange}
|
||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-xs font-medium text-gray-700 mb-1">
|
||
Kazanma Nedeni
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="reason"
|
||
value={formData.reason}
|
||
onChange={handleInputChange}
|
||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
|
||
placeholder="Örn: Q3 hedeflerini %120 başarıyla tamamladı"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-xs font-medium text-gray-700 mb-1">
|
||
Notlar
|
||
</label>
|
||
<textarea
|
||
name="notes"
|
||
value={formData.notes}
|
||
onChange={handleInputChange}
|
||
rows={2}
|
||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
|
||
placeholder="Ek notlar..."
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Footer */}
|
||
<div className="flex items-center justify-end gap-2 mt-3 pt-3 border-t border-gray-200">
|
||
<button
|
||
type="button"
|
||
onClick={onClose}
|
||
className="px-3 py-1 text-sm text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors flex items-center gap-2"
|
||
>
|
||
<FaTimesCircle className="w-4 h-4" />
|
||
İptal
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="px-3 py-1 text-sm bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors flex items-center gap-2 shadow-md hover:shadow-lg"
|
||
>
|
||
<FaSave className="w-4 h-4" />
|
||
Rozet Ata
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)
|
||
);
|
||
};
|
||
|
||
export default BadgeAssignmentModal;
|