Container Maintenance Management
This commit is contained in:
parent
bb2e39c2c3
commit
4b7fd59e61
35 changed files with 4620 additions and 6560 deletions
|
|
@ -3330,16 +3330,6 @@
|
|||
"RequiredPermissionName": null,
|
||||
"IsDisabled": false
|
||||
},
|
||||
{
|
||||
"ParentCode": "App.Maintenance",
|
||||
"Code": "App.Maintenance.Equipment",
|
||||
"DisplayName": "Ekipmanlar",
|
||||
"Order": 1,
|
||||
"Url": "/admin/maintenance/equipment",
|
||||
"Icon": "FcEngineering",
|
||||
"RequiredPermissionName": null,
|
||||
"IsDisabled": false
|
||||
},
|
||||
{
|
||||
"ParentCode": "App.Maintenance",
|
||||
"Code": "App.Maintenance.Workcenters",
|
||||
|
|
@ -3373,7 +3363,7 @@
|
|||
{
|
||||
"ParentCode": "App.Maintenance",
|
||||
"Code": "App.Maintenance.Teams",
|
||||
"DisplayName": "Bakım Takımları",
|
||||
"DisplayName": "Bakım Ekipleri",
|
||||
"Order": 5,
|
||||
"Url": "/admin/maintenance/teams",
|
||||
"Icon": "FcConferenceCall",
|
||||
|
|
|
|||
|
|
@ -1,18 +1,14 @@
|
|||
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";
|
||||
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[];
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (assignments: { notificationIds: string[]; assignedTo?: string; teamId?: string }) => void
|
||||
notifications: PmFaultNotification[]
|
||||
}
|
||||
|
||||
const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||
|
|
@ -21,46 +17,44 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
onSave,
|
||||
notifications,
|
||||
}) => {
|
||||
const [assignmentType, setAssignmentType] = useState<"person" | "team">(
|
||||
"person"
|
||||
);
|
||||
const [assignedTo, setAssignedTo] = useState("");
|
||||
const [teamId, setTeamId] = useState("");
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
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;
|
||||
if (!isOpen || notifications.length === 0) return null
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (assignmentType === "person" && !assignedTo) {
|
||||
newErrors.assignedTo = "Kişi seçimi gerekli";
|
||||
if (assignmentType === 'person' && !assignedTo) {
|
||||
newErrors.assignedTo = 'Kişi seçimi gerekli'
|
||||
}
|
||||
if (assignmentType === "team" && !teamId) {
|
||||
newErrors.teamId = "Ekip seçimi gerekli";
|
||||
if (assignmentType === 'team' && !teamId) {
|
||||
newErrors.teamId = 'Ekip seçimi gerekli'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
if (validateForm()) {
|
||||
const assignmentData = {
|
||||
notificationIds: notifications.map((n) => n.id),
|
||||
...(assignmentType === "person" ? { assignedTo } : { teamId }),
|
||||
};
|
||||
...(assignmentType === 'person' ? { assignedTo } : { teamId }),
|
||||
}
|
||||
|
||||
onSave(assignmentData);
|
||||
onClose();
|
||||
onSave(assignmentData)
|
||||
onClose()
|
||||
|
||||
// Reset form
|
||||
setAssignmentType("person");
|
||||
setAssignedTo("");
|
||||
setTeamId("");
|
||||
setErrors({});
|
||||
setAssignmentType('person')
|
||||
setAssignedTo('')
|
||||
setTeamId('')
|
||||
setErrors({})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -68,10 +62,7 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
{/* 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"
|
||||
>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -86,14 +77,12 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
<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 className="font-medium">{notification.notificationCode}</span>
|
||||
{' - '}
|
||||
<span>{notification.title}</span>
|
||||
{" ("}
|
||||
{' ('}
|
||||
<span>{notification.workCenter.code}</span>
|
||||
{")"}
|
||||
{')'}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -101,18 +90,14 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
|
||||
{/* Assignment Type */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Atama Türü
|
||||
</label>
|
||||
<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")
|
||||
}
|
||||
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" />
|
||||
|
|
@ -122,10 +107,8 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
<input
|
||||
type="radio"
|
||||
value="team"
|
||||
checked={assignmentType === "team"}
|
||||
onChange={(e) =>
|
||||
setAssignmentType(e.target.value as "person" | "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" />
|
||||
|
|
@ -135,7 +118,7 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
</div>
|
||||
|
||||
{/* Person Assignment */}
|
||||
{assignmentType === "person" && (
|
||||
{assignmentType === 'person' && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Atanacak Kişi *
|
||||
|
|
@ -144,7 +127,7 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
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"
|
||||
errors.assignedTo ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">Kişi seçin</option>
|
||||
|
|
@ -161,7 +144,7 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
)}
|
||||
|
||||
{/* Team Assignment */}
|
||||
{assignmentType === "team" && (
|
||||
{assignmentType === 'team' && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Atanacak Ekip *
|
||||
|
|
@ -170,7 +153,7 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
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"
|
||||
errors.teamId ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">Ekip seçin</option>
|
||||
|
|
@ -182,40 +165,28 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.teamId && (
|
||||
<p className="text-red-500 text-xs mt-1">{errors.teamId}</p>
|
||||
)}
|
||||
{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>
|
||||
<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>{notifications.length}</strong> arıza bildirimi{' '}
|
||||
{assignmentType === 'person' ? (
|
||||
<>
|
||||
<strong>
|
||||
{
|
||||
mockEmployees.find(
|
||||
(emp) => emp.fullName === assignedTo
|
||||
)?.fullName
|
||||
}
|
||||
{mockEmployees.find((emp) => emp.fullName === assignedTo)?.fullName}
|
||||
</strong>
|
||||
kişisine atanacak
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<strong>
|
||||
{
|
||||
mockMaintenanceTeams.find(
|
||||
(team) => team.id === teamId
|
||||
)?.name
|
||||
}
|
||||
{mockMaintenanceTeams.find((team) => team.id === teamId)?.name}
|
||||
</strong>
|
||||
ekibine atanacak
|
||||
</>
|
||||
|
|
@ -244,7 +215,7 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default AssignNotificationModal;
|
||||
export default AssignNotificationModal
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaSave, FaWrench } from "react-icons/fa";
|
||||
import { mockMaintenancePlans } from "../../../mocks/mockMaintenancePlans";
|
||||
import { Team } from "../../../types/common";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaSave, FaWrench } from 'react-icons/fa'
|
||||
import { mockMaintenancePlans } from '../../../mocks/mockMaintenancePlans'
|
||||
import { Team } from '../../../types/common'
|
||||
|
||||
interface AssignWorkOrderModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (assignments: { teamId: string; planIds: string[] }[]) => void;
|
||||
selectedTeams: Team[];
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (assignments: { teamId: string; planIds: string[] }[]) => void
|
||||
selectedTeams: Team[]
|
||||
}
|
||||
|
||||
const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||
|
|
@ -16,22 +16,20 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
onSave,
|
||||
selectedTeams,
|
||||
}) => {
|
||||
const [assignments, setAssignments] = useState<Record<string, string[]>>({});
|
||||
const [bulkPlanIds, setBulkPlanIds] = useState<string[]>([]);
|
||||
const [useBulkAssignment, setUseBulkAssignment] = useState(true);
|
||||
const [assignments, setAssignments] = useState<Record<string, string[]>>({})
|
||||
const [bulkPlanIds, setBulkPlanIds] = useState<string[]>([])
|
||||
const [useBulkAssignment, setUseBulkAssignment] = useState(true)
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
// Get available maintenance plans
|
||||
const availablePlans = mockMaintenancePlans.filter((plan) => plan.isActive);
|
||||
const availablePlans = mockMaintenancePlans.filter((plan) => plan.isActive)
|
||||
|
||||
const handleBulkPlanChange = (planId: string) => {
|
||||
setBulkPlanIds((prev) =>
|
||||
prev.includes(planId)
|
||||
? prev.filter((id) => id !== planId)
|
||||
: [...prev, planId]
|
||||
);
|
||||
};
|
||||
prev.includes(planId) ? prev.filter((id) => id !== planId) : [...prev, planId],
|
||||
)
|
||||
}
|
||||
|
||||
const handleIndividualPlanChange = (teamId: string, planId: string) => {
|
||||
setAssignments((prev) => ({
|
||||
|
|
@ -39,40 +37,40 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
[teamId]: prev[teamId]?.includes(planId)
|
||||
? prev[teamId].filter((id) => id !== planId)
|
||||
: [...(prev[teamId] || []), planId],
|
||||
}));
|
||||
};
|
||||
}))
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
if (useBulkAssignment) {
|
||||
if (bulkPlanIds.length === 0) {
|
||||
alert("Lütfen en az bir bakım planı seçin");
|
||||
return;
|
||||
alert('Lütfen en az bir bakım planı seçin')
|
||||
return
|
||||
}
|
||||
const bulkAssignments = selectedTeams.map((team) => ({
|
||||
teamId: team.id,
|
||||
planIds: bulkPlanIds,
|
||||
}));
|
||||
onSave(bulkAssignments);
|
||||
}))
|
||||
onSave(bulkAssignments)
|
||||
} else {
|
||||
const individualAssignments = selectedTeams
|
||||
.map((team) => ({
|
||||
teamId: team.id,
|
||||
planIds: assignments[team.id] || [],
|
||||
}))
|
||||
.filter((assignment) => assignment.planIds.length > 0);
|
||||
.filter((assignment) => assignment.planIds.length > 0)
|
||||
|
||||
if (individualAssignments.length === 0) {
|
||||
alert("Lütfen en az bir ekip için bakım planı seçin");
|
||||
return;
|
||||
alert('Lütfen en az bir ekip için bakım planı seçin')
|
||||
return
|
||||
}
|
||||
onSave(individualAssignments);
|
||||
onSave(individualAssignments)
|
||||
}
|
||||
|
||||
onClose();
|
||||
onClose()
|
||||
// Reset state
|
||||
setAssignments({});
|
||||
setBulkPlanIds([]);
|
||||
};
|
||||
setAssignments({})
|
||||
setBulkPlanIds([])
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -85,10 +83,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
Seçili ekiplere ({selectedTeams.length}) bakım planları atayın
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
||||
>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -97,9 +92,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
<div className="p-3 space-y-3">
|
||||
{/* Assignment Mode Selection */}
|
||||
<div className="border border-gray-200 rounded-lg p-3">
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Atama Türü
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Atama Türü</h3>
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center">
|
||||
<input
|
||||
|
|
@ -128,9 +121,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
|
||||
{/* Selected Teams */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Seçili Ekipler
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Seçili Ekipler</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||
{selectedTeams.map((team) => (
|
||||
<div
|
||||
|
|
@ -139,9 +130,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
>
|
||||
<FaWrench className="w-5 h-5 text-blue-600" />
|
||||
<div>
|
||||
<h4 className="font-medium text-sm text-gray-900">
|
||||
{team.name}
|
||||
</h4>
|
||||
<h4 className="font-medium text-sm text-gray-900">{team.name}</h4>
|
||||
<p className="text-gray-600">{team.code}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -169,12 +158,8 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
className="mt-1 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<h4 className="font-medium text-gray-900">
|
||||
{plan.planCode}
|
||||
</h4>
|
||||
<p className="text-gray-600">
|
||||
{plan.description}
|
||||
</p>
|
||||
<h4 className="font-medium text-gray-900">{plan.planCode}</h4>
|
||||
<p className="text-gray-600">{plan.description}</p>
|
||||
<div className="flex items-center space-x-4 mt-1 text-xs text-gray-500">
|
||||
<span>
|
||||
Sıklık: {plan.frequency} {plan.frequencyUnit}
|
||||
|
|
@ -192,15 +177,10 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
{/* Individual Assignment */}
|
||||
{!useBulkAssignment && (
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Bireysel Plan Atamaları
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Bireysel Plan Atamaları</h3>
|
||||
<div className="space-y-3">
|
||||
{selectedTeams.map((team) => (
|
||||
<div
|
||||
key={team.id}
|
||||
className="border border-gray-200 rounded-lg p-2"
|
||||
>
|
||||
<div key={team.id} className="border border-gray-200 rounded-lg p-2">
|
||||
<h4 className="font-medium text-sm text-gray-900 mb-2">
|
||||
{team.name} ({team.code})
|
||||
</h4>
|
||||
|
|
@ -212,21 +192,13 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={
|
||||
assignments[team.id]?.includes(plan.id) || false
|
||||
}
|
||||
onChange={() =>
|
||||
handleIndividualPlanChange(team.id, plan.id)
|
||||
}
|
||||
checked={assignments[team.id]?.includes(plan.id) || false}
|
||||
onChange={() => handleIndividualPlanChange(team.id, plan.id)}
|
||||
className="mt-1 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<h5 className="text-sm font-medium text-gray-900">
|
||||
{plan.planCode}
|
||||
</h5>
|
||||
<p className="text-xs text-gray-600">
|
||||
{plan.description}
|
||||
</p>
|
||||
<h5 className="text-sm font-medium text-gray-900">{plan.planCode}</h5>
|
||||
<p className="text-xs text-gray-600">{plan.description}</p>
|
||||
<div className="flex items-center space-x-3 mt-1 text-xs text-gray-500">
|
||||
<span>
|
||||
Sıklık: {plan.frequency} {plan.frequencyUnit}
|
||||
|
|
@ -262,7 +234,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default AssignWorkOrderModal;
|
||||
export default AssignWorkOrderModal
|
||||
|
|
|
|||
|
|
@ -1,24 +1,21 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaUserCog, FaUser, FaUsers } from "react-icons/fa";
|
||||
import { PmMaintenanceWorkOrder } from "../../../types/pm";
|
||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaUserCog, FaUser, FaUsers } from 'react-icons/fa'
|
||||
import { PmMaintenanceWorkOrder } from '../../../types/pm'
|
||||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||
|
||||
interface AssignWorkOrderModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onAssign: (
|
||||
workOrders: PmMaintenanceWorkOrder[],
|
||||
assignmentData: AssignmentData
|
||||
) => void;
|
||||
workOrders: PmMaintenanceWorkOrder[];
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onAssign: (workOrders: PmMaintenanceWorkOrder[], assignmentData: AssignmentData) => void
|
||||
workOrders: PmMaintenanceWorkOrder[]
|
||||
}
|
||||
|
||||
interface AssignmentData {
|
||||
assignmentType: "person" | "team";
|
||||
assignedTo?: string;
|
||||
maintenanceTeamId?: string;
|
||||
notes?: string;
|
||||
assignmentType: 'person' | 'team'
|
||||
assignedTo?: string
|
||||
maintenanceTeamId?: string
|
||||
notes?: string
|
||||
}
|
||||
|
||||
const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||
|
|
@ -27,65 +24,60 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
onAssign,
|
||||
workOrders,
|
||||
}) => {
|
||||
const [assignmentType, setAssignmentType] = useState<"person" | "team">(
|
||||
"person"
|
||||
);
|
||||
const [assignmentType, setAssignmentType] = useState<'person' | 'team'>('person')
|
||||
const [formData, setFormData] = useState({
|
||||
assignedTo: "",
|
||||
maintenanceTeamId: "",
|
||||
notes: "",
|
||||
});
|
||||
assignedTo: '',
|
||||
maintenanceTeamId: '',
|
||||
notes: '',
|
||||
})
|
||||
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (assignmentType === "person" && !formData.assignedTo) {
|
||||
newErrors.assignedTo = "Atanacak kişi seçilmelidir";
|
||||
if (assignmentType === 'person' && !formData.assignedTo) {
|
||||
newErrors.assignedTo = 'Atanacak kişi seçilmelidir'
|
||||
}
|
||||
|
||||
if (assignmentType === "team" && !formData.maintenanceTeamId) {
|
||||
newErrors.maintenanceTeamId = "Atanacak ekip seçilmelidir";
|
||||
if (assignmentType === 'team' && !formData.maintenanceTeamId) {
|
||||
newErrors.maintenanceTeamId = 'Atanacak ekip seçilmelidir'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!validateForm()) return;
|
||||
e.preventDefault()
|
||||
if (!validateForm()) return
|
||||
|
||||
const assignmentData: AssignmentData = {
|
||||
assignmentType,
|
||||
assignedTo: assignmentType === "person" ? formData.assignedTo : undefined,
|
||||
maintenanceTeamId:
|
||||
assignmentType === "team" ? formData.maintenanceTeamId : undefined,
|
||||
assignedTo: assignmentType === 'person' ? formData.assignedTo : undefined,
|
||||
maintenanceTeamId: assignmentType === 'team' ? formData.maintenanceTeamId : undefined,
|
||||
notes: formData.notes || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
onAssign(workOrders, assignmentData);
|
||||
onClose();
|
||||
};
|
||||
onAssign(workOrders, assignmentData)
|
||||
onClose()
|
||||
}
|
||||
|
||||
const getSelectedTeam = () => {
|
||||
if (assignmentType === "team" && formData.maintenanceTeamId) {
|
||||
return mockMaintenanceTeams.find(
|
||||
(team) => team.id === formData.maintenanceTeamId
|
||||
);
|
||||
if (assignmentType === 'team' && formData.maintenanceTeamId) {
|
||||
return mockMaintenanceTeams.find((team) => team.id === formData.maintenanceTeamId)
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
const getSelectedEmployee = () => {
|
||||
if (assignmentType === "person" && formData.assignedTo) {
|
||||
return mockEmployees.find((emp) => emp.fullName === formData.assignedTo);
|
||||
if (assignmentType === 'person' && formData.assignedTo) {
|
||||
return mockEmployees.find((emp) => emp.fullName === formData.assignedTo)
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -95,10 +87,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
<FaUserCog className="w-5 h-5 mr-2 text-blue-600" />
|
||||
İş Emirlerini Ata
|
||||
</h3>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -110,22 +99,15 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
</h4>
|
||||
<div className="max-h-32 overflow-y-auto border border-gray-200 rounded-lg">
|
||||
{workOrders.map((workOrder) => (
|
||||
<div
|
||||
key={workOrder.id}
|
||||
className="p-2 border-b border-gray-100 last:border-b-0"
|
||||
>
|
||||
<div key={workOrder.id} className="p-2 border-b border-gray-100 last:border-b-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900">
|
||||
{workOrder.workOrderNumber}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600 truncate">
|
||||
{workOrder.description}
|
||||
</p>
|
||||
<p className="font-medium text-gray-900">{workOrder.workOrderNumber}</p>
|
||||
<p className="text-sm text-gray-600 truncate">{workOrder.description}</p>
|
||||
</div>
|
||||
<div className="text-right text-sm">
|
||||
<p className="text-gray-600">
|
||||
Mevcut Atama: {workOrder.assignedTo || "Atanmadı"}
|
||||
Mevcut Atama: {workOrder.assignedTo || 'Atanmadı'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -137,43 +119,33 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
<form onSubmit={handleSubmit} className="space-y-3">
|
||||
{/* Assignment Type Selection */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Atama Türü
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Atama Türü</label>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<label className="relative">
|
||||
<input
|
||||
type="radio"
|
||||
name="assignmentType"
|
||||
value="person"
|
||||
checked={assignmentType === "person"}
|
||||
onChange={(e) =>
|
||||
setAssignmentType(e.target.value as "person" | "team")
|
||||
}
|
||||
checked={assignmentType === 'person'}
|
||||
onChange={(e) => setAssignmentType(e.target.value as 'person' | 'team')}
|
||||
className="sr-only"
|
||||
/>
|
||||
<div
|
||||
className={`p-2 border-2 rounded-lg cursor-pointer transition-colors ${
|
||||
assignmentType === "person"
|
||||
? "border-blue-500 bg-blue-50"
|
||||
: "border-gray-300 bg-white hover:bg-gray-50"
|
||||
assignmentType === 'person'
|
||||
? 'border-blue-500 bg-blue-50'
|
||||
: 'border-gray-300 bg-white hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<FaUser
|
||||
className={`w-5 h-5 ${
|
||||
assignmentType === "person"
|
||||
? "text-blue-600"
|
||||
: "text-gray-400"
|
||||
assignmentType === 'person' ? 'text-blue-600' : 'text-gray-400'
|
||||
}`}
|
||||
/>
|
||||
<div>
|
||||
<h4 className="font-medium text-sm text-gray-900">
|
||||
Kişiye Ata
|
||||
</h4>
|
||||
<p className="text-xs text-gray-600">
|
||||
Belirli bir çalışana atama yap
|
||||
</p>
|
||||
<h4 className="font-medium text-sm text-gray-900">Kişiye Ata</h4>
|
||||
<p className="text-xs text-gray-600">Belirli bir çalışana atama yap</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -184,34 +156,26 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
type="radio"
|
||||
name="assignmentType"
|
||||
value="team"
|
||||
checked={assignmentType === "team"}
|
||||
onChange={(e) =>
|
||||
setAssignmentType(e.target.value as "person" | "team")
|
||||
}
|
||||
checked={assignmentType === 'team'}
|
||||
onChange={(e) => setAssignmentType(e.target.value as 'person' | 'team')}
|
||||
className="sr-only"
|
||||
/>
|
||||
<div
|
||||
className={`p-2 border-2 rounded-lg cursor-pointer transition-colors ${
|
||||
assignmentType === "team"
|
||||
? "border-blue-500 bg-blue-50"
|
||||
: "border-gray-300 bg-white hover:bg-gray-50"
|
||||
assignmentType === 'team'
|
||||
? 'border-blue-500 bg-blue-50'
|
||||
: 'border-gray-300 bg-white hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<FaUsers
|
||||
className={`w-5 h-5 ${
|
||||
assignmentType === "team"
|
||||
? "text-blue-600"
|
||||
: "text-gray-400"
|
||||
assignmentType === 'team' ? 'text-blue-600' : 'text-gray-400'
|
||||
}`}
|
||||
/>
|
||||
<div>
|
||||
<h4 className="font-medium text-sm text-gray-900">
|
||||
Ekibe Ata
|
||||
</h4>
|
||||
<p className="text-xs text-gray-600">
|
||||
Bakım ekibine atama yap
|
||||
</p>
|
||||
<h4 className="font-medium text-sm text-gray-900">Ekibe Ata</h4>
|
||||
<p className="text-xs text-gray-600">Bakım ekibine atama yap</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -220,7 +184,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
</div>
|
||||
|
||||
{/* Person Assignment */}
|
||||
{assignmentType === "person" && (
|
||||
{assignmentType === 'person' && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
<FaUser className="w-4 h-4 inline mr-2" />
|
||||
|
|
@ -228,11 +192,9 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
</label>
|
||||
<select
|
||||
value={formData.assignedTo}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, assignedTo: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, assignedTo: e.target.value })}
|
||||
className={`w-full px-2 py-1 text-sm border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${
|
||||
errors.assignedTo ? "border-red-500" : "border-gray-300"
|
||||
errors.assignedTo ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">Kişi Seçin</option>
|
||||
|
|
@ -249,7 +211,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
)}
|
||||
|
||||
{/* Team Assignment */}
|
||||
{assignmentType === "team" && (
|
||||
{assignmentType === 'team' && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
<FaUsers className="w-4 h-4 inline mr-2" />
|
||||
|
|
@ -264,9 +226,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
})
|
||||
}
|
||||
className={`w-full px-2 py-1 text-sm border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${
|
||||
errors.maintenanceTeamId
|
||||
? "border-red-500"
|
||||
: "border-gray-300"
|
||||
errors.maintenanceTeamId ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">Ekip Seçin</option>
|
||||
|
|
@ -277,31 +237,23 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
))}
|
||||
</select>
|
||||
{errors.maintenanceTeamId && (
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
{errors.maintenanceTeamId}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-red-600">{errors.maintenanceTeamId}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Team Details */}
|
||||
{assignmentType === "team" && getSelectedTeam() && (
|
||||
{assignmentType === 'team' && getSelectedTeam() && (
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-2">
|
||||
<h5 className="font-medium text-sm text-blue-900 mb-2">
|
||||
Ekip Detayları
|
||||
</h5>
|
||||
<h5 className="font-medium text-sm text-blue-900 mb-2">Ekip Detayları</h5>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div>
|
||||
<span className="text-blue-700">Ekip Adı:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{getSelectedTeam()?.name}
|
||||
</span>
|
||||
<span className="ml-2 font-medium">{getSelectedTeam()?.name}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-blue-700">Üye Sayısı:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{getSelectedTeam()?.members.length}
|
||||
</span>
|
||||
<span className="ml-2 font-medium">{getSelectedTeam()?.members.length}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-blue-700">Uzmanlık Alanları:</span>
|
||||
|
|
@ -321,17 +273,13 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
)}
|
||||
|
||||
{/* Employee Details */}
|
||||
{assignmentType === "person" && getSelectedEmployee() && (
|
||||
{assignmentType === 'person' && getSelectedEmployee() && (
|
||||
<div className="bg-green-50 border border-green-200 rounded-lg p-2">
|
||||
<h5 className="font-medium text-sm text-green-900 mb-2">
|
||||
Çalışan Detayları
|
||||
</h5>
|
||||
<h5 className="font-medium text-sm text-green-900 mb-2">Çalışan Detayları</h5>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div>
|
||||
<span className="text-green-700">Ad Soyad:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{getSelectedEmployee()?.fullName}
|
||||
</span>
|
||||
<span className="ml-2 font-medium">{getSelectedEmployee()?.fullName}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-green-700">Pozisyon:</span>
|
||||
|
|
@ -351,14 +299,10 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
|
||||
{/* Notes */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Atama Notları
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Atama Notları</label>
|
||||
<textarea
|
||||
value={formData.notes}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, notes: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
|
||||
rows={2}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="Atama ile ilgili notlar..."
|
||||
|
|
@ -367,9 +311,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
|
||||
{/* Assignment Preview */}
|
||||
<div className="bg-gray-50 rounded-lg p-2">
|
||||
<h5 className="font-medium text-sm text-gray-900 mb-2">
|
||||
Atama Önizlemesi
|
||||
</h5>
|
||||
<h5 className="font-medium text-sm text-gray-900 mb-2">Atama Önizlemesi</h5>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">İş Emri Sayısı:</span>
|
||||
|
|
@ -378,19 +320,17 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">Atama Türü:</span>
|
||||
<span className="font-medium">
|
||||
{assignmentType === "person" ? "Kişiye Atama" : "Ekip Atama"}
|
||||
{assignmentType === 'person' ? 'Kişiye Atama' : 'Ekip Atama'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">
|
||||
{assignmentType === "person"
|
||||
? "Atanan Kişi:"
|
||||
: "Atanan Ekip:"}
|
||||
{assignmentType === 'person' ? 'Atanan Kişi:' : 'Atanan Ekip:'}
|
||||
</span>
|
||||
<span className="font-medium">
|
||||
{assignmentType === "person"
|
||||
? formData.assignedTo || "Seçilmedi"
|
||||
: getSelectedTeam()?.name || "Seçilmedi"}
|
||||
{assignmentType === 'person'
|
||||
? formData.assignedTo || 'Seçilmedi'
|
||||
: getSelectedTeam()?.name || 'Seçilmedi'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -416,7 +356,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default AssignWorkOrderModal;
|
||||
export default AssignWorkOrderModal
|
||||
|
|
|
|||
|
|
@ -1,43 +1,43 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaSave, FaExclamationTriangle } from "react-icons/fa";
|
||||
import { PmFaultNotification, NotificationStatusEnum } from "../../../types/pm";
|
||||
import {
|
||||
getNotificationStatusColor,
|
||||
getNotificationStatusText,
|
||||
} from "../../../utils/erp";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaSave, FaExclamationTriangle } from 'react-icons/fa'
|
||||
import { PmFaultNotification, NotificationStatusEnum } from '../../../types/pm'
|
||||
import { getNotificationStatusColor, getNotificationStatusText } from '../../../utils/erp'
|
||||
|
||||
interface ChangeNotificationStatusModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (statusChange: {
|
||||
notificationIds: string[];
|
||||
status: NotificationStatusEnum;
|
||||
notes?: string;
|
||||
}) => void;
|
||||
notifications: PmFaultNotification[];
|
||||
notificationIds: string[]
|
||||
status: NotificationStatusEnum
|
||||
notes?: string
|
||||
}) => void
|
||||
notifications: PmFaultNotification[]
|
||||
}
|
||||
|
||||
const ChangeNotificationStatusModal: React.FC<
|
||||
ChangeNotificationStatusModalProps
|
||||
> = ({ isOpen, onClose, onSave, notifications }) => {
|
||||
const ChangeNotificationStatusModal: React.FC<ChangeNotificationStatusModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSave,
|
||||
notifications,
|
||||
}) => {
|
||||
const [newStatus, setNewStatus] = useState<NotificationStatusEnum>(
|
||||
NotificationStatusEnum.InProgress
|
||||
);
|
||||
const [notes, setNotes] = useState("");
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
NotificationStatusEnum.InProgress,
|
||||
)
|
||||
const [notes, setNotes] = useState('')
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
|
||||
if (!isOpen || notifications.length === 0) return null;
|
||||
if (!isOpen || notifications.length === 0) return null
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (!newStatus) {
|
||||
newErrors.status = "Durum seçimi gerekli";
|
||||
newErrors.status = 'Durum seçimi gerekli'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
if (validateForm()) {
|
||||
|
|
@ -45,42 +45,40 @@ const ChangeNotificationStatusModal: React.FC<
|
|||
notificationIds: notifications.map((n) => n.id),
|
||||
status: newStatus,
|
||||
notes: notes.trim() || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
onSave(statusChangeData);
|
||||
onClose();
|
||||
onSave(statusChangeData)
|
||||
onClose()
|
||||
|
||||
// Reset form
|
||||
setNewStatus(NotificationStatusEnum.InProgress);
|
||||
setNotes("");
|
||||
setErrors({});
|
||||
setNewStatus(NotificationStatusEnum.InProgress)
|
||||
setNotes('')
|
||||
setErrors({})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const getCurrentStatuses = () => {
|
||||
const statusCounts = notifications.reduce((acc, notification) => {
|
||||
acc[notification.status] = (acc[notification.status] || 0) + 1;
|
||||
return acc;
|
||||
}, {} as Record<NotificationStatusEnum, number>);
|
||||
const statusCounts = notifications.reduce(
|
||||
(acc, notification) => {
|
||||
acc[notification.status] = (acc[notification.status] || 0) + 1
|
||||
return acc
|
||||
},
|
||||
{} as Record<NotificationStatusEnum, number>,
|
||||
)
|
||||
|
||||
return Object.entries(statusCounts).map(([status, count]) => ({
|
||||
status: status as NotificationStatusEnum,
|
||||
count,
|
||||
}));
|
||||
};
|
||||
}))
|
||||
}
|
||||
|
||||
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">
|
||||
Durum Değiştir
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
||||
>
|
||||
<h2 className="text-lg font-semibold text-gray-900">Durum Değiştir</h2>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<FaTimes className="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -95,20 +93,18 @@ const ChangeNotificationStatusModal: React.FC<
|
|||
<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 className="font-medium">{notification.notificationCode}</span>
|
||||
{' - '}
|
||||
<span>{notification.title}</span>
|
||||
{" ("}
|
||||
{' ('}
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs ${getNotificationStatusColor(
|
||||
notification.status
|
||||
notification.status,
|
||||
)}`}
|
||||
>
|
||||
{getNotificationStatusText(notification.status)}
|
||||
</span>
|
||||
{")"}
|
||||
{')'}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -116,15 +112,13 @@ const ChangeNotificationStatusModal: React.FC<
|
|||
|
||||
{/* Current Status Summary */}
|
||||
<div className="bg-gray-50 border border-gray-200 rounded-lg p-3">
|
||||
<h4 className="text-xs font-medium text-gray-800 mb-2">
|
||||
Mevcut Durumlar
|
||||
</h4>
|
||||
<h4 className="text-xs font-medium text-gray-800 mb-2">Mevcut Durumlar</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{getCurrentStatuses().map(({ status, count }) => (
|
||||
<span
|
||||
key={status}
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getNotificationStatusColor(
|
||||
status
|
||||
status,
|
||||
)}`}
|
||||
>
|
||||
{getNotificationStatusText(status)}: {count}
|
||||
|
|
@ -135,32 +129,22 @@ const ChangeNotificationStatusModal: React.FC<
|
|||
|
||||
{/* New Status Selection */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Yeni Durum *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Yeni Durum *</label>
|
||||
<select
|
||||
value={newStatus}
|
||||
onChange={(e) =>
|
||||
setNewStatus(e.target.value as NotificationStatusEnum)
|
||||
}
|
||||
onChange={(e) => setNewStatus(e.target.value as NotificationStatusEnum)}
|
||||
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.status ? "border-red-500" : "border-gray-300"
|
||||
errors.status ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value={NotificationStatusEnum.Open}>Açık</option>
|
||||
<option value={NotificationStatusEnum.Assigned}>Atandı</option>
|
||||
<option value={NotificationStatusEnum.InProgress}>
|
||||
Devam Ediyor
|
||||
</option>
|
||||
<option value={NotificationStatusEnum.InProgress}>Devam Ediyor</option>
|
||||
<option value={NotificationStatusEnum.Resolved}>Çözüldü</option>
|
||||
<option value={NotificationStatusEnum.Closed}>Kapatıldı</option>
|
||||
<option value={NotificationStatusEnum.Rejected}>
|
||||
Reddedildi
|
||||
</option>
|
||||
<option value={NotificationStatusEnum.Rejected}>Reddedildi</option>
|
||||
</select>
|
||||
{errors.status && (
|
||||
<p className="text-red-500 text-sm mt-1">{errors.status}</p>
|
||||
)}
|
||||
{errors.status && <p className="text-red-500 text-sm mt-1">{errors.status}</p>}
|
||||
</div>
|
||||
|
||||
{/* Status Change Preview */}
|
||||
|
|
@ -168,18 +152,16 @@ const ChangeNotificationStatusModal: React.FC<
|
|||
<div className="flex items-start space-x-3">
|
||||
<FaExclamationTriangle className="w-5 h-5 text-green-600 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<h4 className="text-sm font-medium text-green-800 mb-1">
|
||||
Durum Değişikliği
|
||||
</h4>
|
||||
<h4 className="text-sm font-medium text-green-800 mb-1">Durum Değişikliği</h4>
|
||||
<p className="text-sm text-green-700">
|
||||
<strong>{notifications.length}</strong> arıza bildirimi{" "}
|
||||
<strong>{notifications.length}</strong> arıza bildirimi{' '}
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs ${getNotificationStatusColor(
|
||||
newStatus
|
||||
newStatus,
|
||||
)}`}
|
||||
>
|
||||
{getNotificationStatusText(newStatus)}
|
||||
</span>{" "}
|
||||
</span>{' '}
|
||||
durumuna geçirilecek.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -207,12 +189,10 @@ const ChangeNotificationStatusModal: React.FC<
|
|||
<div className="flex items-start space-x-3">
|
||||
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<h4 className="text-sm font-medium text-yellow-800 mb-1">
|
||||
Dikkat!
|
||||
</h4>
|
||||
<h4 className="text-sm font-medium text-yellow-800 mb-1">Dikkat!</h4>
|
||||
<p className="text-sm text-yellow-700">
|
||||
Bu durum değişikliği kalıcıdır ve bildirimlerin aktif
|
||||
süreçlerden çıkarılmasına neden olabilir.
|
||||
Bu durum değişikliği kalıcıdır ve bildirimlerin aktif süreçlerden çıkarılmasına
|
||||
neden olabilir.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -238,7 +218,7 @@ const ChangeNotificationStatusModal: React.FC<
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default ChangeNotificationStatusModal;
|
||||
export default ChangeNotificationStatusModal
|
||||
|
|
|
|||
|
|
@ -1,32 +1,23 @@
|
|||
import React, { useState } from "react";
|
||||
import {
|
||||
FaTimes,
|
||||
FaExchangeAlt,
|
||||
FaCheck,
|
||||
FaPause,
|
||||
FaTimesCircle,
|
||||
} from "react-icons/fa";
|
||||
import { PmMaintenanceWorkOrder, WorkOrderStatusEnum } from "../../../types/pm";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaExchangeAlt, FaCheck, FaPause, FaTimesCircle } from 'react-icons/fa'
|
||||
import { PmMaintenanceWorkOrder, WorkOrderStatusEnum } from '../../../types/pm'
|
||||
import {
|
||||
getWorkOrderStatusColor,
|
||||
getWorkOrderStatusText,
|
||||
getWorkOrderStatusIcon,
|
||||
} from "../../../utils/erp";
|
||||
} from '../../../utils/erp'
|
||||
|
||||
interface ChangeWorkOrderStatusModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onStatusChange: (
|
||||
workOrders: PmMaintenanceWorkOrder[],
|
||||
statusData: StatusChangeData
|
||||
) => void;
|
||||
workOrders: PmMaintenanceWorkOrder[];
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onStatusChange: (workOrders: PmMaintenanceWorkOrder[], statusData: StatusChangeData) => void
|
||||
workOrders: PmMaintenanceWorkOrder[]
|
||||
}
|
||||
|
||||
interface StatusChangeData {
|
||||
newStatus: WorkOrderStatusEnum;
|
||||
notes?: string;
|
||||
completionNotes?: string;
|
||||
newStatus: WorkOrderStatusEnum
|
||||
notes?: string
|
||||
completionNotes?: string
|
||||
}
|
||||
|
||||
const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||
|
|
@ -37,47 +28,40 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
}) => {
|
||||
const [formData, setFormData] = useState({
|
||||
newStatus: WorkOrderStatusEnum.InProgress,
|
||||
notes: "",
|
||||
completionNotes: "",
|
||||
});
|
||||
notes: '',
|
||||
completionNotes: '',
|
||||
})
|
||||
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (
|
||||
formData.newStatus === WorkOrderStatusEnum.Completed &&
|
||||
!formData.completionNotes.trim()
|
||||
) {
|
||||
newErrors.completionNotes =
|
||||
"Tamamlanan iş emirleri için tamamlanma notları zorunludur";
|
||||
if (formData.newStatus === WorkOrderStatusEnum.Completed && !formData.completionNotes.trim()) {
|
||||
newErrors.completionNotes = 'Tamamlanan iş emirleri için tamamlanma notları zorunludur'
|
||||
}
|
||||
|
||||
if (
|
||||
formData.newStatus === WorkOrderStatusEnum.Cancelled &&
|
||||
!formData.notes.trim()
|
||||
) {
|
||||
newErrors.notes = "İptal edilen iş emirleri için iptal nedeni zorunludur";
|
||||
if (formData.newStatus === WorkOrderStatusEnum.Cancelled && !formData.notes.trim()) {
|
||||
newErrors.notes = 'İptal edilen iş emirleri için iptal nedeni zorunludur'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!validateForm()) return;
|
||||
e.preventDefault()
|
||||
if (!validateForm()) return
|
||||
|
||||
const statusData: StatusChangeData = {
|
||||
newStatus: formData.newStatus,
|
||||
notes: formData.notes || undefined,
|
||||
completionNotes: formData.completionNotes || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
onStatusChange(workOrders, statusData);
|
||||
onClose();
|
||||
};
|
||||
onStatusChange(workOrders, statusData)
|
||||
onClose()
|
||||
}
|
||||
|
||||
const getStatusCounts = () => {
|
||||
const counts: Record<WorkOrderStatusEnum, number> = {
|
||||
|
|
@ -88,43 +72,42 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
[WorkOrderStatusEnum.OnHold]: 0,
|
||||
[WorkOrderStatusEnum.Completed]: 0,
|
||||
[WorkOrderStatusEnum.Cancelled]: 0,
|
||||
};
|
||||
}
|
||||
|
||||
workOrders.forEach((wo) => {
|
||||
counts[wo.status]++;
|
||||
});
|
||||
counts[wo.status]++
|
||||
})
|
||||
|
||||
return counts;
|
||||
};
|
||||
return counts
|
||||
}
|
||||
|
||||
const canChangeToStatus = (status: WorkOrderStatusEnum) => {
|
||||
// İş emirlerinin mevcut durumlarına göre hangi durumlara geçebileceğini kontrol et
|
||||
return workOrders.some((wo) => {
|
||||
switch (status) {
|
||||
case WorkOrderStatusEnum.Planned:
|
||||
return wo.status === WorkOrderStatusEnum.Created;
|
||||
return wo.status === WorkOrderStatusEnum.Created
|
||||
case WorkOrderStatusEnum.Released:
|
||||
return wo.status === WorkOrderStatusEnum.Planned;
|
||||
return wo.status === WorkOrderStatusEnum.Planned
|
||||
case WorkOrderStatusEnum.InProgress:
|
||||
return (
|
||||
wo.status === WorkOrderStatusEnum.Released ||
|
||||
wo.status === WorkOrderStatusEnum.OnHold
|
||||
);
|
||||
wo.status === WorkOrderStatusEnum.Released || wo.status === WorkOrderStatusEnum.OnHold
|
||||
)
|
||||
case WorkOrderStatusEnum.OnHold:
|
||||
return wo.status === WorkOrderStatusEnum.InProgress;
|
||||
return wo.status === WorkOrderStatusEnum.InProgress
|
||||
case WorkOrderStatusEnum.Completed:
|
||||
return wo.status === WorkOrderStatusEnum.InProgress;
|
||||
return wo.status === WorkOrderStatusEnum.InProgress
|
||||
case WorkOrderStatusEnum.Cancelled:
|
||||
return wo.status !== WorkOrderStatusEnum.Completed;
|
||||
return wo.status !== WorkOrderStatusEnum.Completed
|
||||
default:
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
const statusCounts = getStatusCounts();
|
||||
const statusCounts = getStatusCounts()
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -134,10 +117,7 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
<FaExchangeAlt className="w-5 h-5 mr-2 text-orange-600" />
|
||||
İş Emri Durumunu Değiştir
|
||||
</h3>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||
<FaTimes className="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -150,46 +130,36 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
|
||||
{/* Current Status Distribution */}
|
||||
<div className="mb-4">
|
||||
<h5 className="text-sm font-medium text-gray-700 mb-2">
|
||||
Mevcut Durum Dağılımı
|
||||
</h5>
|
||||
<h5 className="text-sm font-medium text-gray-700 mb-2">Mevcut Durum Dağılımı</h5>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Object.entries(statusCounts).map(([status, count]) => {
|
||||
if (count === 0) return null;
|
||||
if (count === 0) return null
|
||||
return (
|
||||
<span
|
||||
key={status}
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
||||
status as WorkOrderStatusEnum
|
||||
status as WorkOrderStatusEnum,
|
||||
)}`}
|
||||
>
|
||||
{getWorkOrderStatusText(status as WorkOrderStatusEnum)}:{" "}
|
||||
{count}
|
||||
{getWorkOrderStatusText(status as WorkOrderStatusEnum)}: {count}
|
||||
</span>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-h-40 overflow-y-auto border border-gray-200 rounded-lg">
|
||||
{workOrders.map((workOrder) => (
|
||||
<div
|
||||
key={workOrder.id}
|
||||
className="p-2 border-b border-gray-100 last:border-b-0"
|
||||
>
|
||||
<div key={workOrder.id} className="p-2 border-b border-gray-100 last:border-b-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900">
|
||||
{workOrder.workOrderNumber}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600 truncate">
|
||||
{workOrder.description}
|
||||
</p>
|
||||
<p className="font-medium text-gray-900">{workOrder.workOrderNumber}</p>
|
||||
<p className="text-sm text-gray-600 truncate">{workOrder.description}</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
||||
workOrder.status
|
||||
workOrder.status,
|
||||
)}`}
|
||||
>
|
||||
{getWorkOrderStatusText(workOrder.status)}
|
||||
|
|
@ -204,9 +174,7 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
{/* New Status Selection */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-3">
|
||||
Yeni Durum *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-3">Yeni Durum *</label>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
WorkOrderStatusEnum.Planned,
|
||||
|
|
@ -234,19 +202,17 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
<div
|
||||
className={`p-2 border-2 rounded-lg cursor-pointer transition-colors ${
|
||||
formData.newStatus === status
|
||||
? "border-blue-500 bg-blue-50"
|
||||
? 'border-blue-500 bg-blue-50'
|
||||
: canChangeToStatus(status)
|
||||
? "border-gray-300 bg-white hover:bg-gray-50"
|
||||
: "border-gray-200 bg-gray-50 cursor-not-allowed"
|
||||
? 'border-gray-300 bg-white hover:bg-gray-50'
|
||||
: 'border-gray-200 bg-gray-50 cursor-not-allowed'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
{getWorkOrderStatusIcon(status)}
|
||||
<span
|
||||
className={`text-sm font-medium ${
|
||||
canChangeToStatus(status)
|
||||
? "text-gray-900"
|
||||
: "text-gray-400"
|
||||
canChangeToStatus(status) ? 'text-gray-900' : 'text-gray-400'
|
||||
}`}
|
||||
>
|
||||
{getWorkOrderStatusText(status)}
|
||||
|
|
@ -266,19 +232,15 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
</label>
|
||||
<textarea
|
||||
value={formData.completionNotes}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, completionNotes: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, completionNotes: e.target.value })}
|
||||
rows={3}
|
||||
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.completionNotes ? "border-red-500" : "border-gray-300"
|
||||
errors.completionNotes ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="İş emirlerinin nasıl tamamlandığına dair detaylar..."
|
||||
/>
|
||||
{errors.completionNotes && (
|
||||
<p className="mt-1 text-sm text-red-600">
|
||||
{errors.completionNotes}
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-red-600">{errors.completionNotes}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -287,27 +249,23 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{formData.newStatus === WorkOrderStatusEnum.Cancelled
|
||||
? "İptal Nedeni *"
|
||||
: "Durum Değişikliği Notları"}
|
||||
? 'İptal Nedeni *'
|
||||
: 'Durum Değişikliği Notları'}
|
||||
</label>
|
||||
<textarea
|
||||
value={formData.notes}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, notes: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
|
||||
rows={3}
|
||||
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.notes ? "border-red-500" : "border-gray-300"
|
||||
errors.notes ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder={
|
||||
formData.newStatus === WorkOrderStatusEnum.Cancelled
|
||||
? "İptal nedenini açıklayın..."
|
||||
: "Durum değişikliği ile ilgili notlar..."
|
||||
? 'İptal nedenini açıklayın...'
|
||||
: 'Durum değişikliği ile ilgili notlar...'
|
||||
}
|
||||
/>
|
||||
{errors.notes && (
|
||||
<p className="mt-1 text-sm text-red-600">{errors.notes}</p>
|
||||
)}
|
||||
{errors.notes && <p className="mt-1 text-sm text-red-600">{errors.notes}</p>}
|
||||
</div>
|
||||
|
||||
{/* Status Change Warnings */}
|
||||
|
|
@ -318,8 +276,8 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
<div>
|
||||
<h5 className="font-medium text-red-800 mb-1">Uyarı</h5>
|
||||
<p className="text-red-700 text-sm">
|
||||
İş emirleri iptal edilecek. Bu işlem geri alınamaz ve tüm
|
||||
aktiviteler durdurulacak.
|
||||
İş emirleri iptal edilecek. Bu işlem geri alınamaz ve tüm aktiviteler
|
||||
durdurulacak.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -333,8 +291,8 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
<div>
|
||||
<h5 className="font-medium text-green-800 mb-1">Bilgi</h5>
|
||||
<p className="text-green-700 text-sm">
|
||||
İş emirleri tamamlandı olarak işaretlenecek. Gerçek bitiş
|
||||
tarihi otomatik olarak şu an olarak ayarlanacak.
|
||||
İş emirleri tamamlandı olarak işaretlenecek. Gerçek bitiş tarihi otomatik olarak
|
||||
şu an olarak ayarlanacak.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -348,8 +306,8 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
<div>
|
||||
<h5 className="font-medium text-yellow-800 mb-1">Bilgi</h5>
|
||||
<p className="text-yellow-700 text-sm">
|
||||
İş emirleri beklemede durumuna alınacak. Aktiviteler
|
||||
durdurulacak ancak daha sonra devam ettirilebilir.
|
||||
İş emirleri beklemede durumuna alınacak. Aktiviteler durdurulacak ancak daha
|
||||
sonra devam ettirilebilir.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -358,9 +316,7 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
|
||||
{/* Change Preview */}
|
||||
<div className="bg-gray-50 rounded-lg p-3">
|
||||
<h5 className="font-medium text-sm text-gray-900 mb-2">
|
||||
Durum Değişikliği Önizlemesi
|
||||
</h5>
|
||||
<h5 className="font-medium text-sm text-gray-900 mb-2">Durum Değişikliği Önizlemesi</h5>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">Etkilenen İş Emri Sayısı:</span>
|
||||
|
|
@ -370,7 +326,7 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
<span className="text-gray-600">Yeni Durum:</span>
|
||||
<span
|
||||
className={`font-medium px-2 py-1 rounded text-xs ${getWorkOrderStatusColor(
|
||||
formData.newStatus
|
||||
formData.newStatus,
|
||||
)}`}
|
||||
>
|
||||
{getWorkOrderStatusText(formData.newStatus)}
|
||||
|
|
@ -379,10 +335,10 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">Değişiklik Tarihi:</span>
|
||||
<span className="font-medium">
|
||||
{new Date().toLocaleDateString("tr-TR")}{" "}
|
||||
{new Date().toLocaleTimeString("tr-TR", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
{new Date().toLocaleDateString('tr-TR')}{' '}
|
||||
{new Date().toLocaleTimeString('tr-TR', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -402,10 +358,10 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
type="submit"
|
||||
className={`px-4 py-1.5 text-sm text-white rounded-lg flex items-center space-x-2 ${
|
||||
formData.newStatus === WorkOrderStatusEnum.Cancelled
|
||||
? "bg-red-600 hover:bg-red-700"
|
||||
? 'bg-red-600 hover:bg-red-700'
|
||||
: formData.newStatus === WorkOrderStatusEnum.Completed
|
||||
? "bg-green-600 hover:bg-green-700"
|
||||
: "bg-orange-600 hover:bg-orange-700"
|
||||
? 'bg-green-600 hover:bg-green-700'
|
||||
: 'bg-orange-600 hover:bg-orange-700'
|
||||
}`}
|
||||
>
|
||||
<FaExchangeAlt className="w-4 h-4" />
|
||||
|
|
@ -415,7 +371,7 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default ChangeWorkOrderStatusModal;
|
||||
export default ChangeWorkOrderStatusModal
|
||||
|
|
|
|||
|
|
@ -1,73 +1,74 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaSave, FaExclamationTriangle } from "react-icons/fa";
|
||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
||||
import { PmFaultNotification, WorkOrderTypeEnum } from "../../../types/pm";
|
||||
import { MrpWorkOrder } from "../../../types/mrp";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaSave, FaExclamationTriangle } from 'react-icons/fa'
|
||||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||
import { PmFaultNotification, WorkOrderTypeEnum } from '../../../types/pm'
|
||||
import { MrpWorkOrder } from '../../../types/mrp'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
|
||||
interface CreateWorkOrderFromNotificationModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (workOrderData: MrpWorkOrder) => void;
|
||||
notifications: PmFaultNotification[];
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (workOrderData: MrpWorkOrder) => void
|
||||
notifications: PmFaultNotification[]
|
||||
}
|
||||
|
||||
const CreateWorkOrderFromNotificationModal: React.FC<
|
||||
CreateWorkOrderFromNotificationModalProps
|
||||
> = ({ isOpen, onClose, onSave, notifications }) => {
|
||||
const CreateWorkOrderFromNotificationModal: React.FC<CreateWorkOrderFromNotificationModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSave,
|
||||
notifications,
|
||||
}) => {
|
||||
const [workOrderData, setWorkOrderData] = useState({
|
||||
workOrderNumber: `WO-${new Date().getFullYear()}-${String(Date.now()).slice(
|
||||
-3
|
||||
)}`,
|
||||
workOrderNumber: `WO-${new Date().getFullYear()}-${String(Date.now()).slice(-3)}`,
|
||||
orderType: WorkOrderTypeEnum.Corrective,
|
||||
priority: PriorityEnum.Normal,
|
||||
description: "",
|
||||
assignedTo: "",
|
||||
maintenanceTeamId: "",
|
||||
scheduledStart: "",
|
||||
scheduledEnd: "",
|
||||
description: '',
|
||||
assignedTo: '',
|
||||
maintenanceTeamId: '',
|
||||
scheduledStart: '',
|
||||
scheduledEnd: '',
|
||||
estimatedCost: 0,
|
||||
notes: "",
|
||||
});
|
||||
notes: '',
|
||||
})
|
||||
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
|
||||
if (!isOpen || notifications.length === 0) return null;
|
||||
if (!isOpen || notifications.length === 0) return null
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (!workOrderData.description.trim()) {
|
||||
newErrors.description = "Açıklama gerekli";
|
||||
newErrors.description = 'Açıklama gerekli'
|
||||
}
|
||||
if (!workOrderData.assignedTo && !workOrderData.maintenanceTeamId) {
|
||||
newErrors.assignment = "Kişi veya ekip ataması gerekli";
|
||||
newErrors.assignment = 'Kişi veya ekip ataması gerekli'
|
||||
}
|
||||
if (!workOrderData.scheduledStart) {
|
||||
newErrors.scheduledStart = "Başlangıç tarihi gerekli";
|
||||
newErrors.scheduledStart = 'Başlangıç tarihi gerekli'
|
||||
}
|
||||
if (!workOrderData.scheduledEnd) {
|
||||
newErrors.scheduledEnd = "Bitiş tarihi gerekli";
|
||||
newErrors.scheduledEnd = 'Bitiş tarihi gerekli'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleInputChange = (field: string, value: string | number) => {
|
||||
setWorkOrderData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
}))
|
||||
// Clear error when user starts typing
|
||||
if (errors[field]) {
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
[field]: "",
|
||||
}));
|
||||
[field]: '',
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
if (validateForm()) {
|
||||
|
|
@ -79,42 +80,35 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
reportedBy: notifications[0].reportedBy,
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
onSave(workOrderToSave);
|
||||
onClose();
|
||||
onSave(workOrderToSave)
|
||||
onClose()
|
||||
|
||||
// Reset form
|
||||
setWorkOrderData({
|
||||
workOrderNumber: `WO-${new Date().getFullYear()}-${String(
|
||||
Date.now()
|
||||
).slice(-3)}`,
|
||||
workOrderNumber: `WO-${new Date().getFullYear()}-${String(Date.now()).slice(-3)}`,
|
||||
orderType: WorkOrderTypeEnum.Corrective,
|
||||
priority: PriorityEnum.Normal,
|
||||
description: "",
|
||||
assignedTo: "",
|
||||
maintenanceTeamId: "",
|
||||
scheduledStart: "",
|
||||
scheduledEnd: "",
|
||||
description: '',
|
||||
assignedTo: '',
|
||||
maintenanceTeamId: '',
|
||||
scheduledStart: '',
|
||||
scheduledEnd: '',
|
||||
estimatedCost: 0,
|
||||
notes: "",
|
||||
});
|
||||
setErrors({});
|
||||
notes: '',
|
||||
})
|
||||
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-3xl 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">
|
||||
İş Emri Oluştur
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
||||
>
|
||||
<h2 className="text-lg font-semibold text-gray-900">İş Emri Oluştur</h2>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<FaTimes className="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -131,18 +125,13 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
</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>
|
||||
{" - "}
|
||||
<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>
|
||||
|
|
@ -159,55 +148,36 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
<input
|
||||
type="text"
|
||||
value={workOrderData.workOrderNumber}
|
||||
onChange={(e) =>
|
||||
handleInputChange("workOrderNumber", e.target.value)
|
||||
}
|
||||
onChange={(e) => handleInputChange('workOrderNumber', 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"
|
||||
placeholder="WO-2024-001"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
İş Emri Türü
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">İş Emri Türü</label>
|
||||
<select
|
||||
value={workOrderData.orderType}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"orderType",
|
||||
e.target.value as WorkOrderTypeEnum
|
||||
)
|
||||
handleInputChange('orderType', e.target.value as WorkOrderTypeEnum)
|
||||
}
|
||||
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={WorkOrderTypeEnum.Preventive}>
|
||||
Önleyici Bakım
|
||||
</option>
|
||||
<option value={WorkOrderTypeEnum.Corrective}>
|
||||
Düzeltici Bakım
|
||||
</option>
|
||||
<option value={WorkOrderTypeEnum.Emergency}>
|
||||
Acil Müdahale
|
||||
</option>
|
||||
<option value={WorkOrderTypeEnum.Preventive}>Önleyici Bakım</option>
|
||||
<option value={WorkOrderTypeEnum.Corrective}>Düzeltici Bakım</option>
|
||||
<option value={WorkOrderTypeEnum.Emergency}>Acil Müdahale</option>
|
||||
<option value={WorkOrderTypeEnum.Inspection}>İnceleme</option>
|
||||
<option value={WorkOrderTypeEnum.Calibration}>
|
||||
Kalibrasyon
|
||||
</option>
|
||||
<option value={WorkOrderTypeEnum.Calibration}>Kalibrasyon</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Priority */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Öncelik
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Öncelik</label>
|
||||
<select
|
||||
value={workOrderData.priority}
|
||||
onChange={(e) =>
|
||||
handleInputChange("priority", e.target.value as PriorityEnum)
|
||||
}
|
||||
onChange={(e) => handleInputChange('priority', e.target.value as PriorityEnum)}
|
||||
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>
|
||||
|
|
@ -219,15 +189,13 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
|
||||
{/* Description */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Açıklama *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Açıklama *</label>
|
||||
<textarea
|
||||
value={workOrderData.description}
|
||||
onChange={(e) => handleInputChange("description", e.target.value)}
|
||||
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||
rows={4}
|
||||
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.description ? "border-red-500" : "border-gray-300"
|
||||
errors.description ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="İş emri detayları ve yapılacak işlemler"
|
||||
/>
|
||||
|
|
@ -239,15 +207,13 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
{/* 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-1">
|
||||
Atanan Kişi
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Atanan Kişi</label>
|
||||
<select
|
||||
value={workOrderData.assignedTo}
|
||||
onChange={(e) => {
|
||||
handleInputChange("assignedTo", e.target.value);
|
||||
handleInputChange('assignedTo', e.target.value)
|
||||
if (e.target.value) {
|
||||
handleInputChange("maintenanceTeamId", "");
|
||||
handleInputChange('maintenanceTeamId', '')
|
||||
}
|
||||
}}
|
||||
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"
|
||||
|
|
@ -262,15 +228,13 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Atanan Ekip
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Atanan Ekip</label>
|
||||
<select
|
||||
value={workOrderData.maintenanceTeamId}
|
||||
onChange={(e) => {
|
||||
handleInputChange("maintenanceTeamId", e.target.value);
|
||||
handleInputChange('maintenanceTeamId', e.target.value)
|
||||
if (e.target.value) {
|
||||
handleInputChange("assignedTo", "");
|
||||
handleInputChange('assignedTo', '')
|
||||
}
|
||||
}}
|
||||
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"
|
||||
|
|
@ -284,9 +248,7 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{errors.assignment && (
|
||||
<p className="text-red-500 text-sm mt-1">{errors.assignment}</p>
|
||||
)}
|
||||
{errors.assignment && <p className="text-red-500 text-sm mt-1">{errors.assignment}</p>}
|
||||
|
||||
{/* Schedule */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
|
|
@ -297,17 +259,13 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
<input
|
||||
type="datetime-local"
|
||||
value={workOrderData.scheduledStart}
|
||||
onChange={(e) =>
|
||||
handleInputChange("scheduledStart", e.target.value)
|
||||
}
|
||||
onChange={(e) => handleInputChange('scheduledStart', 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.scheduledStart ? "border-red-500" : "border-gray-300"
|
||||
errors.scheduledStart ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
/>
|
||||
{errors.scheduledStart && (
|
||||
<p className="text-red-500 text-sm mt-1">
|
||||
{errors.scheduledStart}
|
||||
</p>
|
||||
<p className="text-red-500 text-sm mt-1">{errors.scheduledStart}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
|
@ -318,17 +276,13 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
<input
|
||||
type="datetime-local"
|
||||
value={workOrderData.scheduledEnd}
|
||||
onChange={(e) =>
|
||||
handleInputChange("scheduledEnd", e.target.value)
|
||||
}
|
||||
onChange={(e) => handleInputChange('scheduledEnd', 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.scheduledEnd ? "border-red-500" : "border-gray-300"
|
||||
errors.scheduledEnd ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
/>
|
||||
{errors.scheduledEnd && (
|
||||
<p className="text-red-500 text-sm mt-1">
|
||||
{errors.scheduledEnd}
|
||||
</p>
|
||||
<p className="text-red-500 text-sm mt-1">{errors.scheduledEnd}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -341,12 +295,7 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
<input
|
||||
type="number"
|
||||
value={workOrderData.estimatedCost}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"estimatedCost",
|
||||
parseFloat(e.target.value) || 0
|
||||
)
|
||||
}
|
||||
onChange={(e) => handleInputChange('estimatedCost', parseFloat(e.target.value) || 0)}
|
||||
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="0.00"
|
||||
min="0"
|
||||
|
|
@ -356,12 +305,10 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
|
||||
{/* Notes */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Notlar
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Notlar</label>
|
||||
<textarea
|
||||
value={workOrderData.notes}
|
||||
onChange={(e) => handleInputChange("notes", e.target.value)}
|
||||
onChange={(e) => handleInputChange('notes', e.target.value)}
|
||||
rows={3}
|
||||
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="Ek notlar ve özel talimatlar"
|
||||
|
|
@ -387,7 +334,7 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateWorkOrderFromNotificationModal;
|
||||
export default CreateWorkOrderFromNotificationModal
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaSave, FaExclamationTriangle } from "react-icons/fa";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaSave, FaExclamationTriangle } from 'react-icons/fa'
|
||||
import {
|
||||
PmMaintenancePlan,
|
||||
PmMaintenanceWorkOrder,
|
||||
WorkOrderStatusEnum,
|
||||
WorkOrderTypeEnum,
|
||||
} from "../../../types/pm";
|
||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
import { getPriorityColor } from "../../../utils/erp";
|
||||
} from '../../../types/pm'
|
||||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
import { getPriorityColor } from '../../../utils/erp'
|
||||
|
||||
interface CreateWorkOrderModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (workOrders: Partial<PmMaintenanceWorkOrder>[]) => void;
|
||||
selectedPlans: PmMaintenancePlan[];
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (workOrders: Partial<PmMaintenanceWorkOrder>[]) => void
|
||||
selectedPlans: PmMaintenancePlan[]
|
||||
}
|
||||
|
||||
const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||
|
|
@ -24,64 +24,54 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
selectedPlans,
|
||||
}) => {
|
||||
const [commonSettings, setCommonSettings] = useState({
|
||||
scheduledStartDate: new Date().toISOString().split("T")[0],
|
||||
scheduledStartDate: new Date().toISOString().split('T')[0],
|
||||
priority: PriorityEnum.Normal,
|
||||
assignedTo: "",
|
||||
notes: "",
|
||||
assignedTo: '',
|
||||
notes: '',
|
||||
createSeparateOrders: true,
|
||||
});
|
||||
})
|
||||
|
||||
const [individualSettings, setIndividualSettings] = useState<{
|
||||
[key: string]: {
|
||||
scheduledStartDate?: string;
|
||||
priority?: PriorityEnum;
|
||||
assignedTo?: string;
|
||||
notes?: string;
|
||||
};
|
||||
}>({});
|
||||
scheduledStartDate?: string
|
||||
priority?: PriorityEnum
|
||||
assignedTo?: string
|
||||
notes?: string
|
||||
}
|
||||
}>({})
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
const handleCommonChange = (
|
||||
field: string,
|
||||
value: string | PriorityEnum | boolean
|
||||
) => {
|
||||
const handleCommonChange = (field: string, value: string | PriorityEnum | boolean) => {
|
||||
setCommonSettings((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
}))
|
||||
}
|
||||
|
||||
const handleIndividualChange = (
|
||||
planId: string,
|
||||
field: string,
|
||||
value: string | PriorityEnum
|
||||
) => {
|
||||
const handleIndividualChange = (planId: string, field: string, value: string | PriorityEnum) => {
|
||||
setIndividualSettings((prev) => ({
|
||||
...prev,
|
||||
[planId]: {
|
||||
...prev[planId],
|
||||
[field]: value,
|
||||
},
|
||||
}));
|
||||
};
|
||||
}))
|
||||
}
|
||||
|
||||
const generateWorkOrderCode = (plan: PmMaintenancePlan, index: number) => {
|
||||
const date = new Date();
|
||||
const dateStr = date.toISOString().slice(0, 10).replace(/-/g, "");
|
||||
return `WO-${plan.planCode}-${dateStr}-${String(index + 1).padStart(
|
||||
3,
|
||||
"0"
|
||||
)}`;
|
||||
};
|
||||
const date = new Date()
|
||||
const dateStr = date.toISOString().slice(0, 10).replace(/-/g, '')
|
||||
return `WO-${plan.planCode}-${dateStr}-${String(index + 1).padStart(3, '0')}`
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
const workOrders: Partial<PmMaintenanceWorkOrder>[] = [];
|
||||
const workOrders: Partial<PmMaintenanceWorkOrder>[] = []
|
||||
|
||||
if (commonSettings.createSeparateOrders) {
|
||||
// Her plan için ayrı iş emri oluştur
|
||||
selectedPlans.forEach((plan, index) => {
|
||||
const individual = individualSettings[plan.id] || {};
|
||||
const individual = individualSettings[plan.id] || {}
|
||||
const workOrder: Partial<PmMaintenanceWorkOrder> = {
|
||||
workOrderNumber: generateWorkOrderCode(plan, index),
|
||||
planId: plan.id,
|
||||
|
|
@ -91,16 +81,14 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
status: WorkOrderStatusEnum.Planned,
|
||||
description: `${plan.description} - Bakım Planı: ${plan.planCode}`,
|
||||
scheduledStart: new Date(
|
||||
individual.scheduledStartDate || commonSettings.scheduledStartDate
|
||||
individual.scheduledStartDate || commonSettings.scheduledStartDate,
|
||||
),
|
||||
scheduledEnd: new Date(
|
||||
new Date(
|
||||
individual.scheduledStartDate || commonSettings.scheduledStartDate
|
||||
).getTime() +
|
||||
plan.estimatedDuration * 60000
|
||||
new Date(individual.scheduledStartDate || commonSettings.scheduledStartDate).getTime() +
|
||||
plan.estimatedDuration * 60000,
|
||||
),
|
||||
assignedTo: individual.assignedTo || commonSettings.assignedTo,
|
||||
reportedBy: "System",
|
||||
reportedBy: 'System',
|
||||
estimatedCost: 0,
|
||||
actualCost: 0,
|
||||
materials: [],
|
||||
|
|
@ -108,25 +96,20 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
notes: individual.notes || commonSettings.notes,
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
workOrders.push(workOrder);
|
||||
});
|
||||
}
|
||||
workOrders.push(workOrder)
|
||||
})
|
||||
} else {
|
||||
// Tek bir toplu iş emri oluştur
|
||||
const combinedPlanCodes = selectedPlans.map((p) => p.planCode).join(", ");
|
||||
const combinedWorkCenterIds = [
|
||||
...new Set(selectedPlans.map((p) => p.workCenterId)),
|
||||
];
|
||||
const totalDuration = selectedPlans.reduce(
|
||||
(sum, plan) => sum + plan.estimatedDuration,
|
||||
0
|
||||
);
|
||||
const combinedPlanCodes = selectedPlans.map((p) => p.planCode).join(', ')
|
||||
const combinedWorkCenterIds = [...new Set(selectedPlans.map((p) => p.workCenterId))]
|
||||
const totalDuration = selectedPlans.reduce((sum, plan) => sum + plan.estimatedDuration, 0)
|
||||
|
||||
const workOrder: Partial<PmMaintenanceWorkOrder> = {
|
||||
workOrderNumber: `WO-BULK-${new Date()
|
||||
.toISOString()
|
||||
.slice(0, 10)
|
||||
.replace(/-/g, "")}-${String(Date.now()).slice(-3)}`,
|
||||
.replace(/-/g, '')}-${String(Date.now()).slice(-3)}`,
|
||||
workCenterId: combinedWorkCenterIds[0], // Ana İş merkezleri
|
||||
orderType: WorkOrderTypeEnum.Preventive,
|
||||
priority: commonSettings.priority,
|
||||
|
|
@ -134,34 +117,33 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
description: `Toplu Bakım İş Emri - Planlar: ${combinedPlanCodes}`,
|
||||
scheduledStart: new Date(commonSettings.scheduledStartDate),
|
||||
scheduledEnd: new Date(
|
||||
new Date(commonSettings.scheduledStartDate).getTime() +
|
||||
totalDuration * 60000
|
||||
new Date(commonSettings.scheduledStartDate).getTime() + totalDuration * 60000,
|
||||
),
|
||||
reportedBy: "System",
|
||||
reportedBy: 'System',
|
||||
estimatedCost: 0,
|
||||
actualCost: 0,
|
||||
materials: [],
|
||||
activities: [],
|
||||
notes: `${commonSettings.notes}\n\nPlan Talimatları:\n${selectedPlans
|
||||
.map((p) => `${p.planCode}: ${p.instructions || "Talimat yok"}`)
|
||||
.join("\n")}`,
|
||||
.map((p) => `${p.planCode}: ${p.instructions || 'Talimat yok'}`)
|
||||
.join('\n')}`,
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
workOrders.push(workOrder);
|
||||
}
|
||||
workOrders.push(workOrder)
|
||||
}
|
||||
|
||||
onSave(workOrders);
|
||||
onSave(workOrders)
|
||||
setCommonSettings({
|
||||
scheduledStartDate: new Date().toISOString().split("T")[0],
|
||||
scheduledStartDate: new Date().toISOString().split('T')[0],
|
||||
priority: PriorityEnum.Normal,
|
||||
assignedTo: "",
|
||||
notes: "",
|
||||
assignedTo: '',
|
||||
notes: '',
|
||||
createSeparateOrders: true,
|
||||
});
|
||||
setIndividualSettings({});
|
||||
onClose();
|
||||
};
|
||||
})
|
||||
setIndividualSettings({})
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -169,17 +151,12 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
İş Emri Oluştur
|
||||
</h2>
|
||||
<h2 className="text-lg font-semibold text-gray-900">İş Emri Oluştur</h2>
|
||||
<p className="text-sm text-gray-600">
|
||||
{selectedPlans.length} plan için iş emri oluşturuluyor
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
>
|
||||
<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>
|
||||
|
|
@ -188,24 +165,16 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
<div className="p-4 space-y-4">
|
||||
{/* Selected Plans Summary */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Seçili Bakım Planları
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Seçili Bakım Planları</h3>
|
||||
<div className="bg-gray-50 rounded-lg p-3">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
{selectedPlans.map((plan) => (
|
||||
<div key={plan.id} className="flex items-center space-x-3">
|
||||
<div className="flex-1">
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
{plan.planCode}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500 ml-2">
|
||||
({plan.workCenterId})
|
||||
</span>
|
||||
<span className="text-sm font-medium text-gray-900">{plan.planCode}</span>
|
||||
<span className="text-xs text-gray-500 ml-2">({plan.workCenterId})</span>
|
||||
</div>
|
||||
<span
|
||||
className={`text-xs ${getPriorityColor(plan.priority)}`}
|
||||
>
|
||||
<span className={`text-xs ${getPriorityColor(plan.priority)}`}>
|
||||
{plan.priority}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -216,24 +185,18 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
|
||||
{/* Work Order Type Selection */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
İş Emri Türü
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">İş Emri Türü</h3>
|
||||
<div className="space-y-3">
|
||||
<label className="flex items-center space-x-3">
|
||||
<input
|
||||
type="radio"
|
||||
name="orderType"
|
||||
checked={commonSettings.createSeparateOrders}
|
||||
onChange={() =>
|
||||
handleCommonChange("createSeparateOrders", true)
|
||||
}
|
||||
onChange={() => handleCommonChange('createSeparateOrders', true)}
|
||||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"
|
||||
/>
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
Ayrı İş Emirleri
|
||||
</span>
|
||||
<span className="text-sm font-medium text-gray-900">Ayrı İş Emirleri</span>
|
||||
<p className="text-sm text-gray-600">
|
||||
Her bakım planı için ayrı bir iş emri oluşturulur
|
||||
</p>
|
||||
|
|
@ -244,15 +207,11 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
type="radio"
|
||||
name="orderType"
|
||||
checked={!commonSettings.createSeparateOrders}
|
||||
onChange={() =>
|
||||
handleCommonChange("createSeparateOrders", false)
|
||||
}
|
||||
onChange={() => handleCommonChange('createSeparateOrders', false)}
|
||||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"
|
||||
/>
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
Toplu İş Emri
|
||||
</span>
|
||||
<span className="text-sm font-medium text-gray-900">Toplu İş Emri</span>
|
||||
<p className="text-sm text-gray-600">
|
||||
Tüm seçili planlar için tek bir iş emri oluşturulur
|
||||
</p>
|
||||
|
|
@ -263,9 +222,7 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
|
||||
{/* Common Settings */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Ortak Ayarlar
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Ortak Ayarlar</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">
|
||||
|
|
@ -274,25 +231,16 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
<input
|
||||
type="date"
|
||||
value={commonSettings.scheduledStartDate}
|
||||
onChange={(e) =>
|
||||
handleCommonChange("scheduledStartDate", e.target.value)
|
||||
}
|
||||
onChange={(e) => handleCommonChange('scheduledStartDate', 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"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Öncelik
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Öncelik</label>
|
||||
<select
|
||||
value={commonSettings.priority}
|
||||
onChange={(e) =>
|
||||
handleCommonChange(
|
||||
"priority",
|
||||
e.target.value as PriorityEnum
|
||||
)
|
||||
}
|
||||
onChange={(e) => handleCommonChange('priority', e.target.value as PriorityEnum)}
|
||||
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>
|
||||
|
|
@ -302,14 +250,10 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Atanan Kişi
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Atanan Kişi</label>
|
||||
<select
|
||||
value={commonSettings.assignedTo}
|
||||
onChange={(e) =>
|
||||
handleCommonChange("assignedTo", e.target.value)
|
||||
}
|
||||
onChange={(e) => handleCommonChange('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="">Teknisyen seçin</option>
|
||||
|
|
@ -321,12 +265,10 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Notlar
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Notlar</label>
|
||||
<textarea
|
||||
value={commonSettings.notes}
|
||||
onChange={(e) => handleCommonChange("notes", e.target.value)}
|
||||
onChange={(e) => handleCommonChange('notes', e.target.value)}
|
||||
rows={1}
|
||||
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="Ek notlar ve talimatlar..."
|
||||
|
|
@ -346,12 +288,9 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
</h3>
|
||||
<div className="space-y-4">
|
||||
{selectedPlans.map((plan) => {
|
||||
const individual = individualSettings[plan.id] || {};
|
||||
const individual = individualSettings[plan.id] || {}
|
||||
return (
|
||||
<div
|
||||
key={plan.id}
|
||||
className="border border-gray-200 rounded-lg p-3"
|
||||
>
|
||||
<div key={plan.id} className="border border-gray-200 rounded-lg p-3">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h4 className="text-sm font-medium text-gray-900">
|
||||
{plan.planCode} - {plan.description}
|
||||
|
|
@ -367,13 +306,9 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
</label>
|
||||
<input
|
||||
type="date"
|
||||
value={individual.scheduledStartDate || ""}
|
||||
value={individual.scheduledStartDate || ''}
|
||||
onChange={(e) =>
|
||||
handleIndividualChange(
|
||||
plan.id,
|
||||
"scheduledStartDate",
|
||||
e.target.value
|
||||
)
|
||||
handleIndividualChange(plan.id, 'scheduledStartDate', e.target.value)
|
||||
}
|
||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
|
|
@ -383,12 +318,12 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
Öncelik
|
||||
</label>
|
||||
<select
|
||||
value={individual.priority || ""}
|
||||
value={individual.priority || ''}
|
||||
onChange={(e) =>
|
||||
handleIndividualChange(
|
||||
plan.id,
|
||||
"priority",
|
||||
e.target.value as PriorityEnum
|
||||
'priority',
|
||||
e.target.value as PriorityEnum,
|
||||
)
|
||||
}
|
||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
|
||||
|
|
@ -405,22 +340,15 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
Atanan Kişi
|
||||
</label>
|
||||
<select
|
||||
value={individual.assignedTo || ""}
|
||||
value={individual.assignedTo || ''}
|
||||
onChange={(e) =>
|
||||
handleIndividualChange(
|
||||
plan.id,
|
||||
"assignedTo",
|
||||
e.target.value
|
||||
)
|
||||
handleIndividualChange(plan.id, 'assignedTo', e.target.value)
|
||||
}
|
||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="">Teknisyen seçin</option>
|
||||
{mockEmployees.map((employee) => (
|
||||
<option
|
||||
key={employee.id}
|
||||
value={employee.fullName}
|
||||
>
|
||||
<option key={employee.id} value={employee.fullName}>
|
||||
{employee.fullName} ({employee.code})
|
||||
</option>
|
||||
))}
|
||||
|
|
@ -428,7 +356,7 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -440,13 +368,11 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
<div className="flex items-start space-x-3">
|
||||
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-yellow-800">
|
||||
Toplu İş Emri Uyarısı
|
||||
</h4>
|
||||
<h4 className="text-sm font-medium text-yellow-800">Toplu İş Emri Uyarısı</h4>
|
||||
<p className="text-sm text-yellow-700 mt-1">
|
||||
Toplu iş emri oluşturulduğunda, farklı iş merkezleri ve
|
||||
planlar tek bir iş emri altında birleştirilecektir. Bu durum
|
||||
takip ve raporlama açısından karmaşıklığa neden olabilir.
|
||||
Toplu iş emri oluşturulduğunda, farklı iş merkezleri ve planlar tek bir iş emri
|
||||
altında birleştirilecektir. Bu durum takip ve raporlama açısından karmaşıklığa
|
||||
neden olabilir.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -469,15 +395,12 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
|||
className="flex items-center space-x-2 px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<FaSave className="w-4 h-4" />
|
||||
<span>
|
||||
İş {commonSettings.createSeparateOrders ? "Emirlerini" : "Emrini"}{" "}
|
||||
Oluştur
|
||||
</span>
|
||||
<span>İş {commonSettings.createSeparateOrders ? 'Emirlerini' : 'Emrini'} Oluştur</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateWorkOrderModal;
|
||||
export default CreateWorkOrderModal
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { FaTimes, FaSave, FaUpload, FaMinus } from "react-icons/fa";
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { FaTimes, FaSave, FaUpload, FaMinus } from 'react-icons/fa'
|
||||
import {
|
||||
PmFaultNotification,
|
||||
FaultTypeEnum,
|
||||
CriticalityLevelEnum,
|
||||
NotificationStatusEnum,
|
||||
} from "../../../types/pm";
|
||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
} from '../../../types/pm'
|
||||
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
|
||||
interface EditFaultNotificationModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (notification: PmFaultNotification) => void;
|
||||
notification: PmFaultNotification | null;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (notification: PmFaultNotification) => void
|
||||
notification: PmFaultNotification | null
|
||||
}
|
||||
|
||||
const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||
|
|
@ -23,65 +23,61 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
onSave,
|
||||
notification,
|
||||
}) => {
|
||||
const [notificationData, setNotificationData] = useState<
|
||||
Partial<PmFaultNotification>
|
||||
>({});
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [uploadedImages, setUploadedImages] = useState<string[]>([]);
|
||||
const [notificationData, setNotificationData] = useState<Partial<PmFaultNotification>>({})
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
const [uploadedImages, setUploadedImages] = useState<string[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
if (notification && isOpen) {
|
||||
setNotificationData(notification);
|
||||
setUploadedImages(notification.images || []);
|
||||
setNotificationData(notification)
|
||||
setUploadedImages(notification.images || [])
|
||||
}
|
||||
}, [notification, isOpen]);
|
||||
}, [notification, isOpen])
|
||||
|
||||
if (!isOpen || !notification) return null;
|
||||
if (!isOpen || !notification) return null
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (!notificationData.workCenterId) {
|
||||
newErrors.workCenterId = "İş Merkezi seçimi gerekli";
|
||||
newErrors.workCenterId = 'İş Merkezi seçimi gerekli'
|
||||
}
|
||||
if (!notificationData.title?.trim()) {
|
||||
newErrors.title = "Başlık gerekli";
|
||||
newErrors.title = 'Başlık gerekli'
|
||||
}
|
||||
if (!notificationData.description?.trim()) {
|
||||
newErrors.description = "Açıklama gerekli";
|
||||
newErrors.description = 'Açıklama gerekli'
|
||||
}
|
||||
if (!notificationData.location?.trim()) {
|
||||
newErrors.location = "Konum gerekli";
|
||||
newErrors.location = 'Konum gerekli'
|
||||
}
|
||||
if (!notificationData.reportedBy?.trim()) {
|
||||
newErrors.reportedBy = "Bildiren kişi gerekli";
|
||||
newErrors.reportedBy = 'Bildiren kişi gerekli'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleInputChange = (
|
||||
field: keyof PmFaultNotification,
|
||||
value: string | number | boolean | undefined
|
||||
value: string | number | boolean | undefined,
|
||||
) => {
|
||||
setNotificationData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
}))
|
||||
// Clear error when user starts typing
|
||||
if (errors[field]) {
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
[field]: "",
|
||||
}));
|
||||
[field]: '',
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleWorkCenterChange = (workCenterId: string) => {
|
||||
const selectedWorkCenter = mockWorkCenters.find(
|
||||
(eq) => eq.id === workCenterId
|
||||
);
|
||||
const selectedWorkCenter = mockWorkCenters.find((eq) => eq.id === workCenterId)
|
||||
if (selectedWorkCenter) {
|
||||
setNotificationData((prev) => ({
|
||||
...prev,
|
||||
|
|
@ -89,29 +85,29 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
workCenterCode: selectedWorkCenter.code,
|
||||
workCenterName: selectedWorkCenter.name,
|
||||
location: selectedWorkCenter.location,
|
||||
}));
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleImageUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = event.target.files;
|
||||
const files = event.target.files
|
||||
if (files) {
|
||||
const imageNames = Array.from(files).map((file) => file.name);
|
||||
setUploadedImages((prev) => [...prev, ...imageNames]);
|
||||
const imageNames = Array.from(files).map((file) => file.name)
|
||||
setUploadedImages((prev) => [...prev, ...imageNames])
|
||||
setNotificationData((prev) => ({
|
||||
...prev,
|
||||
images: [...(prev.images || []), ...imageNames],
|
||||
}));
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const removeImage = (index: number) => {
|
||||
setUploadedImages((prev) => prev.filter((_, i) => i !== index));
|
||||
setUploadedImages((prev) => prev.filter((_, i) => i !== index))
|
||||
setNotificationData((prev) => ({
|
||||
...prev,
|
||||
images: prev.images?.filter((_, i) => i !== index) || [],
|
||||
}));
|
||||
};
|
||||
}))
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
if (validateForm()) {
|
||||
|
|
@ -119,25 +115,20 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
...notification,
|
||||
...notificationData,
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
onSave(updatedNotification);
|
||||
onClose();
|
||||
onSave(updatedNotification)
|
||||
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-3xl 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">
|
||||
Arıza Bildirimini Düzenle
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
||||
>
|
||||
<h2 className="text-lg font-semibold text-gray-900">Arıza Bildirimini Düzenle</h2>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -152,24 +143,20 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={notificationData.notificationCode || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("notificationCode", e.target.value)
|
||||
}
|
||||
value={notificationData.notificationCode || ''}
|
||||
onChange={(e) => handleInputChange('notificationCode', 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"
|
||||
placeholder="ARZ-2024-001"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
İş Merkezi *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||
<select
|
||||
value={notificationData.workCenterId || ""}
|
||||
value={notificationData.workCenterId || ''}
|
||||
onChange={(e) => handleWorkCenterChange(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.workCenterId ? "border-red-500" : "border-gray-300"
|
||||
errors.workCenterId ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">İş merkezi seçin</option>
|
||||
|
|
@ -180,9 +167,7 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
))}
|
||||
</select>
|
||||
{errors.workCenterId && (
|
||||
<p className="text-red-500 text-sm mt-1">
|
||||
{errors.workCenterId}
|
||||
</p>
|
||||
<p className="text-red-500 text-sm mt-1">{errors.workCenterId}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -195,16 +180,14 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={notificationData.title || ""}
|
||||
onChange={(e) => handleInputChange("title", e.target.value)}
|
||||
value={notificationData.title || ''}
|
||||
onChange={(e) => handleInputChange('title', 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.title ? "border-red-500" : "border-gray-300"
|
||||
errors.title ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Arıza başlığı"
|
||||
/>
|
||||
{errors.title && (
|
||||
<p className="text-red-500 text-sm mt-1">{errors.title}</p>
|
||||
)}
|
||||
{errors.title && <p className="text-red-500 text-sm mt-1">{errors.title}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
@ -212,20 +195,16 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
Detaylı Açıklama *
|
||||
</label>
|
||||
<textarea
|
||||
value={notificationData.description || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("description", e.target.value)
|
||||
}
|
||||
value={notificationData.description || ''}
|
||||
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||
rows={3}
|
||||
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.description ? "border-red-500" : "border-gray-300"
|
||||
errors.description ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Arızanın detaylı açıklaması, belirtiler ve gözlemler"
|
||||
/>
|
||||
{errors.description && (
|
||||
<p className="text-red-500 text-sm mt-1">
|
||||
{errors.description}
|
||||
</p>
|
||||
<p className="text-red-500 text-sm mt-1">{errors.description}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -233,21 +212,17 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
{/* Location and Reporter */}
|
||||
<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">
|
||||
Konum *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Konum *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={notificationData.location || ""}
|
||||
onChange={(e) => handleInputChange("location", e.target.value)}
|
||||
value={notificationData.location || ''}
|
||||
onChange={(e) => handleInputChange('location', 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.location ? "border-red-500" : "border-gray-300"
|
||||
errors.location ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Atölye A - Hat 1"
|
||||
/>
|
||||
{errors.location && (
|
||||
<p className="text-red-500 text-sm mt-1">{errors.location}</p>
|
||||
)}
|
||||
{errors.location && <p className="text-red-500 text-sm mt-1">{errors.location}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
@ -255,12 +230,10 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
Bildiren Kişi *
|
||||
</label>
|
||||
<select
|
||||
value={notificationData.reportedBy || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("reportedBy", e.target.value)
|
||||
}
|
||||
value={notificationData.reportedBy || ''}
|
||||
onChange={(e) => handleInputChange('reportedBy', 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.reportedBy ? "border-red-500" : "border-gray-300"
|
||||
errors.reportedBy ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">Bildiren kişi seçin</option>
|
||||
|
|
@ -279,41 +252,28 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
{/* Status and 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">
|
||||
Durum
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
||||
<select
|
||||
value={notificationData.status || NotificationStatusEnum.Open}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"status",
|
||||
e.target.value as NotificationStatusEnum
|
||||
)
|
||||
handleInputChange('status', e.target.value as NotificationStatusEnum)
|
||||
}
|
||||
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={NotificationStatusEnum.Open}>Açık</option>
|
||||
<option value={NotificationStatusEnum.Assigned}>Atandı</option>
|
||||
<option value={NotificationStatusEnum.InProgress}>
|
||||
Devam Ediyor
|
||||
</option>
|
||||
<option value={NotificationStatusEnum.InProgress}>Devam Ediyor</option>
|
||||
<option value={NotificationStatusEnum.Resolved}>Çözüldü</option>
|
||||
<option value={NotificationStatusEnum.Closed}>Kapatıldı</option>
|
||||
<option value={NotificationStatusEnum.Rejected}>
|
||||
Reddedildi
|
||||
</option>
|
||||
<option value={NotificationStatusEnum.Rejected}>Reddedildi</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Atanan Kişi
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Atanan Kişi</label>
|
||||
<select
|
||||
value={notificationData.assignedTo || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("assignedTo", e.target.value)
|
||||
}
|
||||
value={notificationData.assignedTo || ''}
|
||||
onChange={(e) => handleInputChange('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="">Atama yapın</option>
|
||||
|
|
@ -328,17 +288,10 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Arıza Türü
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Arıza Türü</label>
|
||||
<select
|
||||
value={notificationData.faultType || FaultTypeEnum.Mechanical}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"faultType",
|
||||
e.target.value as FaultTypeEnum
|
||||
)
|
||||
}
|
||||
onChange={(e) => handleInputChange('faultType', e.target.value as FaultTypeEnum)}
|
||||
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={FaultTypeEnum.Mechanical}>Mekanik</option>
|
||||
|
|
@ -353,14 +306,10 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Öncelik
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Öncelik</label>
|
||||
<select
|
||||
value={notificationData.priority || PriorityEnum.Normal}
|
||||
onChange={(e) =>
|
||||
handleInputChange("priority", e.target.value as PriorityEnum)
|
||||
}
|
||||
onChange={(e) => handleInputChange('priority', e.target.value as PriorityEnum)}
|
||||
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>
|
||||
|
|
@ -371,16 +320,11 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Kritiklik
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Kritiklik</label>
|
||||
<select
|
||||
value={notificationData.severity || CriticalityLevelEnum.Medium}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"severity",
|
||||
e.target.value as CriticalityLevelEnum
|
||||
)
|
||||
handleInputChange('severity', e.target.value as CriticalityLevelEnum)
|
||||
}
|
||||
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"
|
||||
>
|
||||
|
|
@ -400,12 +344,9 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={notificationData.estimatedRepairTime || ""}
|
||||
value={notificationData.estimatedRepairTime || ''}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"estimatedRepairTime",
|
||||
parseInt(e.target.value) || undefined
|
||||
)
|
||||
handleInputChange('estimatedRepairTime', parseInt(e.target.value) || undefined)
|
||||
}
|
||||
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="120"
|
||||
|
|
@ -419,12 +360,9 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={notificationData.actualRepairTime || ""}
|
||||
value={notificationData.actualRepairTime || ''}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"actualRepairTime",
|
||||
parseInt(e.target.value) || undefined
|
||||
)
|
||||
handleInputChange('actualRepairTime', parseInt(e.target.value) || undefined)
|
||||
}
|
||||
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="150"
|
||||
|
|
@ -435,13 +373,11 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
|
||||
{/* Work Order ID */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
İş Emri ID
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">İş Emri ID</label>
|
||||
<input
|
||||
type="text"
|
||||
value={notificationData.workOrderId || ""}
|
||||
onChange={(e) => handleInputChange("workOrderId", e.target.value)}
|
||||
value={notificationData.workOrderId || ''}
|
||||
onChange={(e) => handleInputChange('workOrderId', 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"
|
||||
placeholder="WO-2024-001"
|
||||
/>
|
||||
|
|
@ -449,14 +385,10 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
|
||||
{/* Resolution Notes */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Çözüm Notları
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Çözüm Notları</label>
|
||||
<textarea
|
||||
value={notificationData.resolutionNotes || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("resolutionNotes", e.target.value)
|
||||
}
|
||||
value={notificationData.resolutionNotes || ''}
|
||||
onChange={(e) => handleInputChange('resolutionNotes', e.target.value)}
|
||||
rows={3}
|
||||
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="Çözüm detayları ve yapılan işlemler"
|
||||
|
|
@ -469,9 +401,7 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
<input
|
||||
type="checkbox"
|
||||
checked={notificationData.followUpRequired || false}
|
||||
onChange={(e) =>
|
||||
handleInputChange("followUpRequired", e.target.checked)
|
||||
}
|
||||
onChange={(e) => handleInputChange('followUpRequired', e.target.checked)}
|
||||
className="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
|
||||
/>
|
||||
<span className="ml-2 text-sm text-gray-700">Takip gerekli</span>
|
||||
|
|
@ -480,9 +410,7 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
|
||||
{/* Image Upload */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Fotoğraflar
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Fotoğraflar</label>
|
||||
<div className="border-2 border-dashed border-gray-300 rounded-lg p-4">
|
||||
<div className="text-center">
|
||||
<FaUpload className="mx-auto h-10 w-10 text-gray-400" />
|
||||
|
|
@ -510,9 +438,7 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
|
||||
{uploadedImages.length > 0 && (
|
||||
<div className="mt-4">
|
||||
<h4 className="text-sm font-medium text-gray-700 mb-2">
|
||||
Mevcut Fotoğraflar:
|
||||
</h4>
|
||||
<h4 className="text-sm font-medium text-gray-700 mb-2">Mevcut Fotoğraflar:</h4>
|
||||
<div className="space-y-2">
|
||||
{uploadedImages.map((image, index) => (
|
||||
<div
|
||||
|
|
@ -553,7 +479,7 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default EditFaultNotificationModal;
|
||||
export default EditFaultNotificationModal
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||
import {
|
||||
PmMaintenancePlan,
|
||||
MaintenancePlanTypeEnum,
|
||||
FrequencyUnitEnum,
|
||||
PmPlanMaterial,
|
||||
} from "../../../types/pm";
|
||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
||||
import { mockMaterials } from "../../../mocks/mockMaterials";
|
||||
import { mockUnits } from "../../../mocks/mockUnits";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
} from '../../../types/pm'
|
||||
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||
import { mockMaterials } from '../../../mocks/mockMaterials'
|
||||
import { mockUnits } from '../../../mocks/mockUnits'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
|
||||
interface EditMaintenancePlanModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (plan: PmMaintenancePlan) => void;
|
||||
plan: PmMaintenancePlan | null;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (plan: PmMaintenancePlan) => void
|
||||
plan: PmMaintenancePlan | null
|
||||
}
|
||||
|
||||
const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||
|
|
@ -24,82 +24,67 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
onSave,
|
||||
plan,
|
||||
}) => {
|
||||
const [planData, setPlanData] = useState<Partial<PmMaintenancePlan>>({});
|
||||
const [requiredSkills, setRequiredSkills] = useState<string[]>([]);
|
||||
const [requiredMaterials, setRequiredMaterials] = useState<PmPlanMaterial[]>(
|
||||
[]
|
||||
);
|
||||
const [newSkill, setNewSkill] = useState("");
|
||||
const [planData, setPlanData] = useState<Partial<PmMaintenancePlan>>({})
|
||||
const [requiredSkills, setRequiredSkills] = useState<string[]>([])
|
||||
const [requiredMaterials, setRequiredMaterials] = useState<PmPlanMaterial[]>([])
|
||||
const [newSkill, setNewSkill] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
if (plan) {
|
||||
setPlanData({
|
||||
...plan,
|
||||
nextDue: plan.nextDue ? new Date(plan.nextDue) : new Date(),
|
||||
});
|
||||
setRequiredSkills(plan.requiredSkills || []);
|
||||
setRequiredMaterials(plan.requiredMaterials || []);
|
||||
})
|
||||
setRequiredSkills(plan.requiredSkills || [])
|
||||
setRequiredMaterials(plan.requiredMaterials || [])
|
||||
}
|
||||
}, [plan]);
|
||||
}, [plan])
|
||||
|
||||
if (!isOpen || !plan) return null;
|
||||
if (!isOpen || !plan) return null
|
||||
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<
|
||||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
||||
>
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
const { name, value, type } = e.target;
|
||||
const { name, value, type } = e.target
|
||||
setPlanData((prev) => ({
|
||||
...prev,
|
||||
[name]:
|
||||
type === "date"
|
||||
? new Date(value)
|
||||
: type === "number"
|
||||
? Number(value)
|
||||
: value,
|
||||
}));
|
||||
};
|
||||
[name]: type === 'date' ? new Date(value) : type === 'number' ? Number(value) : value,
|
||||
}))
|
||||
}
|
||||
|
||||
const addSkill = () => {
|
||||
if (newSkill.trim() && !requiredSkills.includes(newSkill.trim())) {
|
||||
setRequiredSkills([...requiredSkills, newSkill.trim()]);
|
||||
setNewSkill("");
|
||||
setRequiredSkills([...requiredSkills, newSkill.trim()])
|
||||
setNewSkill('')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const removeSkill = (skillToRemove: string) => {
|
||||
setRequiredSkills(
|
||||
requiredSkills.filter((skill) => skill !== skillToRemove)
|
||||
);
|
||||
};
|
||||
setRequiredSkills(requiredSkills.filter((skill) => skill !== skillToRemove))
|
||||
}
|
||||
|
||||
const addMaterial = () => {
|
||||
const newMaterial: PmPlanMaterial = {
|
||||
id: `MAT${Date.now()}`,
|
||||
planId: plan.id,
|
||||
materialId: "",
|
||||
materialId: '',
|
||||
quantity: 1,
|
||||
unitId: "",
|
||||
unitId: '',
|
||||
isRequired: true,
|
||||
};
|
||||
setRequiredMaterials([...requiredMaterials, newMaterial]);
|
||||
};
|
||||
}
|
||||
setRequiredMaterials([...requiredMaterials, newMaterial])
|
||||
}
|
||||
|
||||
const removeMaterial = (index: number) => {
|
||||
setRequiredMaterials(requiredMaterials.filter((_, i) => i !== index));
|
||||
};
|
||||
setRequiredMaterials(requiredMaterials.filter((_, i) => i !== index))
|
||||
}
|
||||
|
||||
const updateMaterial = (
|
||||
index: number,
|
||||
field: string,
|
||||
value: string | number | boolean
|
||||
) => {
|
||||
const updateMaterial = (index: number, field: string, value: string | number | boolean) => {
|
||||
const updated = requiredMaterials.map((material, i) =>
|
||||
i === index ? { ...material, [field]: value } : material
|
||||
);
|
||||
setRequiredMaterials(updated);
|
||||
};
|
||||
i === index ? { ...material, [field]: value } : material,
|
||||
)
|
||||
setRequiredMaterials(updated)
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
const updatedPlan: PmMaintenancePlan = {
|
||||
|
|
@ -108,11 +93,11 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
requiredSkills,
|
||||
requiredMaterials,
|
||||
lastModificationTime: new Date(),
|
||||
} as PmMaintenancePlan;
|
||||
} as PmMaintenancePlan
|
||||
|
||||
onSave(updatedPlan);
|
||||
onClose();
|
||||
};
|
||||
onSave(updatedPlan)
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -122,10 +107,7 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
Bakım Planı Düzenle - {plan.planCode}
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
>
|
||||
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -134,30 +116,24 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
<div className="p-4 space-y-4">
|
||||
{/* Basic Information */}
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||
Temel Bilgiler
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">Temel Bilgiler</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-2">
|
||||
Plan Kodu *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Plan Kodu *</label>
|
||||
<input
|
||||
type="text"
|
||||
name="planCode"
|
||||
value={planData.planCode || ""}
|
||||
value={planData.planCode || ''}
|
||||
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"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
İş Merkezi *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||
<select
|
||||
name="workCenterId"
|
||||
value={planData.workCenterId || ""}
|
||||
value={planData.workCenterId || ''}
|
||||
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"
|
||||
required
|
||||
|
|
@ -171,35 +147,21 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Plan Tipi *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Plan Tipi *</label>
|
||||
<select
|
||||
name="planType"
|
||||
value={
|
||||
planData.planType || MaintenancePlanTypeEnum.Preventive
|
||||
}
|
||||
value={planData.planType || MaintenancePlanTypeEnum.Preventive}
|
||||
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>
|
||||
<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-2">
|
||||
Öncelik
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Öncelik</label>
|
||||
<select
|
||||
name="priority"
|
||||
value={planData.priority || PriorityEnum.Normal}
|
||||
|
|
@ -213,12 +175,10 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
</select>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Açıklama *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama *</label>
|
||||
<textarea
|
||||
name="description"
|
||||
value={planData.description || ""}
|
||||
value={planData.description || ''}
|
||||
onChange={handleInputChange}
|
||||
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"
|
||||
|
|
@ -230,14 +190,10 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
|
||||
{/* Frequency Settings */}
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||
Sıklık Ayarları
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">Sıklık Ayarları</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Sıklık *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Sıklık *</label>
|
||||
<input
|
||||
type="number"
|
||||
name="frequency"
|
||||
|
|
@ -287,11 +243,7 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
<input
|
||||
type="date"
|
||||
name="nextDue"
|
||||
value={
|
||||
planData.nextDue
|
||||
? planData.nextDue.toISOString().split("T")[0]
|
||||
: ""
|
||||
}
|
||||
value={planData.nextDue ? planData.nextDue.toISOString().split('T')[0] : ''}
|
||||
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"
|
||||
/>
|
||||
|
|
@ -310,9 +262,7 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
<span className="text-sm font-medium text-gray-700">
|
||||
Plan Aktif
|
||||
</span>
|
||||
<span className="text-sm font-medium text-gray-700">Plan Aktif</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -320,12 +270,10 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
|
||||
{/* Instructions */}
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||
Bakım Talimatları
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">Bakım Talimatları</h3>
|
||||
<textarea
|
||||
name="instructions"
|
||||
value={planData.instructions || ""}
|
||||
value={planData.instructions || ''}
|
||||
onChange={handleInputChange}
|
||||
rows={3}
|
||||
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"
|
||||
|
|
@ -336,9 +284,7 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
{/* Required Skills */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-medium text-gray-900">
|
||||
Gerekli Yetenekler
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900">Gerekli Yetenekler</h3>
|
||||
<div className="flex space-x-2">
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -346,7 +292,7 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
onChange={(e) => setNewSkill(e.target.value)}
|
||||
placeholder="Yetenek ekle"
|
||||
className="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()}
|
||||
onKeyPress={(e) => e.key === 'Enter' && addSkill()}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -379,9 +325,7 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
{/* Required Materials */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-medium text-gray-900">
|
||||
Gerekli Malzemeler
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900">Gerekli Malzemeler</h3>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addMaterial}
|
||||
|
|
@ -393,23 +337,14 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
</div>
|
||||
<div className="space-y-3">
|
||||
{requiredMaterials.map((material, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg"
|
||||
>
|
||||
<div key={index} className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg">
|
||||
<select
|
||||
value={material.materialId}
|
||||
onChange={(e) => {
|
||||
const selectedMaterial = mockMaterials.find(
|
||||
(m) => m.id === e.target.value
|
||||
);
|
||||
updateMaterial(index, "materialId", e.target.value);
|
||||
const selectedMaterial = mockMaterials.find((m) => m.id === e.target.value)
|
||||
updateMaterial(index, 'materialId', e.target.value)
|
||||
if (selectedMaterial) {
|
||||
updateMaterial(
|
||||
index,
|
||||
"unitId",
|
||||
selectedMaterial.baseUnitId
|
||||
);
|
||||
updateMaterial(index, 'unitId', selectedMaterial.baseUnitId)
|
||||
}
|
||||
}}
|
||||
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"
|
||||
|
|
@ -424,18 +359,14 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
<input
|
||||
type="number"
|
||||
value={material.quantity}
|
||||
onChange={(e) =>
|
||||
updateMaterial(index, "quantity", Number(e.target.value))
|
||||
}
|
||||
onChange={(e) => updateMaterial(index, 'quantity', Number(e.target.value))}
|
||||
placeholder="Miktar"
|
||||
min="1"
|
||||
className="w-20 px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
<select
|
||||
value={material.unitId}
|
||||
onChange={(e) =>
|
||||
updateMaterial(index, "unitId", e.target.value)
|
||||
}
|
||||
onChange={(e) => updateMaterial(index, 'unitId', e.target.value)}
|
||||
className="w-24 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="">Birim</option>
|
||||
|
|
@ -448,29 +379,25 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
{material.materialId &&
|
||||
(() => {
|
||||
const selectedMaterial = mockMaterials.find(
|
||||
(m) => m.id === material.materialId
|
||||
);
|
||||
const baseUnit = selectedMaterial?.baseUnit;
|
||||
const existsInList = mockUnits.some(
|
||||
(unit) => unit.id === baseUnit?.id
|
||||
);
|
||||
(m) => m.id === material.materialId,
|
||||
)
|
||||
const baseUnit = selectedMaterial?.baseUnit
|
||||
const existsInList = mockUnits.some((unit) => unit.id === baseUnit?.id)
|
||||
if (baseUnit && !existsInList) {
|
||||
return (
|
||||
<option key={baseUnit.id} value={baseUnit.id}>
|
||||
{baseUnit.code}
|
||||
</option>
|
||||
);
|
||||
)
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
})()}
|
||||
</select>
|
||||
<label className="flex items-center space-x-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={material.isRequired}
|
||||
onChange={(e) =>
|
||||
updateMaterial(index, "isRequired", e.target.checked)
|
||||
}
|
||||
onChange={(e) => updateMaterial(index, 'isRequired', e.target.checked)}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">Zorunlu</span>
|
||||
|
|
@ -486,8 +413,8 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
))}
|
||||
{requiredMaterials.length === 0 && (
|
||||
<p className="text-gray-500 text-center py-4">
|
||||
Henüz malzeme eklenmedi. "Malzeme Ekle" butonunu kullanarak
|
||||
malzeme ekleyebilirsiniz.
|
||||
Henüz malzeme eklenmedi. "Malzeme Ekle" butonunu kullanarak malzeme
|
||||
ekleyebilirsiniz.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -512,7 +439,7 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default EditMaintenancePlanModal;
|
||||
export default EditMaintenancePlanModal
|
||||
|
|
|
|||
|
|
@ -1,164 +1,143 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
||||
import MultiSelectEmployee from "../../../components/common/MultiSelectEmployee";
|
||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
||||
import { Team, TeamMember, TeamRoleEnum } from "../../../types/common";
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||
import MultiSelectEmployee from '../../../components/common/MultiSelectEmployee'
|
||||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||
import { Team, TeamMember, TeamRoleEnum } from '../../../types/common'
|
||||
|
||||
interface EditTeamModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (team: Team) => void;
|
||||
team: Team | null;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (team: Team) => void
|
||||
team: Team | null
|
||||
}
|
||||
|
||||
const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSave,
|
||||
team,
|
||||
}) => {
|
||||
const EditTeamModal: React.FC<EditTeamModalProps> = ({ isOpen, onClose, onSave, team }) => {
|
||||
const [teamData, setTeamData] = useState<Partial<Team>>({
|
||||
code: "",
|
||||
name: "",
|
||||
description: "",
|
||||
code: '',
|
||||
name: '',
|
||||
description: '',
|
||||
isActive: true,
|
||||
specializations: [],
|
||||
members: [],
|
||||
});
|
||||
})
|
||||
|
||||
const [selectedEmployees, setSelectedEmployees] = useState<string[]>([]);
|
||||
const [newSpecialization, setNewSpecialization] = useState("");
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [selectedEmployees, setSelectedEmployees] = useState<string[]>([])
|
||||
const [newSpecialization, setNewSpecialization] = useState('')
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
|
||||
useEffect(() => {
|
||||
if (team && isOpen) {
|
||||
setTeamData(team);
|
||||
setTeamData(team)
|
||||
const employeeNames = team.members
|
||||
.filter((m) => m.isActive)
|
||||
.map((m) => m.employee?.firstName + " " + m.employee?.lastName || "");
|
||||
setSelectedEmployees(employeeNames);
|
||||
.map((m) => m.employee?.firstName + ' ' + m.employee?.lastName || '')
|
||||
setSelectedEmployees(employeeNames)
|
||||
}
|
||||
}, [team, isOpen]);
|
||||
}, [team, isOpen])
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (!teamData.code?.trim()) {
|
||||
newErrors.teamCode = "Ekip kodu gerekli";
|
||||
newErrors.teamCode = 'Ekip kodu gerekli'
|
||||
}
|
||||
if (!teamData.name?.trim()) {
|
||||
newErrors.teamName = "Ekip adı gerekli";
|
||||
newErrors.teamName = 'Ekip adı gerekli'
|
||||
}
|
||||
if (!teamData.description?.trim()) {
|
||||
newErrors.description = "Açıklama gerekli";
|
||||
newErrors.description = 'Açıklama gerekli'
|
||||
}
|
||||
if (!teamData.managerId) {
|
||||
newErrors.leaderId = "Ekip lideri seçilmeli";
|
||||
newErrors.leaderId = 'Ekip lideri seçilmeli'
|
||||
}
|
||||
if (selectedEmployees.length === 0) {
|
||||
newErrors.members = "En az bir ekip üyesi seçilmeli";
|
||||
newErrors.members = 'En az bir ekip üyesi seçilmeli'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleInputChange = (
|
||||
field: keyof Team,
|
||||
value: string | boolean | string[]
|
||||
) => {
|
||||
const handleInputChange = (field: keyof Team, value: string | boolean | string[]) => {
|
||||
setTeamData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
}))
|
||||
// Clear error when user starts typing
|
||||
if (errors[field]) {
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
[field]: "",
|
||||
}));
|
||||
[field]: '',
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const addSpecialization = () => {
|
||||
if (
|
||||
newSpecialization.trim() &&
|
||||
!teamData.specializations?.includes(newSpecialization.trim())
|
||||
) {
|
||||
if (newSpecialization.trim() && !teamData.specializations?.includes(newSpecialization.trim())) {
|
||||
setTeamData((prev) => ({
|
||||
...prev,
|
||||
specializations: [
|
||||
...(prev.specializations || []),
|
||||
newSpecialization.trim(),
|
||||
],
|
||||
}));
|
||||
setNewSpecialization("");
|
||||
specializations: [...(prev.specializations || []), newSpecialization.trim()],
|
||||
}))
|
||||
setNewSpecialization('')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const removeSpecialization = (index: number) => {
|
||||
setTeamData((prev) => ({
|
||||
...prev,
|
||||
specializations:
|
||||
prev.specializations?.filter((_, i) => i !== index) || [],
|
||||
}));
|
||||
};
|
||||
specializations: prev.specializations?.filter((_, i) => i !== index) || [],
|
||||
}))
|
||||
}
|
||||
|
||||
const handleEmployeeSelection = (employees: string[]) => {
|
||||
setSelectedEmployees(employees);
|
||||
setSelectedEmployees(employees)
|
||||
if (errors.members) {
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
members: "",
|
||||
}));
|
||||
members: '',
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleMemberRoleChange = (employeeName: string, role: TeamRoleEnum) => {
|
||||
const employee = mockEmployees.find((emp) => emp.fullName === employeeName);
|
||||
const employee = mockEmployees.find((emp) => emp.fullName === employeeName)
|
||||
if (role === TeamRoleEnum.Lead && employee) {
|
||||
handleInputChange("managerId", employee.id);
|
||||
handleInputChange('managerId', employee.id)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
if (validateForm()) {
|
||||
const members: TeamMember[] = selectedEmployees.map(
|
||||
(employeeName, index) => {
|
||||
const employee = mockEmployees.find(
|
||||
(emp) => emp.fullName === employeeName
|
||||
);
|
||||
const existingMember = team?.members.find(
|
||||
(m) =>
|
||||
m.employee?.firstName + " " + m.employee?.lastName ===
|
||||
employeeName
|
||||
);
|
||||
const isLeader = teamData.managerId === employee?.id;
|
||||
const members: TeamMember[] = selectedEmployees.map((employeeName, index) => {
|
||||
const employee = mockEmployees.find((emp) => emp.fullName === employeeName)
|
||||
const existingMember = team?.members.find(
|
||||
(m) => m.employee?.firstName + ' ' + m.employee?.lastName === employeeName,
|
||||
)
|
||||
const isLeader = teamData.managerId === employee?.id
|
||||
|
||||
return {
|
||||
id: existingMember?.id || `TM${Date.now()}-${index}`,
|
||||
teamId: team?.id || "",
|
||||
employeeId: employee?.id || "",
|
||||
employee: mockEmployees.find((emp) => emp.id === employee?.id),
|
||||
role: isLeader ? TeamRoleEnum.Lead : TeamRoleEnum.Member,
|
||||
joinDate: existingMember?.joinDate || new Date(),
|
||||
isActive: true,
|
||||
};
|
||||
return {
|
||||
id: existingMember?.id || `TM${Date.now()}-${index}`,
|
||||
teamId: team?.id || '',
|
||||
employeeId: employee?.id || '',
|
||||
employee: mockEmployees.find((emp) => emp.id === employee?.id),
|
||||
role: isLeader ? TeamRoleEnum.Lead : TeamRoleEnum.Member,
|
||||
joinDate: existingMember?.joinDate || new Date(),
|
||||
isActive: true,
|
||||
}
|
||||
);
|
||||
})
|
||||
|
||||
const updatedTeam: Team = {
|
||||
...team!,
|
||||
...teamData,
|
||||
managerId: teamData.managerId || "",
|
||||
managerId: teamData.managerId || '',
|
||||
members,
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
onSave(updatedTeam);
|
||||
onClose();
|
||||
onSave(updatedTeam)
|
||||
onClose()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
isOpen &&
|
||||
|
|
@ -181,91 +160,69 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
|||
{/* Basic Info */}
|
||||
<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">
|
||||
Ekip Kodu *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Ekip Kodu *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={teamData.code || ""}
|
||||
onChange={(e) => handleInputChange("code", e.target.value)}
|
||||
value={teamData.code || ''}
|
||||
onChange={(e) => handleInputChange('code', 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.code ? "border-red-500" : "border-gray-300"
|
||||
errors.code ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="MEC-001"
|
||||
/>
|
||||
{errors.code && (
|
||||
<p className="text-red-500 text-sm mt-1">{errors.code}</p>
|
||||
)}
|
||||
{errors.code && <p className="text-red-500 text-sm mt-1">{errors.code}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Ekip Adı *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Ekip Adı *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={teamData.name || ""}
|
||||
onChange={(e) => handleInputChange("name", e.target.value)}
|
||||
value={teamData.name || ''}
|
||||
onChange={(e) => handleInputChange('name', 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.name ? "border-red-500" : "border-gray-300"
|
||||
errors.name ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Mekanik Bakım Ekibi"
|
||||
/>
|
||||
{errors.name && (
|
||||
<p className="text-red-500 text-sm mt-1">{errors.name}</p>
|
||||
)}
|
||||
{errors.name && <p className="text-red-500 text-sm mt-1">{errors.name}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Açıklama *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Açıklama *</label>
|
||||
<textarea
|
||||
value={teamData.description || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("description", e.target.value)
|
||||
}
|
||||
value={teamData.description || ''}
|
||||
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||
rows={2}
|
||||
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.description ? "border-red-500" : "border-gray-300"
|
||||
errors.description ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Ekip açıklaması ve sorumlulukları"
|
||||
/>
|
||||
{errors.description && (
|
||||
<p className="text-red-500 text-sm mt-1">
|
||||
{errors.description}
|
||||
</p>
|
||||
<p className="text-red-500 text-sm mt-1">{errors.description}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Team Members */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Ekip Üyeleri *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Ekip Üyeleri *</label>
|
||||
<MultiSelectEmployee
|
||||
selectedEmployees={selectedEmployees}
|
||||
onChange={handleEmployeeSelection}
|
||||
placeholder="Ekip üyelerini seçin"
|
||||
className={errors.members ? "border-red-500" : ""}
|
||||
className={errors.members ? 'border-red-500' : ''}
|
||||
/>
|
||||
{errors.members && (
|
||||
<p className="text-red-500 text-sm mt-1">{errors.members}</p>
|
||||
)}
|
||||
{errors.members && <p className="text-red-500 text-sm mt-1">{errors.members}</p>}
|
||||
|
||||
{/* Selected Members Display */}
|
||||
{selectedEmployees.length > 0 && (
|
||||
<div className="mt-3 p-3 bg-gray-50 rounded-lg">
|
||||
<h4 className="text-sm font-medium text-gray-700 mb-2">
|
||||
Seçili Ekip Üyeleri:
|
||||
</h4>
|
||||
<h4 className="text-sm font-medium text-gray-700 mb-2">Seçili Ekip Üyeleri:</h4>
|
||||
<div className="space-y-2">
|
||||
{selectedEmployees.map((employeeName, index) => {
|
||||
const employee = mockEmployees.find(
|
||||
(emp) => emp.fullName === employeeName
|
||||
);
|
||||
const employee = mockEmployees.find((emp) => emp.fullName === employeeName)
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
|
|
@ -279,12 +236,8 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
|||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{employeeName}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
{employee?.jobPosition?.name}
|
||||
</p>
|
||||
<p className="text-sm font-medium text-gray-900">{employeeName}</p>
|
||||
<p className="text-sm text-gray-600">{employee?.jobPosition?.name}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
|
|
@ -295,22 +248,17 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
|||
: TeamRoleEnum.Member
|
||||
}
|
||||
onChange={(e) =>
|
||||
handleMemberRoleChange(
|
||||
employeeName,
|
||||
e.target.value as TeamRoleEnum
|
||||
)
|
||||
handleMemberRoleChange(employeeName, e.target.value as TeamRoleEnum)
|
||||
}
|
||||
className="text-xs px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<option value={TeamRoleEnum.Member}>Üye</option>
|
||||
<option value={TeamRoleEnum.Lead}>Lider</option>
|
||||
<option value={TeamRoleEnum.Specialist}>
|
||||
Uzman
|
||||
</option>
|
||||
<option value={TeamRoleEnum.Specialist}>Uzman</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -331,9 +279,9 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
|||
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"
|
||||
placeholder="Uzmanlık alanı ekle"
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
addSpecialization();
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
addSpecialization()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
|
@ -345,26 +293,25 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
|||
<FaPlus className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
{teamData.specializations &&
|
||||
teamData.specializations.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{teamData.specializations.map((spec, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="inline-flex items-center px-2 py-1 rounded-full text-xs bg-blue-100 text-blue-800"
|
||||
{teamData.specializations && teamData.specializations.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{teamData.specializations.map((spec, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="inline-flex items-center px-2 py-1 rounded-full text-xs bg-blue-100 text-blue-800"
|
||||
>
|
||||
{spec}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeSpecialization(index)}
|
||||
className="ml-2 text-blue-600 hover:text-blue-800"
|
||||
>
|
||||
{spec}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeSpecialization(index)}
|
||||
className="ml-2 text-blue-600 hover:text-blue-800"
|
||||
>
|
||||
<FaMinus className="w-3 h-3" />
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<FaMinus className="w-3 h-3" />
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -374,9 +321,7 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
|||
<input
|
||||
type="checkbox"
|
||||
checked={teamData.isActive || false}
|
||||
onChange={(e) =>
|
||||
handleInputChange("isActive", e.target.checked)
|
||||
}
|
||||
onChange={(e) => handleInputChange('isActive', e.target.checked)}
|
||||
className="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
|
||||
/>
|
||||
<span className="ml-2 text-sm text-gray-700">Ekip aktif</span>
|
||||
|
|
@ -403,7 +348,7 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default EditTeamModal;
|
||||
export default EditTeamModal
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||
import {
|
||||
PmWorkCenter,
|
||||
WorkCenterStatusEnum,
|
||||
CriticalityLevelEnum,
|
||||
PmWorkCenterSpecification,
|
||||
} from "../../../types/pm";
|
||||
import { mockDepartments } from "../../../mocks/mockDepartments";
|
||||
} from '../../../types/pm'
|
||||
import { mockDepartments } from '../../../mocks/mockDepartments'
|
||||
|
||||
interface EditWorkCenterModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (workCenter: PmWorkCenter) => void;
|
||||
workCenter: PmWorkCenter;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (workCenter: PmWorkCenter) => void
|
||||
workCenter: PmWorkCenter
|
||||
}
|
||||
|
||||
const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||
|
|
@ -21,61 +21,55 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
onSave,
|
||||
workCenter,
|
||||
}) => {
|
||||
const [formData, setFormData] = useState<PmWorkCenter>(workCenter);
|
||||
const [specifications, setSpecifications] = useState<
|
||||
PmWorkCenterSpecification[]
|
||||
>(workCenter.specifications || []);
|
||||
const [formData, setFormData] = useState<PmWorkCenter>(workCenter)
|
||||
const [specifications, setSpecifications] = useState<PmWorkCenterSpecification[]>(
|
||||
workCenter.specifications || [],
|
||||
)
|
||||
|
||||
if (!isOpen || !workCenter) return null;
|
||||
if (!isOpen || !workCenter) return null
|
||||
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<
|
||||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
||||
>
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
const { name, value, type } = e.target;
|
||||
const { name, value, type } = e.target
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: type === "date" ? new Date(value) : value,
|
||||
}));
|
||||
};
|
||||
[name]: type === 'date' ? new Date(value) : value,
|
||||
}))
|
||||
}
|
||||
|
||||
const addSpecification = () => {
|
||||
const newSpec: PmWorkCenterSpecification = {
|
||||
id: `SPEC${Date.now()}`,
|
||||
workCenterId: workCenter.id,
|
||||
specificationName: "",
|
||||
specificationValue: "",
|
||||
unit: "",
|
||||
specificationName: '',
|
||||
specificationValue: '',
|
||||
unit: '',
|
||||
isRequired: false,
|
||||
};
|
||||
setSpecifications([...specifications, newSpec]);
|
||||
};
|
||||
}
|
||||
setSpecifications([...specifications, newSpec])
|
||||
}
|
||||
|
||||
const removeSpecification = (index: number) => {
|
||||
setSpecifications(specifications.filter((_, i) => i !== index));
|
||||
};
|
||||
setSpecifications(specifications.filter((_, i) => i !== index))
|
||||
}
|
||||
|
||||
const updateSpecification = (
|
||||
index: number,
|
||||
field: string,
|
||||
value: string | boolean
|
||||
) => {
|
||||
const updateSpecification = (index: number, field: string, value: string | boolean) => {
|
||||
const updated = specifications.map((spec, i) =>
|
||||
i === index ? { ...spec, [field]: value } : spec
|
||||
);
|
||||
setSpecifications(updated);
|
||||
};
|
||||
i === index ? { ...spec, [field]: value } : spec,
|
||||
)
|
||||
setSpecifications(updated)
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
const updatedWorkCenter = {
|
||||
...formData,
|
||||
specifications,
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
onSave(updatedWorkCenter);
|
||||
onClose();
|
||||
};
|
||||
}
|
||||
onSave(updatedWorkCenter)
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -85,10 +79,7 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
İş Merkezi Düzenle - {workCenter.code}
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
>
|
||||
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -97,9 +88,7 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
<div className="p-4 space-y-4">
|
||||
{/* Basic Information */}
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||
Temel Bilgiler
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">Temel Bilgiler</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-2">
|
||||
|
|
@ -130,12 +119,10 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Açıklama
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama</label>
|
||||
<textarea
|
||||
name="description"
|
||||
value={formData.description || ""}
|
||||
value={formData.description || ''}
|
||||
onChange={handleInputChange}
|
||||
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"
|
||||
|
|
@ -145,17 +132,13 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||
Teknik Özellikler
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">Teknik Özellikler</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-2">
|
||||
Makine Türü
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Makine Türü</label>
|
||||
<select
|
||||
name="machineType"
|
||||
value={formData.machineType || "Manual"}
|
||||
value={formData.machineType || 'Manual'}
|
||||
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"
|
||||
>
|
||||
|
|
@ -166,9 +149,7 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Departman
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Departman</label>
|
||||
|
||||
<select
|
||||
value={formData.departmentId}
|
||||
|
|
@ -215,26 +196,22 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
<div>
|
||||
<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">
|
||||
Üretici
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Üretici</label>
|
||||
<input
|
||||
type="text"
|
||||
name="manufacturer"
|
||||
value={formData.manufacturer || ""}
|
||||
value={formData.manufacturer || ''}
|
||||
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"
|
||||
placeholder="örn: HAAS Automation"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Model
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Model</label>
|
||||
<input
|
||||
type="text"
|
||||
name="model"
|
||||
value={formData.model || ""}
|
||||
value={formData.model || ''}
|
||||
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"
|
||||
placeholder="örn: ST-30"
|
||||
|
|
@ -247,7 +224,7 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
<input
|
||||
type="text"
|
||||
name="serialNumber"
|
||||
value={formData.serialNumber || ""}
|
||||
value={formData.serialNumber || ''}
|
||||
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"
|
||||
placeholder="örn: SN123456789"
|
||||
|
|
@ -262,8 +239,8 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
name="installationDate"
|
||||
value={
|
||||
formData.installationDate
|
||||
? formData.installationDate.toISOString().split("T")[0]
|
||||
: ""
|
||||
? formData.installationDate.toISOString().split('T')[0]
|
||||
: ''
|
||||
}
|
||||
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"
|
||||
|
|
@ -279,17 +256,15 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
name="warrantyExpiry"
|
||||
value={
|
||||
formData.warrantyExpiry
|
||||
? formData.warrantyExpiry.toISOString().split("T")[0]
|
||||
: ""
|
||||
? formData.warrantyExpiry.toISOString().split('T')[0]
|
||||
: ''
|
||||
}
|
||||
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"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Lokasyon *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Lokasyon *</label>
|
||||
<input
|
||||
type="text"
|
||||
name="location"
|
||||
|
|
@ -305,29 +280,19 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
|
||||
{/* Status and Priority */}
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||
Durum ve Öncelik
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">Durum ve Öncelik</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Durum
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
||||
<select
|
||||
name="status"
|
||||
value={formData.status}
|
||||
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={WorkCenterStatusEnum.Operational}>
|
||||
Operasyonel
|
||||
</option>
|
||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>
|
||||
Bakımda
|
||||
</option>
|
||||
<option value={WorkCenterStatusEnum.OutOfOrder}>
|
||||
Arızalı
|
||||
</option>
|
||||
<option value={WorkCenterStatusEnum.Operational}>Operasyonel</option>
|
||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>Bakımda</option>
|
||||
<option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option>
|
||||
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -348,16 +313,14 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Aktif Durum
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Aktif Durum</label>
|
||||
<select
|
||||
name="isActive"
|
||||
value={formData.isActive ? "true" : "false"}
|
||||
value={formData.isActive ? 'true' : 'false'}
|
||||
onChange={(e) =>
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
isActive: e.target.value === "true",
|
||||
isActive: e.target.value === 'true',
|
||||
}))
|
||||
}
|
||||
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"
|
||||
|
|
@ -372,9 +335,7 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
{/* Specifications */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-medium text-gray-900">
|
||||
Teknik Özellikler
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900">Teknik Özellikler</h3>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addSpecification}
|
||||
|
|
@ -386,19 +347,12 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
</div>
|
||||
<div className="space-y-3">
|
||||
{specifications.map((spec, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg"
|
||||
>
|
||||
<div key={index} className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg">
|
||||
<input
|
||||
type="text"
|
||||
value={spec.specificationName}
|
||||
onChange={(e) =>
|
||||
updateSpecification(
|
||||
index,
|
||||
"specificationName",
|
||||
e.target.value
|
||||
)
|
||||
updateSpecification(index, 'specificationName', e.target.value)
|
||||
}
|
||||
placeholder="Özellik adı"
|
||||
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"
|
||||
|
|
@ -407,21 +361,15 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
type="text"
|
||||
value={spec.specificationValue}
|
||||
onChange={(e) =>
|
||||
updateSpecification(
|
||||
index,
|
||||
"specificationValue",
|
||||
e.target.value
|
||||
)
|
||||
updateSpecification(index, 'specificationValue', e.target.value)
|
||||
}
|
||||
placeholder="Değer"
|
||||
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"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={spec.unit || ""}
|
||||
onChange={(e) =>
|
||||
updateSpecification(index, "unit", e.target.value)
|
||||
}
|
||||
value={spec.unit || ''}
|
||||
onChange={(e) => updateSpecification(index, 'unit', e.target.value)}
|
||||
placeholder="Birim"
|
||||
className="w-20 px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
|
|
@ -429,13 +377,7 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
<input
|
||||
type="checkbox"
|
||||
checked={spec.isRequired}
|
||||
onChange={(e) =>
|
||||
updateSpecification(
|
||||
index,
|
||||
"isRequired",
|
||||
e.target.checked
|
||||
)
|
||||
}
|
||||
onChange={(e) => updateSpecification(index, 'isRequired', e.target.checked)}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">Zorunlu</span>
|
||||
|
|
@ -451,8 +393,8 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
))}
|
||||
{specifications.length === 0 && (
|
||||
<p className="text-gray-500 text-center py-4">
|
||||
Henüz teknik özellik eklenmedi. "Özellik Ekle" butonunu
|
||||
kullanarak özellik ekleyebilirsiniz.
|
||||
Henüz teknik özellik eklenmedi. "Özellik Ekle" butonunu kullanarak özellik
|
||||
ekleyebilirsiniz.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -460,20 +402,18 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
|
||||
{/* WorkCenter Information Summary */}
|
||||
<div className="bg-gray-50 p-3 rounded-lg">
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-3">
|
||||
İş Merkezi Bilgi Özeti
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-3">İş Merkezi Bilgi Özeti</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="text-gray-600">Oluşturulma Tarihi:</span>
|
||||
<p className="font-medium text-gray-900">
|
||||
{formData.creationTime.toLocaleDateString("tr-TR")}
|
||||
{formData.creationTime.toLocaleDateString('tr-TR')}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-600">Son Güncelleme:</span>
|
||||
<p className="font-medium text-gray-900">
|
||||
{formData.lastModificationTime.toLocaleDateString("tr-TR")}
|
||||
{formData.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -500,7 +440,7 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default EditWorkCenterModal;
|
||||
export default EditWorkCenterModal
|
||||
|
|
|
|||
|
|
@ -1,30 +1,23 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
FaTimes,
|
||||
FaPlus,
|
||||
FaTrash,
|
||||
FaCalendar,
|
||||
FaClock,
|
||||
FaSave,
|
||||
} from "react-icons/fa";
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { FaTimes, FaPlus, FaTrash, FaCalendar, FaClock, FaSave } from 'react-icons/fa'
|
||||
import {
|
||||
PmMaintenanceWorkOrder,
|
||||
WorkOrderTypeEnum,
|
||||
WorkOrderStatusEnum,
|
||||
PmWorkOrderMaterial,
|
||||
PmWorkOrderActivity,
|
||||
} from "../../../types/pm";
|
||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
||||
import { mockMaterials } from "../../../mocks/mockMaterials";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
} from '../../../types/pm'
|
||||
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||
import { mockMaterials } from '../../../mocks/mockMaterials'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
|
||||
interface EditWorkOrderModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (workOrder: PmMaintenanceWorkOrder) => void;
|
||||
workOrder: PmMaintenanceWorkOrder;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (workOrder: PmMaintenanceWorkOrder) => void
|
||||
workOrder: PmMaintenanceWorkOrder
|
||||
}
|
||||
|
||||
const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||
|
|
@ -34,28 +27,28 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
workOrder,
|
||||
}) => {
|
||||
const [formData, setFormData] = useState({
|
||||
workOrderNumber: "",
|
||||
workCenterId: "",
|
||||
workOrderNumber: '',
|
||||
workCenterId: '',
|
||||
orderType: WorkOrderTypeEnum.Corrective,
|
||||
priority: PriorityEnum.Normal,
|
||||
status: WorkOrderStatusEnum.Created,
|
||||
description: "",
|
||||
reportedBy: "",
|
||||
assignedTo: "",
|
||||
maintenanceTeamId: "",
|
||||
scheduledStart: "",
|
||||
scheduledEnd: "",
|
||||
actualStart: "",
|
||||
actualEnd: "",
|
||||
description: '',
|
||||
reportedBy: '',
|
||||
assignedTo: '',
|
||||
maintenanceTeamId: '',
|
||||
scheduledStart: '',
|
||||
scheduledEnd: '',
|
||||
actualStart: '',
|
||||
actualEnd: '',
|
||||
estimatedCost: 0,
|
||||
actualCost: 0,
|
||||
notes: "",
|
||||
completionNotes: "",
|
||||
});
|
||||
notes: '',
|
||||
completionNotes: '',
|
||||
})
|
||||
|
||||
const [materials, setMaterials] = useState<PmWorkOrderMaterial[]>([]);
|
||||
const [activities, setActivities] = useState<PmWorkOrderActivity[]>([]);
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [materials, setMaterials] = useState<PmWorkOrderMaterial[]>([])
|
||||
const [activities, setActivities] = useState<PmWorkOrderActivity[]>([])
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
|
||||
useEffect(() => {
|
||||
if (workOrder && isOpen) {
|
||||
|
|
@ -67,56 +60,52 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
status: workOrder.status,
|
||||
description: workOrder.description,
|
||||
reportedBy: workOrder.reportedBy,
|
||||
assignedTo: workOrder.assignedTo || "",
|
||||
maintenanceTeamId: workOrder.maintenanceTeamId || "",
|
||||
assignedTo: workOrder.assignedTo || '',
|
||||
maintenanceTeamId: workOrder.maintenanceTeamId || '',
|
||||
scheduledStart: workOrder.scheduledStart
|
||||
? workOrder.scheduledStart.toISOString().slice(0, 16)
|
||||
: "",
|
||||
: '',
|
||||
scheduledEnd: workOrder.scheduledEnd
|
||||
? workOrder.scheduledEnd.toISOString().slice(0, 16)
|
||||
: "",
|
||||
actualStart: workOrder.actualStart
|
||||
? workOrder.actualStart.toISOString().slice(0, 16)
|
||||
: "",
|
||||
actualEnd: workOrder.actualEnd
|
||||
? workOrder.actualEnd.toISOString().slice(0, 16)
|
||||
: "",
|
||||
: '',
|
||||
actualStart: workOrder.actualStart ? workOrder.actualStart.toISOString().slice(0, 16) : '',
|
||||
actualEnd: workOrder.actualEnd ? workOrder.actualEnd.toISOString().slice(0, 16) : '',
|
||||
estimatedCost: workOrder.estimatedCost,
|
||||
actualCost: workOrder.actualCost,
|
||||
notes: workOrder.notes || "",
|
||||
completionNotes: workOrder.completionNotes || "",
|
||||
});
|
||||
setMaterials(workOrder.materials);
|
||||
setActivities(workOrder.activities);
|
||||
notes: workOrder.notes || '',
|
||||
completionNotes: workOrder.completionNotes || '',
|
||||
})
|
||||
setMaterials(workOrder.materials)
|
||||
setActivities(workOrder.activities)
|
||||
}
|
||||
}, [workOrder, isOpen]);
|
||||
}, [workOrder, isOpen])
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (!formData.description.trim()) {
|
||||
newErrors.description = "Açıklama alanı zorunludur";
|
||||
newErrors.description = 'Açıklama alanı zorunludur'
|
||||
}
|
||||
if (!formData.workCenterId) {
|
||||
newErrors.workCenterId = "İş merkezi seçimi zorunludur";
|
||||
newErrors.workCenterId = 'İş merkezi seçimi zorunludur'
|
||||
}
|
||||
if (!formData.reportedBy.trim()) {
|
||||
newErrors.reportedBy = "Bildiren kişi zorunludur";
|
||||
newErrors.reportedBy = 'Bildiren kişi zorunludur'
|
||||
}
|
||||
if (formData.estimatedCost < 0) {
|
||||
newErrors.estimatedCost = "Tahmini maliyet negatif olamaz";
|
||||
newErrors.estimatedCost = 'Tahmini maliyet negatif olamaz'
|
||||
}
|
||||
if (formData.actualCost < 0) {
|
||||
newErrors.actualCost = "Gerçek maliyet negatif olamaz";
|
||||
newErrors.actualCost = 'Gerçek maliyet negatif olamaz'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!validateForm()) return;
|
||||
e.preventDefault()
|
||||
if (!validateForm()) return
|
||||
|
||||
const updatedWorkOrder: PmMaintenanceWorkOrder = {
|
||||
...workOrder,
|
||||
|
|
@ -129,15 +118,9 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
reportedBy: formData.reportedBy,
|
||||
assignedTo: formData.assignedTo || undefined,
|
||||
maintenanceTeamId: formData.maintenanceTeamId || undefined,
|
||||
scheduledStart: formData.scheduledStart
|
||||
? new Date(formData.scheduledStart)
|
||||
: undefined,
|
||||
scheduledEnd: formData.scheduledEnd
|
||||
? new Date(formData.scheduledEnd)
|
||||
: undefined,
|
||||
actualStart: formData.actualStart
|
||||
? new Date(formData.actualStart)
|
||||
: undefined,
|
||||
scheduledStart: formData.scheduledStart ? new Date(formData.scheduledStart) : undefined,
|
||||
scheduledEnd: formData.scheduledEnd ? new Date(formData.scheduledEnd) : undefined,
|
||||
actualStart: formData.actualStart ? new Date(formData.actualStart) : undefined,
|
||||
actualEnd: formData.actualEnd ? new Date(formData.actualEnd) : undefined,
|
||||
estimatedCost: formData.estimatedCost,
|
||||
actualCost: formData.actualCost,
|
||||
|
|
@ -146,39 +129,35 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
notes: formData.notes || undefined,
|
||||
completionNotes: formData.completionNotes || undefined,
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
onSave(updatedWorkOrder);
|
||||
onClose();
|
||||
};
|
||||
onSave(updatedWorkOrder)
|
||||
onClose()
|
||||
}
|
||||
|
||||
const addMaterial = () => {
|
||||
const newMaterial: PmWorkOrderMaterial = {
|
||||
id: `mat-${Date.now()}`,
|
||||
workOrderId: workOrder.id,
|
||||
materialId: "",
|
||||
materialCode: "",
|
||||
materialName: "",
|
||||
materialId: '',
|
||||
materialCode: '',
|
||||
materialName: '',
|
||||
plannedQuantity: 1,
|
||||
actualQuantity: 0,
|
||||
unitCost: 0,
|
||||
totalCost: 0,
|
||||
};
|
||||
setMaterials([...materials, newMaterial]);
|
||||
};
|
||||
}
|
||||
setMaterials([...materials, newMaterial])
|
||||
}
|
||||
|
||||
const removeMaterial = (index: number) => {
|
||||
setMaterials(materials.filter((_, i) => i !== index));
|
||||
};
|
||||
setMaterials(materials.filter((_, i) => i !== index))
|
||||
}
|
||||
|
||||
const updateMaterial = (
|
||||
index: number,
|
||||
field: string,
|
||||
value: string | number
|
||||
) => {
|
||||
const updated = [...materials];
|
||||
if (field === "materialId") {
|
||||
const selectedMaterial = mockMaterials.find((m) => m.id === value);
|
||||
const updateMaterial = (index: number, field: string, value: string | number) => {
|
||||
const updated = [...materials]
|
||||
if (field === 'materialId') {
|
||||
const selectedMaterial = mockMaterials.find((m) => m.id === value)
|
||||
if (selectedMaterial) {
|
||||
updated[index] = {
|
||||
...updated[index],
|
||||
|
|
@ -186,75 +165,70 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
materialCode: selectedMaterial.code,
|
||||
materialName: selectedMaterial.name,
|
||||
unitCost: selectedMaterial.costPrice,
|
||||
totalCost:
|
||||
updated[index].plannedQuantity * selectedMaterial.costPrice,
|
||||
};
|
||||
totalCost: updated[index].plannedQuantity * selectedMaterial.costPrice,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const material = updated[index];
|
||||
if (field === "plannedQuantity") {
|
||||
material.plannedQuantity = value as number;
|
||||
material.totalCost = material.plannedQuantity * material.unitCost;
|
||||
} else if (field === "actualQuantity") {
|
||||
material.actualQuantity = value as number;
|
||||
} else if (field === "unitCost") {
|
||||
material.unitCost = value as number;
|
||||
material.totalCost = material.plannedQuantity * material.unitCost;
|
||||
const material = updated[index]
|
||||
if (field === 'plannedQuantity') {
|
||||
material.plannedQuantity = value as number
|
||||
material.totalCost = material.plannedQuantity * material.unitCost
|
||||
} else if (field === 'actualQuantity') {
|
||||
material.actualQuantity = value as number
|
||||
} else if (field === 'unitCost') {
|
||||
material.unitCost = value as number
|
||||
material.totalCost = material.plannedQuantity * material.unitCost
|
||||
}
|
||||
}
|
||||
setMaterials(updated);
|
||||
};
|
||||
setMaterials(updated)
|
||||
}
|
||||
|
||||
const addActivity = () => {
|
||||
const newActivity: PmWorkOrderActivity = {
|
||||
id: `act-${Date.now()}`,
|
||||
workOrderId: workOrder.id,
|
||||
activityDescription: "",
|
||||
activityDescription: '',
|
||||
plannedDuration: 60,
|
||||
actualDuration: 0,
|
||||
performedBy: "",
|
||||
notes: "",
|
||||
};
|
||||
setActivities([...activities, newActivity]);
|
||||
};
|
||||
performedBy: '',
|
||||
notes: '',
|
||||
}
|
||||
setActivities([...activities, newActivity])
|
||||
}
|
||||
|
||||
const removeActivity = (index: number) => {
|
||||
setActivities(activities.filter((_, i) => i !== index));
|
||||
};
|
||||
setActivities(activities.filter((_, i) => i !== index))
|
||||
}
|
||||
|
||||
const updateActivity = (
|
||||
index: number,
|
||||
field: string,
|
||||
value: string | number
|
||||
) => {
|
||||
const updated = [...activities];
|
||||
const activity = updated[index];
|
||||
if (field === "activityDescription") {
|
||||
activity.activityDescription = value as string;
|
||||
} else if (field === "plannedDuration") {
|
||||
activity.plannedDuration = value as number;
|
||||
} else if (field === "actualDuration") {
|
||||
activity.actualDuration = value as number;
|
||||
} else if (field === "performedBy") {
|
||||
activity.performedBy = value as string;
|
||||
} else if (field === "notes") {
|
||||
activity.notes = value as string;
|
||||
const updateActivity = (index: number, field: string, value: string | number) => {
|
||||
const updated = [...activities]
|
||||
const activity = updated[index]
|
||||
if (field === 'activityDescription') {
|
||||
activity.activityDescription = value as string
|
||||
} else if (field === 'plannedDuration') {
|
||||
activity.plannedDuration = value as number
|
||||
} else if (field === 'actualDuration') {
|
||||
activity.actualDuration = value as number
|
||||
} else if (field === 'performedBy') {
|
||||
activity.performedBy = value as string
|
||||
} else if (field === 'notes') {
|
||||
activity.notes = value as string
|
||||
}
|
||||
setActivities(updated);
|
||||
};
|
||||
setActivities(updated)
|
||||
}
|
||||
|
||||
const toggleActivityCompletion = (index: number) => {
|
||||
const updated = [...activities];
|
||||
const activity = updated[index];
|
||||
const updated = [...activities]
|
||||
const activity = updated[index]
|
||||
if (activity.completedAt) {
|
||||
activity.completedAt = undefined;
|
||||
activity.completedAt = undefined
|
||||
} else {
|
||||
activity.completedAt = new Date();
|
||||
activity.completedAt = new Date()
|
||||
}
|
||||
setActivities(updated);
|
||||
};
|
||||
setActivities(updated)
|
||||
}
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -264,10 +238,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
<FaSave className="w-5 h-5 mr-2 text-blue-600" />
|
||||
İş Emrini Düzenle
|
||||
</h3>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -276,24 +247,18 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
{/* Basic Information */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
İş Emri No
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">İş Emri No</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.workOrderNumber}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, workOrderNumber: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, workOrderNumber: 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"
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Durum
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
||||
<select
|
||||
value={formData.status}
|
||||
onChange={(e) =>
|
||||
|
|
@ -306,33 +271,21 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
>
|
||||
<option value={WorkOrderStatusEnum.Created}>Oluşturuldu</option>
|
||||
<option value={WorkOrderStatusEnum.Planned}>Planlandı</option>
|
||||
<option value={WorkOrderStatusEnum.Released}>
|
||||
Serbest Bırakıldı
|
||||
</option>
|
||||
<option value={WorkOrderStatusEnum.InProgress}>
|
||||
Devam Ediyor
|
||||
</option>
|
||||
<option value={WorkOrderStatusEnum.Released}>Serbest Bırakıldı</option>
|
||||
<option value={WorkOrderStatusEnum.InProgress}>Devam Ediyor</option>
|
||||
<option value={WorkOrderStatusEnum.OnHold}>Beklemede</option>
|
||||
<option value={WorkOrderStatusEnum.Completed}>
|
||||
Tamamlandı
|
||||
</option>
|
||||
<option value={WorkOrderStatusEnum.Cancelled}>
|
||||
İptal Edildi
|
||||
</option>
|
||||
<option value={WorkOrderStatusEnum.Completed}>Tamamlandı</option>
|
||||
<option value={WorkOrderStatusEnum.Cancelled}>İptal Edildi</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
İş Merkezi *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||
<select
|
||||
value={formData.workCenterId}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, workCenterId: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, workCenterId: 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.workCenterId ? "border-red-500" : "border-gray-300"
|
||||
errors.workCenterId ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">İş Merkezi Seçin</option>
|
||||
|
|
@ -343,16 +296,12 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
))}
|
||||
</select>
|
||||
{errors.workCenterId && (
|
||||
<p className="mt-1 text-sm text-red-600">
|
||||
{errors.workCenterId}
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-red-600">{errors.workCenterId}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
İş Emri Tipi
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">İş Emri Tipi</label>
|
||||
<select
|
||||
value={formData.orderType}
|
||||
onChange={(e) =>
|
||||
|
|
@ -367,16 +316,12 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
<option value={WorkOrderTypeEnum.Corrective}>Düzeltici</option>
|
||||
<option value={WorkOrderTypeEnum.Emergency}>Acil</option>
|
||||
<option value={WorkOrderTypeEnum.Inspection}>İnceleme</option>
|
||||
<option value={WorkOrderTypeEnum.Calibration}>
|
||||
Kalibrasyon
|
||||
</option>
|
||||
<option value={WorkOrderTypeEnum.Calibration}>Kalibrasyon</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Öncelik
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Öncelik</label>
|
||||
<select
|
||||
value={formData.priority}
|
||||
onChange={(e) =>
|
||||
|
|
@ -396,17 +341,13 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Açıklama *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama *</label>
|
||||
<textarea
|
||||
value={formData.description}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, description: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||
rows={2}
|
||||
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.description ? "border-red-500" : "border-gray-300"
|
||||
errors.description ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="İş emri açıklaması..."
|
||||
/>
|
||||
|
|
@ -418,17 +359,13 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
{/* Assignment */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Bildiren *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Bildiren *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.reportedBy}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, reportedBy: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, reportedBy: 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.reportedBy ? "border-red-500" : "border-gray-300"
|
||||
errors.reportedBy ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Bildiren kişi adı"
|
||||
/>
|
||||
|
|
@ -438,14 +375,10 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Atanan Kişi
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Atanan Kişi</label>
|
||||
<select
|
||||
value={formData.assignedTo}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, assignedTo: e.target.value })
|
||||
}
|
||||
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>
|
||||
|
|
@ -458,9 +391,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Bakım Ekibi
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Bakım Ekibi</label>
|
||||
<select
|
||||
value={formData.maintenanceTeamId}
|
||||
onChange={(e) =>
|
||||
|
|
@ -491,9 +422,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
<input
|
||||
type="datetime-local"
|
||||
value={formData.scheduledStart}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, scheduledStart: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, scheduledStart: 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"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -506,9 +435,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
<input
|
||||
type="datetime-local"
|
||||
value={formData.scheduledEnd}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, scheduledEnd: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, scheduledEnd: 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"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -521,9 +448,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
<input
|
||||
type="datetime-local"
|
||||
value={formData.actualStart}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, actualStart: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, actualStart: 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"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -536,9 +461,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
<input
|
||||
type="datetime-local"
|
||||
value={formData.actualEnd}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, actualEnd: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, actualEnd: 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"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -562,21 +485,17 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
})
|
||||
}
|
||||
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.estimatedCost ? "border-red-500" : "border-gray-300"
|
||||
errors.estimatedCost ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
{errors.estimatedCost && (
|
||||
<p className="mt-1 text-sm text-red-600">
|
||||
{errors.estimatedCost}
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-red-600">{errors.estimatedCost}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Gerçek Maliyet
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Gerçek Maliyet</label>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
|
|
@ -589,7 +508,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
})
|
||||
}
|
||||
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.actualCost ? "border-red-500" : "border-gray-300"
|
||||
errors.actualCost ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
|
|
@ -614,20 +533,13 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
</div>
|
||||
|
||||
{materials.map((material, index) => (
|
||||
<div
|
||||
key={material.id || index}
|
||||
className="bg-gray-50 p-3 rounded-lg mb-2"
|
||||
>
|
||||
<div key={material.id || index} className="bg-gray-50 p-3 rounded-lg mb-2">
|
||||
<div className="grid grid-cols-1 md:grid-cols-6 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Malzeme
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Malzeme</label>
|
||||
<select
|
||||
value={material.materialId}
|
||||
onChange={(e) =>
|
||||
updateMaterial(index, "materialId", e.target.value)
|
||||
}
|
||||
onChange={(e) => updateMaterial(index, 'materialId', 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="">Malzeme Seçin</option>
|
||||
|
|
@ -647,11 +559,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
min="0"
|
||||
value={material.plannedQuantity}
|
||||
onChange={(e) =>
|
||||
updateMaterial(
|
||||
index,
|
||||
"plannedQuantity",
|
||||
parseInt(e.target.value) || 0
|
||||
)
|
||||
updateMaterial(index, 'plannedQuantity', parseInt(e.target.value) || 0)
|
||||
}
|
||||
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"
|
||||
/>
|
||||
|
|
@ -665,11 +573,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
min="0"
|
||||
value={material.actualQuantity}
|
||||
onChange={(e) =>
|
||||
updateMaterial(
|
||||
index,
|
||||
"actualQuantity",
|
||||
parseInt(e.target.value) || 0
|
||||
)
|
||||
updateMaterial(index, 'actualQuantity', parseInt(e.target.value) || 0)
|
||||
}
|
||||
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"
|
||||
/>
|
||||
|
|
@ -684,19 +588,13 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
step="0.01"
|
||||
value={material.unitCost}
|
||||
onChange={(e) =>
|
||||
updateMaterial(
|
||||
index,
|
||||
"unitCost",
|
||||
parseFloat(e.target.value) || 0
|
||||
)
|
||||
updateMaterial(index, 'unitCost', parseFloat(e.target.value) || 0)
|
||||
}
|
||||
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"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Toplam
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Toplam</label>
|
||||
<input
|
||||
type="text"
|
||||
value={`₺${material.totalCost.toFixed(2)}`}
|
||||
|
|
@ -733,10 +631,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
</div>
|
||||
|
||||
{activities.map((activity, index) => (
|
||||
<div
|
||||
key={activity.id || index}
|
||||
className="bg-gray-50 p-3 rounded-lg mb-2"
|
||||
>
|
||||
<div key={activity.id || index} className="bg-gray-50 p-3 rounded-lg mb-2">
|
||||
<div className="grid grid-cols-1 md:grid-cols-6 gap-4">
|
||||
<div className="md:col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
|
|
@ -745,13 +640,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
<input
|
||||
type="text"
|
||||
value={activity.activityDescription}
|
||||
onChange={(e) =>
|
||||
updateActivity(
|
||||
index,
|
||||
"activityDescription",
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
onChange={(e) => updateActivity(index, 'activityDescription', 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"
|
||||
placeholder="Aktivite açıklaması"
|
||||
/>
|
||||
|
|
@ -766,11 +655,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
min="0"
|
||||
value={activity.plannedDuration}
|
||||
onChange={(e) =>
|
||||
updateActivity(
|
||||
index,
|
||||
"plannedDuration",
|
||||
parseInt(e.target.value) || 0
|
||||
)
|
||||
updateActivity(index, 'plannedDuration', parseInt(e.target.value) || 0)
|
||||
}
|
||||
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"
|
||||
/>
|
||||
|
|
@ -785,25 +670,17 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
min="0"
|
||||
value={activity.actualDuration}
|
||||
onChange={(e) =>
|
||||
updateActivity(
|
||||
index,
|
||||
"actualDuration",
|
||||
parseInt(e.target.value) || 0
|
||||
)
|
||||
updateActivity(index, 'actualDuration', parseInt(e.target.value) || 0)
|
||||
}
|
||||
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"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Yapan
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Yapan</label>
|
||||
<input
|
||||
type="text"
|
||||
value={activity.performedBy}
|
||||
onChange={(e) =>
|
||||
updateActivity(index, "performedBy", e.target.value)
|
||||
}
|
||||
onChange={(e) => updateActivity(index, 'performedBy', 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"
|
||||
placeholder="Yapan kişi"
|
||||
/>
|
||||
|
|
@ -814,11 +691,11 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
onClick={() => toggleActivityCompletion(index)}
|
||||
className={`px-3 py-2 rounded text-xs ${
|
||||
activity.completedAt
|
||||
? "bg-green-600 text-white"
|
||||
: "bg-gray-300 text-gray-700"
|
||||
? 'bg-green-600 text-white'
|
||||
: 'bg-gray-300 text-gray-700'
|
||||
}`}
|
||||
>
|
||||
{activity.completedAt ? "Tamamlandı" : "Bekliyor"}
|
||||
{activity.completedAt ? 'Tamamlandı' : 'Bekliyor'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -836,9 +713,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
</label>
|
||||
<textarea
|
||||
value={activity.notes}
|
||||
onChange={(e) =>
|
||||
updateActivity(index, "notes", e.target.value)
|
||||
}
|
||||
onChange={(e) => updateActivity(index, 'notes', e.target.value)}
|
||||
rows={1}
|
||||
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="Aktivite hakkında notlar..."
|
||||
|
|
@ -857,9 +732,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
</label>
|
||||
<textarea
|
||||
value={formData.notes}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, notes: e.target.value })
|
||||
}
|
||||
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="Genel notlar..."
|
||||
|
|
@ -872,9 +745,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
</label>
|
||||
<textarea
|
||||
value={formData.completionNotes}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, completionNotes: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, completionNotes: 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="Tamamlanma ile ilgili notlar..."
|
||||
|
|
@ -902,7 +773,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default EditWorkOrderModal;
|
||||
export default EditWorkOrderModal
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useState } from 'react'
|
||||
import {
|
||||
FaPlus,
|
||||
FaSearch,
|
||||
|
|
@ -13,21 +13,17 @@ import {
|
|||
FaFileAlt,
|
||||
FaCamera,
|
||||
FaPhone,
|
||||
} from "react-icons/fa";
|
||||
import {
|
||||
NotificationStatusEnum,
|
||||
PmFaultNotification,
|
||||
PmWorkCenter,
|
||||
} from "../../../types/pm";
|
||||
import { mockFaultNotifications } from "../../../mocks/mockFaultNotifications";
|
||||
import NewFaultNotificationModal from "./NewFaultNotificationModal";
|
||||
import ViewFaultNotificationModal from "./ViewFaultNotificationModal";
|
||||
import EditFaultNotificationModal from "./EditFaultNotificationModal";
|
||||
import CreateWorkOrderFromNotificationModal from "./CreateWorkOrderFromNotificationModal";
|
||||
import AssignNotificationModal from "./AssignNotificationModal";
|
||||
import ChangeNotificationStatusModal from "./ChangeNotificationStatusModal";
|
||||
import Widget from "../../../components/common/Widget";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
} from 'react-icons/fa'
|
||||
import { NotificationStatusEnum, PmFaultNotification, PmWorkCenter } from '../../../types/pm'
|
||||
import { mockFaultNotifications } from '../../../mocks/mockFaultNotifications'
|
||||
import NewFaultNotificationModal from './NewFaultNotificationModal'
|
||||
import ViewFaultNotificationModal from './ViewFaultNotificationModal'
|
||||
import EditFaultNotificationModal from './EditFaultNotificationModal'
|
||||
import CreateWorkOrderFromNotificationModal from './CreateWorkOrderFromNotificationModal'
|
||||
import AssignNotificationModal from './AssignNotificationModal'
|
||||
import ChangeNotificationStatusModal from './ChangeNotificationStatusModal'
|
||||
import Widget from '../../../components/common/Widget'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
import {
|
||||
getFaultTypeColor,
|
||||
getFaultTypeText,
|
||||
|
|
@ -37,139 +33,116 @@ import {
|
|||
getNotificationStatusColor,
|
||||
getNotificationStatusIcon,
|
||||
getNotificationStatusText,
|
||||
} from "../../../utils/erp";
|
||||
} from '../../../utils/erp'
|
||||
import { Container } from '@/components/shared'
|
||||
|
||||
interface AssignmentData {
|
||||
notificationIds: string[];
|
||||
assignedTo?: string;
|
||||
teamId?: string;
|
||||
notificationIds: string[]
|
||||
assignedTo?: string
|
||||
teamId?: string
|
||||
}
|
||||
|
||||
interface StatusChangeData {
|
||||
notificationIds: string[];
|
||||
status: NotificationStatusEnum;
|
||||
notificationIds: string[]
|
||||
status: NotificationStatusEnum
|
||||
}
|
||||
|
||||
const FaultNotifications: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [statusFilter, setStatusFilter] = useState<
|
||||
NotificationStatusEnum | "all"
|
||||
>("all");
|
||||
const [priorityFilter, setPriorityFilter] = useState<PriorityEnum | "all">(
|
||||
"all"
|
||||
);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [editingNotification, setEditingNotification] =
|
||||
useState<PmFaultNotification | null>(null);
|
||||
const [viewingNotification, setViewingNotification] =
|
||||
useState<PmFaultNotification | null>(null);
|
||||
const [selectedNotifications, setSelectedNotifications] = useState<string[]>(
|
||||
[]
|
||||
);
|
||||
const [showCreateWorkOrderModal, setShowCreateWorkOrderModal] =
|
||||
useState(false);
|
||||
const [showAssignModal, setShowAssignModal] = useState(false);
|
||||
const [showStatusChangeModal, setShowStatusChangeModal] = useState(false);
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const [statusFilter, setStatusFilter] = useState<NotificationStatusEnum | 'all'>('all')
|
||||
const [priorityFilter, setPriorityFilter] = useState<PriorityEnum | 'all'>('all')
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const [editingNotification, setEditingNotification] = useState<PmFaultNotification | null>(null)
|
||||
const [viewingNotification, setViewingNotification] = useState<PmFaultNotification | null>(null)
|
||||
const [selectedNotifications, setSelectedNotifications] = useState<string[]>([])
|
||||
const [showCreateWorkOrderModal, setShowCreateWorkOrderModal] = useState(false)
|
||||
const [showAssignModal, setShowAssignModal] = useState(false)
|
||||
const [showStatusChangeModal, setShowStatusChangeModal] = useState(false)
|
||||
|
||||
// Mock data - replace with actual API calls
|
||||
const [notifications, setNotifications] = useState<PmFaultNotification[]>(
|
||||
mockFaultNotifications
|
||||
);
|
||||
const [notifications, setNotifications] = useState<PmFaultNotification[]>(mockFaultNotifications)
|
||||
|
||||
const filteredNotifications = notifications.filter((notification) => {
|
||||
const matchesSearch =
|
||||
notification.notificationCode
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase()) ||
|
||||
notification.notificationCode.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
notification.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
notification.workCenter.code
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase()) ||
|
||||
notification.reportedBy.toLowerCase().includes(searchTerm.toLowerCase());
|
||||
const matchesStatus =
|
||||
statusFilter === "all" || notification.status === statusFilter;
|
||||
const matchesPriority =
|
||||
priorityFilter === "all" || notification.priority === priorityFilter;
|
||||
return matchesSearch && matchesStatus && matchesPriority;
|
||||
});
|
||||
notification.workCenter.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
notification.reportedBy.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
const matchesStatus = statusFilter === 'all' || notification.status === statusFilter
|
||||
const matchesPriority = priorityFilter === 'all' || notification.priority === priorityFilter
|
||||
return matchesSearch && matchesStatus && matchesPriority
|
||||
})
|
||||
|
||||
const getTimeAgo = (date: Date) => {
|
||||
const now = new Date();
|
||||
const diffInMs = now.getTime() - date.getTime();
|
||||
const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60));
|
||||
const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
|
||||
const now = new Date()
|
||||
const diffInMs = now.getTime() - date.getTime()
|
||||
const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60))
|
||||
const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24))
|
||||
|
||||
if (diffInHours < 1) {
|
||||
const diffInMinutes = Math.floor(diffInMs / (1000 * 60));
|
||||
return `${diffInMinutes} dakika önce`;
|
||||
const diffInMinutes = Math.floor(diffInMs / (1000 * 60))
|
||||
return `${diffInMinutes} dakika önce`
|
||||
} else if (diffInHours < 24) {
|
||||
return `${diffInHours} saat önce`;
|
||||
return `${diffInHours} saat önce`
|
||||
} else {
|
||||
return `${diffInDays} gün önce`;
|
||||
return `${diffInDays} gün önce`
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleAddNotification = () => {
|
||||
setEditingNotification(null);
|
||||
setShowModal(true);
|
||||
};
|
||||
setEditingNotification(null)
|
||||
setShowModal(true)
|
||||
}
|
||||
|
||||
const handleEdit = (notification: PmFaultNotification) => {
|
||||
setEditingNotification(notification);
|
||||
setShowModal(true);
|
||||
};
|
||||
setEditingNotification(notification)
|
||||
setShowModal(true)
|
||||
}
|
||||
|
||||
const handleView = (notification: PmFaultNotification) => {
|
||||
setViewingNotification(notification);
|
||||
};
|
||||
setViewingNotification(notification)
|
||||
}
|
||||
|
||||
const handleSelectNotification = (notificationId: string) => {
|
||||
setSelectedNotifications((prev) =>
|
||||
prev.includes(notificationId)
|
||||
? prev.filter((id) => id !== notificationId)
|
||||
: [...prev, notificationId]
|
||||
);
|
||||
};
|
||||
: [...prev, notificationId],
|
||||
)
|
||||
}
|
||||
|
||||
const handleSaveNotification = (
|
||||
notificationData: Partial<PmFaultNotification>
|
||||
) => {
|
||||
const handleSaveNotification = (notificationData: Partial<PmFaultNotification>) => {
|
||||
if (editingNotification) {
|
||||
// Update existing notification
|
||||
setNotifications((prev) =>
|
||||
prev.map((n) =>
|
||||
n.id === editingNotification.id ? { ...n, ...notificationData } : n
|
||||
)
|
||||
);
|
||||
prev.map((n) => (n.id === editingNotification.id ? { ...n, ...notificationData } : n)),
|
||||
)
|
||||
} else {
|
||||
// Add new notification
|
||||
setNotifications((prev) => [
|
||||
...prev,
|
||||
notificationData as PmFaultNotification,
|
||||
]);
|
||||
setNotifications((prev) => [...prev, notificationData as PmFaultNotification])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleCreateWorkOrder = () => {
|
||||
setShowCreateWorkOrderModal(true);
|
||||
};
|
||||
setShowCreateWorkOrderModal(true)
|
||||
}
|
||||
|
||||
const handleAssignNotifications = () => {
|
||||
setShowAssignModal(true);
|
||||
};
|
||||
setShowAssignModal(true)
|
||||
}
|
||||
|
||||
const handleChangeStatus = () => {
|
||||
setShowStatusChangeModal(true);
|
||||
};
|
||||
setShowStatusChangeModal(true)
|
||||
}
|
||||
|
||||
const handleWorkOrderSave = (workOrderData: PmWorkCenter) => {
|
||||
console.log("İş emri oluşturuldu:", workOrderData);
|
||||
console.log('İş emri oluşturuldu:', workOrderData)
|
||||
// Here you would typically save to backend
|
||||
setSelectedNotifications([]);
|
||||
};
|
||||
setSelectedNotifications([])
|
||||
}
|
||||
|
||||
const handleAssignmentSave = (assignmentData: AssignmentData) => {
|
||||
console.log("Atama yapıldı:", assignmentData);
|
||||
console.log('Atama yapıldı:', assignmentData)
|
||||
// Here you would typically save to backend
|
||||
// Update notifications with assignment
|
||||
setNotifications((prev) =>
|
||||
|
|
@ -180,14 +153,14 @@ const FaultNotifications: React.FC = () => {
|
|||
assignedTo: assignmentData.assignedTo,
|
||||
status: NotificationStatusEnum.Assigned,
|
||||
}
|
||||
: n
|
||||
)
|
||||
);
|
||||
setSelectedNotifications([]);
|
||||
};
|
||||
: n,
|
||||
),
|
||||
)
|
||||
setSelectedNotifications([])
|
||||
}
|
||||
|
||||
const handleStatusChangeSave = (statusChangeData: StatusChangeData) => {
|
||||
console.log("Durum değiştirildi:", statusChangeData);
|
||||
console.log('Durum değiştirildi:', statusChangeData)
|
||||
// Here you would typically save to backend
|
||||
// Update notifications with new status
|
||||
setNotifications((prev) =>
|
||||
|
|
@ -198,359 +171,322 @@ const FaultNotifications: React.FC = () => {
|
|||
status: statusChangeData.status,
|
||||
lastModificationTime: new Date(),
|
||||
}
|
||||
: n
|
||||
)
|
||||
);
|
||||
setSelectedNotifications([]);
|
||||
};
|
||||
: n,
|
||||
),
|
||||
)
|
||||
setSelectedNotifications([])
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4 pt-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
Arıza Bildirimleri
|
||||
</h2>
|
||||
<p className="text-gray-600">
|
||||
İş merkezi arızalarını takip edin ve yönetin
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAddNotification}
|
||||
className="bg-blue-600 text-white px-3 py-1.5 rounded-lg hover:bg-blue-700 flex items-center space-x-2 text-sm"
|
||||
>
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Bildirim</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Summary Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
|
||||
<Widget
|
||||
title="Toplam"
|
||||
value={notifications.length}
|
||||
color="gray"
|
||||
icon="FaExclamationTriangle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Açık"
|
||||
value={
|
||||
notifications.filter(
|
||||
(n) => n.status === NotificationStatusEnum.Open
|
||||
).length
|
||||
}
|
||||
color="red"
|
||||
icon="FaExclamationTriangle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Devam Ediyor"
|
||||
value={
|
||||
notifications.filter(
|
||||
(n) => n.status === NotificationStatusEnum.InProgress
|
||||
).length
|
||||
}
|
||||
color="orange"
|
||||
icon="FaClock"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Çözüldü"
|
||||
value={
|
||||
notifications.filter(
|
||||
(n) => n.status === NotificationStatusEnum.Resolved
|
||||
).length
|
||||
}
|
||||
color="green"
|
||||
icon="FaCheckCircle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Acil"
|
||||
value={
|
||||
notifications.filter((n) => n.priority === PriorityEnum.Urgent)
|
||||
.length
|
||||
}
|
||||
color="red"
|
||||
icon="FaTimesCircle"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex space-x-4">
|
||||
<div className="flex-1 relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Bildirim ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<FaFilter className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) =>
|
||||
setStatusFilter(e.target.value as NotificationStatusEnum | "all")
|
||||
}
|
||||
className="pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Durumlar</option>
|
||||
<option value={NotificationStatusEnum.Open}>Açık</option>
|
||||
<option value={NotificationStatusEnum.Assigned}>Atandı</option>
|
||||
<option value={NotificationStatusEnum.InProgress}>
|
||||
Devam Ediyor
|
||||
</option>
|
||||
<option value={NotificationStatusEnum.Resolved}>Çözüldü</option>
|
||||
<option value={NotificationStatusEnum.Closed}>Kapatıldı</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={priorityFilter}
|
||||
onChange={(e) =>
|
||||
setPriorityFilter(e.target.value as PriorityEnum | "all")
|
||||
}
|
||||
className="pl-4 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Öncelikler</option>
|
||||
<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>
|
||||
|
||||
{/* Notifications List */}
|
||||
<div className="space-y-3 pt-2">
|
||||
{filteredNotifications.map((notification) => (
|
||||
<div
|
||||
key={notification.id}
|
||||
className={`bg-white rounded-lg shadow-md border-l-4 p-4 hover:shadow-lg transition-shadow ${getCriticalityLevelColor(
|
||||
notification.severity
|
||||
)}`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-start space-x-4 flex-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedNotifications.includes(notification.id)}
|
||||
onChange={() => handleSelectNotification(notification.id)}
|
||||
className="mt-1 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-3 mb-2">
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
{notification.notificationCode}
|
||||
</h3>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium flex items-center space-x-1 ${getNotificationStatusColor(
|
||||
notification.status
|
||||
)}`}
|
||||
>
|
||||
{getNotificationStatusIcon(notification.status)}
|
||||
<span>
|
||||
{getNotificationStatusText(notification.status)}
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getPriorityColor(
|
||||
notification.priority
|
||||
)}`}
|
||||
>
|
||||
{getPriorityText(notification.priority)}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getFaultTypeColor(
|
||||
notification.faultType
|
||||
)}`}
|
||||
>
|
||||
{getFaultTypeText(notification.faultType)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h4 className="font-medium text-gray-800 mb-2">
|
||||
{notification.title}
|
||||
</h4>
|
||||
<p className="text-sm text-gray-600 mb-3">
|
||||
{notification.description.length > 150
|
||||
? `${notification.description.substring(0, 150)}...`
|
||||
: notification.description}
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 text-sm mb-3">
|
||||
<div>
|
||||
<span className="text-gray-500">İş Merkezi:</span>
|
||||
<p className="font-medium text-gray-900">
|
||||
{notification.workCenter.code}
|
||||
</p>
|
||||
<p className="text-xs text-gray-600">
|
||||
{notification.workCenter.name}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">Konum:</span>
|
||||
<p className="font-medium text-gray-900 flex items-center">
|
||||
<FaMapMarkerAlt className="w-3 h-3 mr-1" />
|
||||
{notification.location}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">Bildiren:</span>
|
||||
<p className="font-medium text-gray-900">
|
||||
{notification.reportedBy}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">Süre:</span>
|
||||
<p className="font-medium text-gray-900">
|
||||
{notification.estimatedRepairTime
|
||||
? `~${notification.estimatedRepairTime} dk`
|
||||
: "-"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{notification.assignedTo && (
|
||||
<div className="bg-blue-50 rounded-lg p-3 mb-3">
|
||||
<div className="flex items-center space-x-2 text-sm">
|
||||
<FaUser className="w-4 h-4 text-blue-600" />
|
||||
<span className="text-gray-600">Atanan:</span>
|
||||
<span className="font-medium text-gray-900">
|
||||
{notification.assignedTo}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{notification.images && notification.images.length > 0 && (
|
||||
<div className="flex items-center space-x-2 text-sm text-gray-500 mb-3">
|
||||
<FaCamera className="w-4 h-4" />
|
||||
<span>{notification.images.length} fotoğraf</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{notification.resolutionNotes && (
|
||||
<div className="bg-green-50 rounded-lg p-3 mb-3">
|
||||
<div className="flex items-start space-x-2 text-sm">
|
||||
<FaFileAlt className="w-4 h-4 text-green-600 mt-0.5" />
|
||||
<div>
|
||||
<span className="text-gray-600">Çözüm Notları:</span>
|
||||
<p className="text-gray-900 mt-1">
|
||||
{notification.resolutionNotes}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between text-xs text-gray-500">
|
||||
<div className="flex items-center space-x-2">
|
||||
<FaClock className="w-3 h-3" />
|
||||
<span>
|
||||
Bildirilme: {getTimeAgo(notification.reportedAt)}
|
||||
</span>
|
||||
</div>
|
||||
{notification.workOrderId && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<span>İş Emri: {notification.workOrderId}</span>
|
||||
</div>
|
||||
)}
|
||||
{notification.followUpRequired && (
|
||||
<div className="flex items-center space-x-2 text-orange-600">
|
||||
<FaExclamationTriangle className="w-3 h-3" />
|
||||
<span>Takip Gerekli</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-1">
|
||||
<button
|
||||
onClick={() => handleView(notification)}
|
||||
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||
>
|
||||
<FaEye className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleEdit(notification)}
|
||||
className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md transition-colors"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md transition-colors">
|
||||
<FaPhone className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Container>
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Arıza Bildirimleri</h2>
|
||||
<p className="text-gray-600">İş merkezi arızalarını takip edin ve yönetin</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{filteredNotifications.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<FaExclamationTriangle className="w-16 h-16 text-gray-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||
Bildirim bulunamadı
|
||||
</h3>
|
||||
<p className="text-gray-500 mb-4">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir bildirim oluşturun.
|
||||
</p>
|
||||
<button
|
||||
onClick={handleAddNotification}
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
|
||||
className="bg-blue-600 text-white px-3 py-1.5 rounded-lg hover:bg-blue-700 flex items-center space-x-2 text-sm"
|
||||
>
|
||||
Yeni Bildirim Oluştur
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Bildirim</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Bulk Actions */}
|
||||
{selectedNotifications.length > 0 && (
|
||||
<div className="fixed bottom-6 left-1/2 transform -translate-x-1/2 bg-white rounded-lg shadow-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
<span className="text-sm text-gray-600">
|
||||
{selectedNotifications.length} bildirim seçildi
|
||||
</span>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={handleCreateWorkOrder}
|
||||
className="bg-green-600 text-white px-3 py-2 rounded text-sm hover:bg-green-700"
|
||||
>
|
||||
İş Emri Oluştur
|
||||
</button>
|
||||
<button
|
||||
onClick={handleAssignNotifications}
|
||||
className="bg-blue-600 text-white px-3 py-2 rounded text-sm hover:bg-blue-700"
|
||||
>
|
||||
Atama Yap
|
||||
</button>
|
||||
<button
|
||||
onClick={handleChangeStatus}
|
||||
className="bg-orange-600 text-white px-3 py-2 rounded text-sm hover:bg-orange-700"
|
||||
>
|
||||
Durum Değiştir
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedNotifications([])}
|
||||
className="bg-gray-600 text-white px-3 py-2 rounded text-sm hover:bg-gray-700"
|
||||
>
|
||||
Temizle
|
||||
</button>
|
||||
</div>
|
||||
{/* Summary Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
|
||||
<Widget
|
||||
title="Toplam"
|
||||
value={notifications.length}
|
||||
color="gray"
|
||||
icon="FaExclamationTriangle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Açık"
|
||||
value={notifications.filter((n) => n.status === NotificationStatusEnum.Open).length}
|
||||
color="red"
|
||||
icon="FaExclamationTriangle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Devam Ediyor"
|
||||
value={
|
||||
notifications.filter((n) => n.status === NotificationStatusEnum.InProgress).length
|
||||
}
|
||||
color="orange"
|
||||
icon="FaClock"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Çözüldü"
|
||||
value={notifications.filter((n) => n.status === NotificationStatusEnum.Resolved).length}
|
||||
color="green"
|
||||
icon="FaCheckCircle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Acil"
|
||||
value={notifications.filter((n) => n.priority === PriorityEnum.Urgent).length}
|
||||
color="red"
|
||||
icon="FaTimesCircle"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex space-x-4">
|
||||
<div className="flex-1 relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Bildirim ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<FaFilter className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value as NotificationStatusEnum | 'all')}
|
||||
className="pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Durumlar</option>
|
||||
<option value={NotificationStatusEnum.Open}>Açık</option>
|
||||
<option value={NotificationStatusEnum.Assigned}>Atandı</option>
|
||||
<option value={NotificationStatusEnum.InProgress}>Devam Ediyor</option>
|
||||
<option value={NotificationStatusEnum.Resolved}>Çözüldü</option>
|
||||
<option value={NotificationStatusEnum.Closed}>Kapatıldı</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={priorityFilter}
|
||||
onChange={(e) => setPriorityFilter(e.target.value as PriorityEnum | 'all')}
|
||||
className="pl-4 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Öncelikler</option>
|
||||
<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>
|
||||
)}
|
||||
|
||||
{/* Notifications List */}
|
||||
<div className="space-y-3 pt-2">
|
||||
{filteredNotifications.map((notification) => (
|
||||
<div
|
||||
key={notification.id}
|
||||
className={`bg-white rounded-lg shadow-md border-l-4 p-4 hover:shadow-lg transition-shadow ${getCriticalityLevelColor(
|
||||
notification.severity,
|
||||
)}`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-start space-x-4 flex-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedNotifications.includes(notification.id)}
|
||||
onChange={() => handleSelectNotification(notification.id)}
|
||||
className="mt-1 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-3 mb-2">
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
{notification.notificationCode}
|
||||
</h3>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium flex items-center space-x-1 ${getNotificationStatusColor(
|
||||
notification.status,
|
||||
)}`}
|
||||
>
|
||||
{getNotificationStatusIcon(notification.status)}
|
||||
<span>{getNotificationStatusText(notification.status)}</span>
|
||||
</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getPriorityColor(
|
||||
notification.priority,
|
||||
)}`}
|
||||
>
|
||||
{getPriorityText(notification.priority)}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getFaultTypeColor(
|
||||
notification.faultType,
|
||||
)}`}
|
||||
>
|
||||
{getFaultTypeText(notification.faultType)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h4 className="font-medium text-gray-800 mb-2">{notification.title}</h4>
|
||||
<p className="text-sm text-gray-600 mb-3">
|
||||
{notification.description.length > 150
|
||||
? `${notification.description.substring(0, 150)}...`
|
||||
: notification.description}
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 text-sm mb-3">
|
||||
<div>
|
||||
<span className="text-gray-500">İş Merkezi:</span>
|
||||
<p className="font-medium text-gray-900">{notification.workCenter.code}</p>
|
||||
<p className="text-xs text-gray-600">{notification.workCenter.name}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">Konum:</span>
|
||||
<p className="font-medium text-gray-900 flex items-center">
|
||||
<FaMapMarkerAlt className="w-3 h-3 mr-1" />
|
||||
{notification.location}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">Bildiren:</span>
|
||||
<p className="font-medium text-gray-900">{notification.reportedBy}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">Süre:</span>
|
||||
<p className="font-medium text-gray-900">
|
||||
{notification.estimatedRepairTime
|
||||
? `~${notification.estimatedRepairTime} dk`
|
||||
: '-'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{notification.assignedTo && (
|
||||
<div className="bg-blue-50 rounded-lg p-3 mb-3">
|
||||
<div className="flex items-center space-x-2 text-sm">
|
||||
<FaUser className="w-4 h-4 text-blue-600" />
|
||||
<span className="text-gray-600">Atanan:</span>
|
||||
<span className="font-medium text-gray-900">
|
||||
{notification.assignedTo}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{notification.images && notification.images.length > 0 && (
|
||||
<div className="flex items-center space-x-2 text-sm text-gray-500 mb-3">
|
||||
<FaCamera className="w-4 h-4" />
|
||||
<span>{notification.images.length} fotoğraf</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{notification.resolutionNotes && (
|
||||
<div className="bg-green-50 rounded-lg p-3 mb-3">
|
||||
<div className="flex items-start space-x-2 text-sm">
|
||||
<FaFileAlt className="w-4 h-4 text-green-600 mt-0.5" />
|
||||
<div>
|
||||
<span className="text-gray-600">Çözüm Notları:</span>
|
||||
<p className="text-gray-900 mt-1">{notification.resolutionNotes}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between text-xs text-gray-500">
|
||||
<div className="flex items-center space-x-2">
|
||||
<FaClock className="w-3 h-3" />
|
||||
<span>Bildirilme: {getTimeAgo(notification.reportedAt)}</span>
|
||||
</div>
|
||||
{notification.workOrderId && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<span>İş Emri: {notification.workOrderId}</span>
|
||||
</div>
|
||||
)}
|
||||
{notification.followUpRequired && (
|
||||
<div className="flex items-center space-x-2 text-orange-600">
|
||||
<FaExclamationTriangle className="w-3 h-3" />
|
||||
<span>Takip Gerekli</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-1">
|
||||
<button
|
||||
onClick={() => handleView(notification)}
|
||||
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||
>
|
||||
<FaEye className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleEdit(notification)}
|
||||
className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md transition-colors"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md transition-colors">
|
||||
<FaPhone className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{filteredNotifications.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<FaExclamationTriangle className="w-16 h-16 text-gray-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">Bildirim bulunamadı</h3>
|
||||
<p className="text-gray-500 mb-4">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir bildirim oluşturun.
|
||||
</p>
|
||||
<button
|
||||
onClick={handleAddNotification}
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
|
||||
>
|
||||
Yeni Bildirim Oluştur
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Bulk Actions */}
|
||||
{selectedNotifications.length > 0 && (
|
||||
<div className="fixed bottom-6 left-1/2 transform -translate-x-1/2 bg-white rounded-lg shadow-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
<span className="text-sm text-gray-600">
|
||||
{selectedNotifications.length} bildirim seçildi
|
||||
</span>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={handleCreateWorkOrder}
|
||||
className="bg-green-600 text-white px-3 py-2 rounded text-sm hover:bg-green-700"
|
||||
>
|
||||
İş Emri Oluştur
|
||||
</button>
|
||||
<button
|
||||
onClick={handleAssignNotifications}
|
||||
className="bg-blue-600 text-white px-3 py-2 rounded text-sm hover:bg-blue-700"
|
||||
>
|
||||
Atama Yap
|
||||
</button>
|
||||
<button
|
||||
onClick={handleChangeStatus}
|
||||
className="bg-orange-600 text-white px-3 py-2 rounded text-sm hover:bg-orange-700"
|
||||
>
|
||||
Durum Değiştir
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedNotifications([])}
|
||||
className="bg-gray-600 text-white px-3 py-2 rounded text-sm hover:bg-gray-700"
|
||||
>
|
||||
Temizle
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Modals */}
|
||||
{showModal && !editingNotification && (
|
||||
|
|
@ -565,8 +501,8 @@ const FaultNotifications: React.FC = () => {
|
|||
<EditFaultNotificationModal
|
||||
isOpen={showModal}
|
||||
onClose={() => {
|
||||
setShowModal(false);
|
||||
setEditingNotification(null);
|
||||
setShowModal(false)
|
||||
setEditingNotification(null)
|
||||
}}
|
||||
onSave={handleSaveNotification}
|
||||
notification={editingNotification}
|
||||
|
|
@ -578,9 +514,9 @@ const FaultNotifications: React.FC = () => {
|
|||
isOpen={!!viewingNotification}
|
||||
onClose={() => setViewingNotification(null)}
|
||||
onEdit={(notification) => {
|
||||
setViewingNotification(null);
|
||||
setEditingNotification(notification);
|
||||
setShowModal(true);
|
||||
setViewingNotification(null)
|
||||
setEditingNotification(notification)
|
||||
setShowModal(true)
|
||||
}}
|
||||
notification={viewingNotification}
|
||||
/>
|
||||
|
|
@ -591,9 +527,7 @@ const FaultNotifications: React.FC = () => {
|
|||
isOpen={showCreateWorkOrderModal}
|
||||
onClose={() => setShowCreateWorkOrderModal(false)}
|
||||
onSave={handleWorkOrderSave}
|
||||
notifications={notifications.filter((n) =>
|
||||
selectedNotifications.includes(n.id)
|
||||
)}
|
||||
notifications={notifications.filter((n) => selectedNotifications.includes(n.id))}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
@ -602,9 +536,7 @@ const FaultNotifications: React.FC = () => {
|
|||
isOpen={showAssignModal}
|
||||
onClose={() => setShowAssignModal(false)}
|
||||
onSave={handleAssignmentSave}
|
||||
notifications={notifications.filter((n) =>
|
||||
selectedNotifications.includes(n.id)
|
||||
)}
|
||||
notifications={notifications.filter((n) => selectedNotifications.includes(n.id))}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
@ -613,13 +545,11 @@ const FaultNotifications: React.FC = () => {
|
|||
isOpen={showStatusChangeModal}
|
||||
onClose={() => setShowStatusChangeModal(false)}
|
||||
onSave={handleStatusChangeSave}
|
||||
notifications={notifications.filter((n) =>
|
||||
selectedNotifications.includes(n.id)
|
||||
)}
|
||||
notifications={notifications.filter((n) => selectedNotifications.includes(n.id))}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default FaultNotifications;
|
||||
export default FaultNotifications
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useState } from 'react'
|
||||
import {
|
||||
FaCalendar,
|
||||
FaClock,
|
||||
|
|
@ -8,124 +8,109 @@ import {
|
|||
FaChevronRight,
|
||||
FaEdit,
|
||||
FaUser,
|
||||
} from "react-icons/fa";
|
||||
import {
|
||||
WorkOrderStatusEnum,
|
||||
CalendarView,
|
||||
PmCalendarEvent,
|
||||
} from "../../../types/pm";
|
||||
import { mockCalendarEvents } from "../../../mocks/mockMaintenanceCalendarEvent";
|
||||
import NewCalendarEventModal from "./NewCalendarEventModal";
|
||||
} from 'react-icons/fa'
|
||||
import { WorkOrderStatusEnum, CalendarView, PmCalendarEvent } from '../../../types/pm'
|
||||
import { mockCalendarEvents } from '../../../mocks/mockMaintenanceCalendarEvent'
|
||||
import NewCalendarEventModal from './NewCalendarEventModal'
|
||||
import {
|
||||
getPriorityColor,
|
||||
getWorkOrderStatusColor,
|
||||
getWorkOrderStatusIcon,
|
||||
} from "../../../utils/erp";
|
||||
} from '../../../utils/erp'
|
||||
import { Container } from '@/components/shared'
|
||||
|
||||
const MaintenanceCalendar: React.FC = () => {
|
||||
const [currentDate, setCurrentDate] = useState(new Date());
|
||||
const [view, setView] = useState<CalendarView>("month");
|
||||
const [showEventDetailModal, setShowEventDetailModal] = useState(false);
|
||||
const [showNewEventModal, setShowNewEventModal] = useState(false);
|
||||
const [selectedEvent, setSelectedEvent] = useState<PmCalendarEvent | null>(
|
||||
null
|
||||
);
|
||||
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
||||
const [statusFilter, setStatusFilter] = useState<"all" | WorkOrderStatusEnum>(
|
||||
"all"
|
||||
);
|
||||
const [currentDate, setCurrentDate] = useState(new Date())
|
||||
const [view, setView] = useState<CalendarView>('month')
|
||||
const [showEventDetailModal, setShowEventDetailModal] = useState(false)
|
||||
const [showNewEventModal, setShowNewEventModal] = useState(false)
|
||||
const [selectedEvent, setSelectedEvent] = useState<PmCalendarEvent | null>(null)
|
||||
const [selectedDate, setSelectedDate] = useState<Date | null>(null)
|
||||
const [statusFilter, setStatusFilter] = useState<'all' | WorkOrderStatusEnum>('all')
|
||||
|
||||
// Mock data - replace with actual API calls
|
||||
const [events, setEvents] = useState<PmCalendarEvent[]>(mockCalendarEvents);
|
||||
const [events, setEvents] = useState<PmCalendarEvent[]>(mockCalendarEvents)
|
||||
|
||||
const getDaysInMonth = (date: Date) => {
|
||||
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
|
||||
};
|
||||
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate()
|
||||
}
|
||||
|
||||
const getFirstDayOfMonth = (date: Date) => {
|
||||
return new Date(date.getFullYear(), date.getMonth(), 1).getDay();
|
||||
};
|
||||
return new Date(date.getFullYear(), date.getMonth(), 1).getDay()
|
||||
}
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
return date.toLocaleDateString("tr-TR", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
});
|
||||
};
|
||||
return date.toLocaleDateString('tr-TR', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
})
|
||||
}
|
||||
|
||||
const getEventsForDate = (date: Date) => {
|
||||
return events.filter((event) => {
|
||||
const eventDate = new Date(event.date);
|
||||
return eventDate.toDateString() === date.toDateString();
|
||||
});
|
||||
};
|
||||
const eventDate = new Date(event.date)
|
||||
return eventDate.toDateString() === date.toDateString()
|
||||
})
|
||||
}
|
||||
|
||||
const navigateMonth = (direction: "prev" | "next") => {
|
||||
const newDate = new Date(currentDate);
|
||||
if (direction === "prev") {
|
||||
newDate.setMonth(newDate.getMonth() - 1);
|
||||
const navigateMonth = (direction: 'prev' | 'next') => {
|
||||
const newDate = new Date(currentDate)
|
||||
if (direction === 'prev') {
|
||||
newDate.setMonth(newDate.getMonth() - 1)
|
||||
} else {
|
||||
newDate.setMonth(newDate.getMonth() + 1);
|
||||
newDate.setMonth(newDate.getMonth() + 1)
|
||||
}
|
||||
setCurrentDate(newDate);
|
||||
};
|
||||
setCurrentDate(newDate)
|
||||
}
|
||||
|
||||
const handleEventClick = (event: PmCalendarEvent) => {
|
||||
setSelectedEvent(event);
|
||||
setShowEventDetailModal(true);
|
||||
};
|
||||
setSelectedEvent(event)
|
||||
setShowEventDetailModal(true)
|
||||
}
|
||||
|
||||
const handleDayClick = (date: Date) => {
|
||||
setSelectedDate(date);
|
||||
setShowNewEventModal(true);
|
||||
};
|
||||
setSelectedDate(date)
|
||||
setShowNewEventModal(true)
|
||||
}
|
||||
|
||||
const handleNewEventSave = (newEvent: Partial<PmCalendarEvent>) => {
|
||||
if (newEvent.id) {
|
||||
setEvents((prevEvents) => [...prevEvents, newEvent as PmCalendarEvent]);
|
||||
setEvents((prevEvents) => [...prevEvents, newEvent as PmCalendarEvent])
|
||||
}
|
||||
setShowNewEventModal(false);
|
||||
setSelectedDate(null);
|
||||
};
|
||||
setShowNewEventModal(false)
|
||||
setSelectedDate(null)
|
||||
}
|
||||
|
||||
const renderMonthView = () => {
|
||||
const daysInMonth = getDaysInMonth(currentDate);
|
||||
const firstDay = getFirstDayOfMonth(currentDate);
|
||||
const days = [];
|
||||
const daysInMonth = getDaysInMonth(currentDate)
|
||||
const firstDay = getFirstDayOfMonth(currentDate)
|
||||
const days = []
|
||||
|
||||
// Empty cells for days before the first day of the month
|
||||
for (let i = 0; i < firstDay; i++) {
|
||||
days.push(
|
||||
<div key={`empty-${i}`} className="p-1 border border-gray-200"></div>
|
||||
);
|
||||
days.push(<div key={`empty-${i}`} className="p-1 border border-gray-200"></div>)
|
||||
}
|
||||
|
||||
// Days of the month
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
const date = new Date(
|
||||
currentDate.getFullYear(),
|
||||
currentDate.getMonth(),
|
||||
day
|
||||
);
|
||||
const dayEvents = getEventsForDate(date);
|
||||
const date = new Date(currentDate.getFullYear(), currentDate.getMonth(), day)
|
||||
const dayEvents = getEventsForDate(date)
|
||||
const filteredDayEvents = dayEvents.filter(
|
||||
(event) => statusFilter === "all" || event.status === statusFilter
|
||||
);
|
||||
(event) => statusFilter === 'all' || event.status === statusFilter,
|
||||
)
|
||||
|
||||
const isToday = date.toDateString() === new Date().toDateString();
|
||||
const isToday = date.toDateString() === new Date().toDateString()
|
||||
|
||||
days.push(
|
||||
<div
|
||||
key={day}
|
||||
className={`p-1.5 border border-gray-200 min-h-[100px] cursor-pointer hover:bg-gray-50 ${
|
||||
isToday ? "bg-blue-50" : "bg-white"
|
||||
isToday ? 'bg-blue-50' : 'bg-white'
|
||||
}`}
|
||||
onClick={() => handleDayClick(date)}
|
||||
>
|
||||
<div
|
||||
className={`text-xs font-medium mb-1 ${
|
||||
isToday ? "text-blue-600" : "text-gray-900"
|
||||
}`}
|
||||
className={`text-xs font-medium mb-1 ${isToday ? 'text-blue-600' : 'text-gray-900'}`}
|
||||
>
|
||||
{day}
|
||||
</div>
|
||||
|
|
@ -134,11 +119,11 @@ const MaintenanceCalendar: React.FC = () => {
|
|||
<div
|
||||
key={event.id}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleEventClick(event);
|
||||
e.stopPropagation()
|
||||
handleEventClick(event)
|
||||
}}
|
||||
className={`text-xs p-0.5 rounded border-l-2 cursor-pointer hover:bg-gray-50 ${getPriorityColor(
|
||||
event.priority
|
||||
event.priority,
|
||||
)} ${getWorkOrderStatusColor(event.status)}`}
|
||||
>
|
||||
<div className="font-medium truncate">{event.title}</div>
|
||||
|
|
@ -146,9 +131,7 @@ const MaintenanceCalendar: React.FC = () => {
|
|||
{getWorkOrderStatusIcon(event.status)}
|
||||
<span>{event.startTime}</span>
|
||||
{event.workCenterCode && (
|
||||
<span className="text-gray-500">
|
||||
({event.workCenterCode})
|
||||
</span>
|
||||
<span className="text-gray-500">({event.workCenterCode})</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -159,14 +142,14 @@ const MaintenanceCalendar: React.FC = () => {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
</div>,
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-7 gap-0 bg-white rounded-lg shadow overflow-hidden">
|
||||
{/* Header */}
|
||||
{["Pzt", "Sal", "Çar", "Per", "Cum", "Cmt", "Paz"].map((day) => (
|
||||
{['Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt', 'Paz'].map((day) => (
|
||||
<div
|
||||
key={day}
|
||||
className="p-2 bg-gray-50 text-center text-xs font-medium text-gray-700 border-r border-gray-200 last:border-r-0"
|
||||
|
|
@ -176,261 +159,233 @@ const MaintenanceCalendar: React.FC = () => {
|
|||
))}
|
||||
{days}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
const renderWeekView = () => {
|
||||
const startOfWeek = new Date(currentDate);
|
||||
startOfWeek.setDate(currentDate.getDate() - currentDate.getDay() + 1); // Start from Monday
|
||||
const startOfWeek = new Date(currentDate)
|
||||
startOfWeek.setDate(currentDate.getDate() - currentDate.getDay() + 1) // Start from Monday
|
||||
|
||||
const weekDays: Date[] = [];
|
||||
const weekDays: Date[] = []
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const date = new Date(startOfWeek);
|
||||
date.setDate(startOfWeek.getDate() + i);
|
||||
weekDays.push(date);
|
||||
const date = new Date(startOfWeek)
|
||||
date.setDate(startOfWeek.getDate() + i)
|
||||
weekDays.push(date)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||
<div className="grid grid-cols-8 border-b border-gray-200">
|
||||
<div className="p-2 bg-gray-50 text-xs font-medium text-gray-700">
|
||||
Saat
|
||||
</div>
|
||||
<div className="p-2 bg-gray-50 text-xs font-medium text-gray-700">Saat</div>
|
||||
{weekDays.map((date, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="p-2 bg-gray-50 text-center border-l border-gray-200"
|
||||
>
|
||||
<div key={index} className="p-2 bg-gray-50 text-center border-l border-gray-200">
|
||||
<div className="text-sm font-medium text-gray-700">
|
||||
{date.toLocaleDateString("tr-TR", { weekday: "short" })}
|
||||
</div>
|
||||
<div className="text-lg font-bold text-gray-900">
|
||||
{date.getDate()}
|
||||
{date.toLocaleDateString('tr-TR', { weekday: 'short' })}
|
||||
</div>
|
||||
<div className="text-lg font-bold text-gray-900">{date.getDate()}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="max-h-96 overflow-y-auto">
|
||||
{Array.from({ length: 12 }, (_, hour) => hour + 8).map((hour) => (
|
||||
<div
|
||||
key={hour}
|
||||
className="grid grid-cols-8 border-b border-gray-100"
|
||||
>
|
||||
<div key={hour} className="grid grid-cols-8 border-b border-gray-100">
|
||||
<div className="p-1.5 text-xs text-gray-500 bg-gray-50 border-r border-gray-200">
|
||||
{hour}:00
|
||||
</div>
|
||||
{weekDays.map((date, dayIndex) => {
|
||||
const dayEvents = getEventsForDate(date).filter((event) => {
|
||||
const eventHour = parseInt(
|
||||
event.startTime?.split(":")[0] || "0"
|
||||
);
|
||||
const eventHour = parseInt(event.startTime?.split(':')[0] || '0')
|
||||
return (
|
||||
eventHour === hour &&
|
||||
(statusFilter === "all" || event.status === statusFilter)
|
||||
);
|
||||
});
|
||||
eventHour === hour && (statusFilter === 'all' || event.status === statusFilter)
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
key={dayIndex}
|
||||
className="p-1 border-l border-gray-200 min-h-[50px] cursor-pointer hover:bg-gray-50"
|
||||
onClick={() => {
|
||||
const selectedDateTime = new Date(date);
|
||||
selectedDateTime.setHours(hour, 0, 0, 0);
|
||||
handleDayClick(selectedDateTime);
|
||||
const selectedDateTime = new Date(date)
|
||||
selectedDateTime.setHours(hour, 0, 0, 0)
|
||||
handleDayClick(selectedDateTime)
|
||||
}}
|
||||
>
|
||||
{dayEvents.map((event) => (
|
||||
<div
|
||||
key={event.id}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleEventClick(event);
|
||||
e.stopPropagation()
|
||||
handleEventClick(event)
|
||||
}}
|
||||
className={`text-xs p-1 rounded mb-1 border-l-2 cursor-pointer hover:bg-gray-50 ${getPriorityColor(
|
||||
event.priority
|
||||
event.priority,
|
||||
)} ${getWorkOrderStatusColor(event.status)}`}
|
||||
>
|
||||
<div className="font-medium truncate">
|
||||
{event.title}
|
||||
</div>
|
||||
<div className="font-medium truncate">{event.title}</div>
|
||||
<div className="text-gray-500">
|
||||
{event.startTime}-{event.endTime}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
const getTodayEvents = () => {
|
||||
const today = new Date();
|
||||
const today = new Date()
|
||||
return getEventsForDate(today).filter(
|
||||
(event) => statusFilter === "all" || event.status === statusFilter
|
||||
);
|
||||
};
|
||||
(event) => statusFilter === 'all' || event.status === statusFilter,
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4 pt-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Bakım Takvimi</h2>
|
||||
<p className="text-gray-600">
|
||||
Bakım planları ve iş emirlerini takip edin. Yeni planlama için
|
||||
gün/saat seçin.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowNewEventModal(true)}
|
||||
className="bg-blue-600 text-white px-3 py-1.5 rounded-lg hover:bg-blue-700 flex items-center space-x-2 text-sm"
|
||||
>
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Planlama</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Calendar Controls */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="flex items-center space-x-1">
|
||||
<button
|
||||
onClick={() => navigateMonth("prev")}
|
||||
className="p-1.5 hover:bg-gray-100 rounded-md"
|
||||
>
|
||||
<FaChevronLeft className="w-4 h-4" />
|
||||
</button>
|
||||
<h3 className="text-base font-semibold text-gray-900 min-w-[180px] text-center">
|
||||
{formatDate(currentDate)}
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => navigateMonth("next")}
|
||||
className="p-1.5 hover:bg-gray-100 rounded-md"
|
||||
>
|
||||
<FaChevronRight className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-1 bg-gray-100 rounded-lg p-1">
|
||||
{(["month", "week"] as CalendarView[]).map((viewType) => (
|
||||
<button
|
||||
key={viewType}
|
||||
onClick={() => setView(viewType)}
|
||||
className={`px-3 py-1 rounded-md text-sm font-medium transition-colors ${
|
||||
view === viewType
|
||||
? "bg-white text-gray-900 shadow-sm"
|
||||
: "text-gray-600 hover:text-gray-900"
|
||||
}`}
|
||||
>
|
||||
{viewType === "month" ? "Ay" : "Hafta"}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="relative">
|
||||
<FaFilter className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) =>
|
||||
setStatusFilter(e.target.value as "all" | WorkOrderStatusEnum)
|
||||
}
|
||||
className="pl-9 pr-4 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"
|
||||
>
|
||||
<option value="all">Tüm Durumlar</option>
|
||||
<option value="scheduled">Planlanmış</option>
|
||||
<option value={WorkOrderStatusEnum.Created}>Oluşturuldu</option>
|
||||
<option value={WorkOrderStatusEnum.InProgress}>
|
||||
Devam Ediyor
|
||||
</option>
|
||||
<option value={WorkOrderStatusEnum.Completed}>Tamamlandı</option>
|
||||
</select>
|
||||
<Container>
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Bakım Takvimi</h2>
|
||||
<p className="text-gray-600">
|
||||
Bakım planları ve iş emirlerini takip edin. Yeni planlama için gün/saat seçin.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setCurrentDate(new Date())}
|
||||
className="px-3 py-1.5 text-sm text-blue-600 hover:bg-blue-50 rounded-lg"
|
||||
onClick={() => setShowNewEventModal(true)}
|
||||
className="bg-blue-600 text-white px-3 py-1.5 rounded-lg hover:bg-blue-700 flex items-center space-x-2 text-sm"
|
||||
>
|
||||
Bugün
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Planlama</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Calendar View */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4">
|
||||
<div className="lg:col-span-3">
|
||||
{view === "month" && renderMonthView()}
|
||||
{view === "week" && renderWeekView()}
|
||||
{/* Calendar Controls */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="flex items-center space-x-1">
|
||||
<button
|
||||
onClick={() => navigateMonth('prev')}
|
||||
className="p-1.5 hover:bg-gray-100 rounded-md"
|
||||
>
|
||||
<FaChevronLeft className="w-4 h-4" />
|
||||
</button>
|
||||
<h3 className="text-base font-semibold text-gray-900 min-w-[180px] text-center">
|
||||
{formatDate(currentDate)}
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => navigateMonth('next')}
|
||||
className="p-1.5 hover:bg-gray-100 rounded-md"
|
||||
>
|
||||
<FaChevronRight className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-1 bg-gray-100 rounded-lg p-1">
|
||||
{(['month', 'week'] as CalendarView[]).map((viewType) => (
|
||||
<button
|
||||
key={viewType}
|
||||
onClick={() => setView(viewType)}
|
||||
className={`px-3 py-1 rounded-md text-sm font-medium transition-colors ${
|
||||
view === viewType
|
||||
? 'bg-white text-gray-900 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
{viewType === 'month' ? 'Ay' : 'Hafta'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="relative">
|
||||
<FaFilter className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value as 'all' | WorkOrderStatusEnum)}
|
||||
className="pl-9 pr-4 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"
|
||||
>
|
||||
<option value="all">Tüm Durumlar</option>
|
||||
<option value="scheduled">Planlanmış</option>
|
||||
<option value={WorkOrderStatusEnum.Created}>Oluşturuldu</option>
|
||||
<option value={WorkOrderStatusEnum.InProgress}>Devam Ediyor</option>
|
||||
<option value={WorkOrderStatusEnum.Completed}>Tamamlandı</option>
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setCurrentDate(new Date())}
|
||||
className="px-3 py-1.5 text-sm text-blue-600 hover:bg-blue-50 rounded-lg"
|
||||
>
|
||||
Bugün
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Today's Events Sidebar */}
|
||||
<div className="bg-white rounded-lg shadow p-4">
|
||||
<h4 className="text-base font-semibold text-gray-900 mb-3">
|
||||
Bugünün Etkinlikleri
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
{getTodayEvents().map((event) => (
|
||||
<div
|
||||
key={event.id}
|
||||
onClick={() => handleEventClick(event)}
|
||||
className={`p-2 border-l-4 rounded-r-lg cursor-pointer hover:bg-gray-50 ${getPriorityColor(
|
||||
event.priority
|
||||
)}`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<h5 className="text-sm font-medium text-gray-900">
|
||||
{event.title}
|
||||
</h5>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
{event.workCenterCode}
|
||||
</p>
|
||||
<div className="flex items-center space-x-2 mt-2">
|
||||
<FaClock className="w-3 h-3 text-gray-400" />
|
||||
<span className="text-xs text-gray-500">
|
||||
{event.startTime} - {event.endTime}
|
||||
</span>
|
||||
</div>
|
||||
{event.assignedTo && (
|
||||
<div className="flex items-center space-x-2 mt-1">
|
||||
<FaUser className="w-3 h-3 text-gray-400" />
|
||||
{/* Calendar View */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4">
|
||||
<div className="lg:col-span-3">
|
||||
{view === 'month' && renderMonthView()}
|
||||
{view === 'week' && renderWeekView()}
|
||||
</div>
|
||||
|
||||
{/* Today's Events Sidebar */}
|
||||
<div className="bg-white rounded-lg shadow p-4">
|
||||
<h4 className="text-base font-semibold text-gray-900 mb-3">Bugünün Etkinlikleri</h4>
|
||||
<div className="space-y-3">
|
||||
{getTodayEvents().map((event) => (
|
||||
<div
|
||||
key={event.id}
|
||||
onClick={() => handleEventClick(event)}
|
||||
className={`p-2 border-l-4 rounded-r-lg cursor-pointer hover:bg-gray-50 ${getPriorityColor(
|
||||
event.priority,
|
||||
)}`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<h5 className="text-sm font-medium text-gray-900">{event.title}</h5>
|
||||
<p className="text-xs text-gray-500 mt-1">{event.workCenterCode}</p>
|
||||
<div className="flex items-center space-x-2 mt-2">
|
||||
<FaClock className="w-3 h-3 text-gray-400" />
|
||||
<span className="text-xs text-gray-500">
|
||||
{event.assignedTo}
|
||||
{event.startTime} - {event.endTime}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{event.assignedTo && (
|
||||
<div className="flex items-center space-x-2 mt-1">
|
||||
<FaUser className="w-3 h-3 text-gray-400" />
|
||||
<span className="text-xs text-gray-500">{event.assignedTo}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
className={`px-2 py-1 text-xs font-semibold rounded-full ${getWorkOrderStatusColor(
|
||||
event.status,
|
||||
)}`}
|
||||
>
|
||||
{event.status === WorkOrderStatusEnum.Planned
|
||||
? 'Planlandı'
|
||||
: event.status === WorkOrderStatusEnum.InProgress
|
||||
? 'Devam Ediyor'
|
||||
: event.status === WorkOrderStatusEnum.Completed
|
||||
? 'Tamamlandı'
|
||||
: 'Bekliyor'}
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
className={`px-2 py-1 text-xs font-semibold rounded-full ${getWorkOrderStatusColor(
|
||||
event.status
|
||||
)}`}
|
||||
>
|
||||
{event.status === WorkOrderStatusEnum.Planned
|
||||
? "Planlandı"
|
||||
: event.status === WorkOrderStatusEnum.InProgress
|
||||
? "Devam Ediyor"
|
||||
: event.status === WorkOrderStatusEnum.Completed
|
||||
? "Tamamlandı"
|
||||
: "Bekliyor"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{getTodayEvents().length === 0 && (
|
||||
<div className="text-center py-8">
|
||||
<FaCalendar className="w-10 h-10 text-gray-400 mx-auto mb-2" />
|
||||
<p className="text-sm text-gray-600">
|
||||
Bugün için planlanan etkinlik yok
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
{getTodayEvents().length === 0 && (
|
||||
<div className="text-center py-8">
|
||||
<FaCalendar className="w-10 h-10 text-gray-400 mx-auto mb-2" />
|
||||
<p className="text-sm text-gray-600">Bugün için planlanan etkinlik yok</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -441,12 +396,8 @@ const MaintenanceCalendar: React.FC = () => {
|
|||
<div className="bg-white rounded-lg p-4 w-full max-w-lg mx-4">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-gray-900">
|
||||
{selectedEvent.title}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
{selectedEvent.workCenterCode}
|
||||
</p>
|
||||
<h3 className="text-base font-semibold text-gray-900">{selectedEvent.title}</h3>
|
||||
<p className="text-sm text-gray-500 mt-1">{selectedEvent.workCenterCode}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowEventDetailModal(false)}
|
||||
|
|
@ -458,46 +409,34 @@ const MaintenanceCalendar: React.FC = () => {
|
|||
|
||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Tarih & Saat
|
||||
</label>
|
||||
<label className="text-sm font-medium text-gray-500">Tarih & Saat</label>
|
||||
<p className="text-sm text-gray-900">
|
||||
{selectedEvent.date.toLocaleDateString("tr-TR")} -{" "}
|
||||
{selectedEvent.startTime} / {selectedEvent.endTime}
|
||||
{selectedEvent.date.toLocaleDateString('tr-TR')} - {selectedEvent.startTime} /{' '}
|
||||
{selectedEvent.endTime}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Süre
|
||||
</label>
|
||||
<p className="text-sm text-gray-900">
|
||||
{selectedEvent.duration} dakika
|
||||
</p>
|
||||
<label className="text-sm font-medium text-gray-500">Süre</label>
|
||||
<p className="text-sm text-gray-900">{selectedEvent.duration} dakika</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Atanan Kişi
|
||||
</label>
|
||||
<p className="text-sm text-gray-900">
|
||||
{selectedEvent.assignedTo || "Atanmadı"}
|
||||
</p>
|
||||
<label className="text-sm font-medium text-gray-500">Atanan Kişi</label>
|
||||
<p className="text-sm text-gray-900">{selectedEvent.assignedTo || 'Atanmadı'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Durum
|
||||
</label>
|
||||
<label className="text-sm font-medium text-gray-500">Durum</label>
|
||||
<span
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getWorkOrderStatusColor(
|
||||
selectedEvent.status
|
||||
selectedEvent.status,
|
||||
)}`}
|
||||
>
|
||||
{selectedEvent.status === WorkOrderStatusEnum.Planned
|
||||
? "Planlandı"
|
||||
? 'Planlandı'
|
||||
: selectedEvent.status === WorkOrderStatusEnum.InProgress
|
||||
? "Devam Ediyor"
|
||||
: selectedEvent.status === WorkOrderStatusEnum.Completed
|
||||
? "Tamamlandı"
|
||||
: "Bekliyor"}
|
||||
? 'Devam Ediyor'
|
||||
: selectedEvent.status === WorkOrderStatusEnum.Completed
|
||||
? 'Tamamlandı'
|
||||
: 'Bekliyor'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -522,14 +461,14 @@ const MaintenanceCalendar: React.FC = () => {
|
|||
<NewCalendarEventModal
|
||||
isOpen={showNewEventModal}
|
||||
onClose={() => {
|
||||
setShowNewEventModal(false);
|
||||
setSelectedDate(null);
|
||||
setShowNewEventModal(false)
|
||||
setSelectedDate(null)
|
||||
}}
|
||||
onSave={handleNewEventSave}
|
||||
selectedDate={selectedDate || undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default MaintenanceCalendar;
|
||||
export default MaintenanceCalendar
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaSave, FaPlus } from "react-icons/fa";
|
||||
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";
|
||||
} from '../../../types/pm'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
|
||||
interface MaintenancePlanModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (planData: PmMaintenancePlan[]) => void;
|
||||
selectedWorkCenters: PmWorkCenter[];
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (planData: PmMaintenancePlan[]) => void
|
||||
selectedWorkCenters: PmWorkCenter[]
|
||||
}
|
||||
|
||||
const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
||||
|
|
@ -23,55 +23,46 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
|||
}) => {
|
||||
const [planData, setPlanData] = useState({
|
||||
planType: MaintenancePlanTypeEnum.Preventive,
|
||||
description: "",
|
||||
description: '',
|
||||
frequency: 1,
|
||||
frequencyUnit: FrequencyUnitEnum.Months,
|
||||
estimatedDuration: 60,
|
||||
priority: PriorityEnum.Normal,
|
||||
instructions: "",
|
||||
instructions: '',
|
||||
requiredSkills: [] as string[],
|
||||
nextDue: new Date(),
|
||||
});
|
||||
})
|
||||
|
||||
const [newSkill, setNewSkill] = useState("");
|
||||
const [newSkill, setNewSkill] = useState('')
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<
|
||||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
||||
>
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
const { name, value, type } = e.target;
|
||||
const { name, value, type } = e.target
|
||||
setPlanData((prev) => ({
|
||||
...prev,
|
||||
[name]:
|
||||
type === "date"
|
||||
? new Date(value)
|
||||
: type === "number"
|
||||
? Number(value)
|
||||
: value,
|
||||
}));
|
||||
};
|
||||
[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("");
|
||||
}))
|
||||
setNewSkill('')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const removeSkill = (skillToRemove: string) => {
|
||||
setPlanData((prev) => ({
|
||||
...prev,
|
||||
requiredSkills: prev.requiredSkills.filter(
|
||||
(skill) => skill !== skillToRemove
|
||||
),
|
||||
}));
|
||||
};
|
||||
requiredSkills: prev.requiredSkills.filter((skill) => skill !== skillToRemove),
|
||||
}))
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
const plansToCreate = selectedWorkCenters.map((workCenter) => ({
|
||||
|
|
@ -84,11 +75,11 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
|||
isActive: true,
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
}));
|
||||
}))
|
||||
|
||||
onSave(plansToCreate);
|
||||
onClose();
|
||||
};
|
||||
onSave(plansToCreate)
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -98,10 +89,7 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
|||
<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"
|
||||
>
|
||||
<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>
|
||||
|
|
@ -110,21 +98,14 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
|||
<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>
|
||||
<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"
|
||||
>
|
||||
<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>
|
||||
<span className="text-xs text-gray-500">{workCenter.location}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -132,38 +113,24 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
|||
|
||||
{/* Plan Details */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||||
Plan Detayları
|
||||
</h3>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Öncelik</label>
|
||||
<select
|
||||
name="priority"
|
||||
value={planData.priority}
|
||||
|
|
@ -191,7 +158,7 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
|||
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()}
|
||||
onKeyPress={(e) => e.key === 'Enter' && addSkill()}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -241,7 +208,7 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default MaintenancePlanModal;
|
||||
export default MaintenancePlanModal
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useState } from 'react'
|
||||
import {
|
||||
FaPlus,
|
||||
FaSearch,
|
||||
|
|
@ -10,489 +10,442 @@ import {
|
|||
FaTrash,
|
||||
FaEye,
|
||||
FaTasks,
|
||||
} from "react-icons/fa";
|
||||
} from 'react-icons/fa'
|
||||
import {
|
||||
PmMaintenancePlan,
|
||||
MaintenancePlanTypeEnum,
|
||||
PmMaintenanceWorkOrder,
|
||||
} from "../../../types/pm";
|
||||
import { mockMaintenancePlans } from "../../../mocks/mockMaintenancePlans";
|
||||
import NewMaintenancePlanModal from "./NewMaintenancePlanModal";
|
||||
import ViewMaintenancePlanModal from "./ViewMaintenancePlanModal";
|
||||
import EditMaintenancePlanModal from "./EditMaintenancePlanModal";
|
||||
import CreateWorkOrderModal from "./CreateWorkOrderModal";
|
||||
import PlanStatusChangeModal from "./PlanStatusChangeModal";
|
||||
import Widget from "../../../components/common/Widget";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
} from '../../../types/pm'
|
||||
import { mockMaintenancePlans } from '../../../mocks/mockMaintenancePlans'
|
||||
import NewMaintenancePlanModal from './NewMaintenancePlanModal'
|
||||
import ViewMaintenancePlanModal from './ViewMaintenancePlanModal'
|
||||
import EditMaintenancePlanModal from './EditMaintenancePlanModal'
|
||||
import CreateWorkOrderModal from './CreateWorkOrderModal'
|
||||
import PlanStatusChangeModal from './PlanStatusChangeModal'
|
||||
import Widget from '../../../components/common/Widget'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
import {
|
||||
getFrequencyUnitText,
|
||||
getMaintenancePlanTypeColor,
|
||||
getMaintenancePlanTypeText,
|
||||
getPriorityColor,
|
||||
} from "../../../utils/erp";
|
||||
} from '../../../utils/erp'
|
||||
import { Container } from '@/components/shared'
|
||||
|
||||
const MaintenancePlans: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [typeFilter, setTypeFilter] = useState<MaintenancePlanTypeEnum | "all">(
|
||||
"all"
|
||||
);
|
||||
const [statusFilter, setStatusFilter] = useState<
|
||||
"active" | "inactive" | "all"
|
||||
>("all");
|
||||
const [editingPlan, setEditingPlan] = useState<PmMaintenancePlan | null>(
|
||||
null
|
||||
);
|
||||
const [selectedPlans, setSelectedPlans] = useState<string[]>([]);
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const [typeFilter, setTypeFilter] = useState<MaintenancePlanTypeEnum | 'all'>('all')
|
||||
const [statusFilter, setStatusFilter] = useState<'active' | 'inactive' | 'all'>('all')
|
||||
const [editingPlan, setEditingPlan] = useState<PmMaintenancePlan | null>(null)
|
||||
const [selectedPlans, setSelectedPlans] = useState<string[]>([])
|
||||
|
||||
// Modal states
|
||||
const [showNewModal, setShowNewModal] = useState(false);
|
||||
const [showViewModal, setShowViewModal] = useState(false);
|
||||
const [showEditModal, setShowEditModal] = useState(false);
|
||||
const [showWorkOrderModal, setShowWorkOrderModal] = useState(false);
|
||||
const [showStatusModal, setShowStatusModal] = useState(false);
|
||||
const [viewingPlan, setViewingPlan] = useState<PmMaintenancePlan | null>(
|
||||
null
|
||||
);
|
||||
const [showNewModal, setShowNewModal] = useState(false)
|
||||
const [showViewModal, setShowViewModal] = useState(false)
|
||||
const [showEditModal, setShowEditModal] = useState(false)
|
||||
const [showWorkOrderModal, setShowWorkOrderModal] = useState(false)
|
||||
const [showStatusModal, setShowStatusModal] = useState(false)
|
||||
const [viewingPlan, setViewingPlan] = useState<PmMaintenancePlan | null>(null)
|
||||
|
||||
// Mock data - replace with actual API calls
|
||||
const [plans] = useState<PmMaintenancePlan[]>(mockMaintenancePlans);
|
||||
const [plans] = useState<PmMaintenancePlan[]>(mockMaintenancePlans)
|
||||
|
||||
const filteredPlans = plans.filter((plan) => {
|
||||
const matchesSearch =
|
||||
plan.planCode.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
plan.description.toLowerCase().includes(searchTerm.toLowerCase());
|
||||
const matchesType = typeFilter === "all" || plan.planType === typeFilter;
|
||||
plan.description.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
const matchesType = typeFilter === 'all' || plan.planType === typeFilter
|
||||
const matchesStatus =
|
||||
statusFilter === "all" ||
|
||||
(statusFilter === "active" && plan.isActive) ||
|
||||
(statusFilter === "inactive" && !plan.isActive);
|
||||
return matchesSearch && matchesType && matchesStatus;
|
||||
});
|
||||
statusFilter === 'all' ||
|
||||
(statusFilter === 'active' && plan.isActive) ||
|
||||
(statusFilter === 'inactive' && !plan.isActive)
|
||||
return matchesSearch && matchesType && matchesStatus
|
||||
})
|
||||
|
||||
const isOverdue = (plan: PmMaintenancePlan) => {
|
||||
if (!plan.nextDue) return false;
|
||||
return plan.nextDue < new Date();
|
||||
};
|
||||
if (!plan.nextDue) return false
|
||||
return plan.nextDue < new Date()
|
||||
}
|
||||
|
||||
const isDueSoon = (plan: PmMaintenancePlan) => {
|
||||
if (!plan.nextDue) return false;
|
||||
const today = new Date();
|
||||
const diffTime = plan.nextDue.getTime() - today.getTime();
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||
return diffDays <= 7 && diffDays > 0;
|
||||
};
|
||||
if (!plan.nextDue) return false
|
||||
const today = new Date()
|
||||
const diffTime = plan.nextDue.getTime() - today.getTime()
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
||||
return diffDays <= 7 && diffDays > 0
|
||||
}
|
||||
|
||||
const getTotalMaterialCost = (plan: PmMaintenancePlan) => {
|
||||
return plan.priority;
|
||||
};
|
||||
return plan.priority
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
const handleAddPlan = () => {
|
||||
setShowNewModal(true);
|
||||
};
|
||||
setShowNewModal(true)
|
||||
}
|
||||
|
||||
const handleEdit = (plan: PmMaintenancePlan) => {
|
||||
setEditingPlan(plan);
|
||||
setShowEditModal(true);
|
||||
};
|
||||
setEditingPlan(plan)
|
||||
setShowEditModal(true)
|
||||
}
|
||||
|
||||
const handleView = (plan: PmMaintenancePlan) => {
|
||||
setViewingPlan(plan);
|
||||
setShowViewModal(true);
|
||||
};
|
||||
setViewingPlan(plan)
|
||||
setShowViewModal(true)
|
||||
}
|
||||
|
||||
const handleSelectPlan = (planId: string) => {
|
||||
setSelectedPlans((prev) =>
|
||||
prev.includes(planId)
|
||||
? prev.filter((id) => id !== planId)
|
||||
: [...prev, planId]
|
||||
);
|
||||
};
|
||||
prev.includes(planId) ? prev.filter((id) => id !== planId) : [...prev, planId],
|
||||
)
|
||||
}
|
||||
|
||||
const handleCreateWorkOrder = () => {
|
||||
setShowWorkOrderModal(true);
|
||||
};
|
||||
setShowWorkOrderModal(true)
|
||||
}
|
||||
|
||||
const handleStatusChange = () => {
|
||||
setShowStatusModal(true);
|
||||
};
|
||||
setShowStatusModal(true)
|
||||
}
|
||||
|
||||
// Modal handlers
|
||||
const handleSaveNewPlan = (newPlan: Partial<PmMaintenancePlan>) => {
|
||||
console.log("New plan:", newPlan);
|
||||
console.log('New plan:', newPlan)
|
||||
// TODO: API call to save new plan
|
||||
};
|
||||
}
|
||||
|
||||
const handleSaveEditPlan = (updatedPlan: PmMaintenancePlan) => {
|
||||
console.log("Updated plan:", updatedPlan);
|
||||
console.log('Updated plan:', updatedPlan)
|
||||
// TODO: API call to update plan
|
||||
};
|
||||
}
|
||||
|
||||
const handleSaveWorkOrders = (
|
||||
workOrders: Partial<PmMaintenanceWorkOrder>[]
|
||||
) => {
|
||||
console.log("Work orders:", workOrders);
|
||||
const handleSaveWorkOrders = (workOrders: Partial<PmMaintenanceWorkOrder>[]) => {
|
||||
console.log('Work orders:', workOrders)
|
||||
// TODO: API call to create work orders
|
||||
setSelectedPlans([]);
|
||||
};
|
||||
setSelectedPlans([])
|
||||
}
|
||||
|
||||
const handleSaveStatusChange = (
|
||||
planIds: string[],
|
||||
isActive: boolean,
|
||||
reason: string
|
||||
) => {
|
||||
console.log("Status change:", { planIds, isActive, reason });
|
||||
const handleSaveStatusChange = (planIds: string[], isActive: boolean, reason: string) => {
|
||||
console.log('Status change:', { planIds, isActive, reason })
|
||||
// TODO: API call to update plan statuses
|
||||
setSelectedPlans([]);
|
||||
};
|
||||
setSelectedPlans([])
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4 pt-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Bakım Planları</h2>
|
||||
<p className="text-gray-600">
|
||||
Periyodik ve düzeltici bakım planlarını yönetin
|
||||
</p>
|
||||
<Container>
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Bakım Planları</h2>
|
||||
<p className="text-gray-600">Periyodik ve düzeltici bakım planlarını yönetin</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAddPlan}
|
||||
className="bg-blue-600 text-white px-3 py-1.5 rounded-lg hover:bg-blue-700 flex items-center space-x-2 text-sm"
|
||||
>
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Plan</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAddPlan}
|
||||
className="bg-blue-600 text-white px-3 py-1.5 rounded-lg hover:bg-blue-700 flex items-center space-x-2 text-sm"
|
||||
>
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Plan</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Summary Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
<Widget
|
||||
title="Toplam Plan"
|
||||
value={plans.length}
|
||||
color="blue"
|
||||
icon="FaCalendar"
|
||||
/>
|
||||
{/* Summary Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
<Widget title="Toplam Plan" value={plans.length} color="blue" icon="FaCalendar" />
|
||||
|
||||
<Widget
|
||||
title="Aktif Plan"
|
||||
value={plans.filter((p) => p.isActive).length}
|
||||
color="green"
|
||||
icon="FaCheckCircle"
|
||||
/>
|
||||
<Widget
|
||||
title="Aktif Plan"
|
||||
value={plans.filter((p) => p.isActive).length}
|
||||
color="green"
|
||||
icon="FaCheckCircle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Geciken"
|
||||
value={plans.filter((p) => isOverdue(p)).length}
|
||||
color="red"
|
||||
icon="FaExclamationTriangle"
|
||||
/>
|
||||
<Widget
|
||||
title="Geciken"
|
||||
value={plans.filter((p) => isOverdue(p)).length}
|
||||
color="red"
|
||||
icon="FaExclamationTriangle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Bu Hafta"
|
||||
value={plans.filter((p) => isDueSoon(p)).length}
|
||||
color="orange"
|
||||
icon="FaClock"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex space-x-3">
|
||||
<div className="flex-1 relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Plan ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-9 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
<Widget
|
||||
title="Bu Hafta"
|
||||
value={plans.filter((p) => isDueSoon(p)).length}
|
||||
color="orange"
|
||||
icon="FaClock"
|
||||
/>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<FaFilter className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<select
|
||||
value={typeFilter}
|
||||
onChange={(e) =>
|
||||
setTypeFilter(e.target.value as MaintenancePlanTypeEnum | "all")
|
||||
}
|
||||
className="pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Tipler</option>
|
||||
<option value={MaintenancePlanTypeEnum.Preventive}>Önleyici</option>
|
||||
<option value={MaintenancePlanTypeEnum.Corrective}>
|
||||
Düzeltici
|
||||
</option>
|
||||
<option value={MaintenancePlanTypeEnum.Predictive}>
|
||||
Tahminsel
|
||||
</option>
|
||||
<option value={MaintenancePlanTypeEnum.Condition}>
|
||||
Duruma Bağlı
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) =>
|
||||
setStatusFilter(e.target.value as "active" | "inactive" | "all")
|
||||
}
|
||||
className="pl-4 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Durumlar</option>
|
||||
<option value="active">Aktif</option>
|
||||
<option value="inactive">Pasif</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Plans Table */}
|
||||
<div className="bg-white rounded-lg shadow-md overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={
|
||||
selectedPlans.length === filteredPlans.length &&
|
||||
filteredPlans.length > 0
|
||||
}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setSelectedPlans(filteredPlans.map((p) => p.id));
|
||||
} else {
|
||||
setSelectedPlans([]);
|
||||
}
|
||||
}}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Plan Bilgileri
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
İş Merkezi
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Tip/Öncelik
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Sıklık
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Sonraki Bakım
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Maliyet
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Durum
|
||||
</th>
|
||||
<th className="px-4 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
İşlemler
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{filteredPlans.map((plan) => (
|
||||
<tr key={plan.id} className="hover:bg-gray-50">
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
{/* Filters */}
|
||||
<div className="flex space-x-3">
|
||||
<div className="flex-1 relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Plan ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-9 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<FaFilter className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<select
|
||||
value={typeFilter}
|
||||
onChange={(e) => setTypeFilter(e.target.value as MaintenancePlanTypeEnum | 'all')}
|
||||
className="pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Tipler</option>
|
||||
<option value={MaintenancePlanTypeEnum.Preventive}>Önleyici</option>
|
||||
<option value={MaintenancePlanTypeEnum.Corrective}>Düzeltici</option>
|
||||
<option value={MaintenancePlanTypeEnum.Predictive}>Tahminsel</option>
|
||||
<option value={MaintenancePlanTypeEnum.Condition}>Duruma Bağlı</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value as 'active' | 'inactive' | 'all')}
|
||||
className="pl-4 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Durumlar</option>
|
||||
<option value="active">Aktif</option>
|
||||
<option value="inactive">Pasif</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Plans Table */}
|
||||
<div className="bg-white rounded-lg shadow-md overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedPlans.includes(plan.id)}
|
||||
onChange={() => handleSelectPlan(plan.id)}
|
||||
checked={
|
||||
selectedPlans.length === filteredPlans.length && filteredPlans.length > 0
|
||||
}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setSelectedPlans(filteredPlans.map((p) => p.id))
|
||||
} else {
|
||||
setSelectedPlans([])
|
||||
}
|
||||
}}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<div className="flex items-center">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{plan.planCode}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{plan.description}
|
||||
</div>
|
||||
<div className="text-xs text-gray-400 mt-1">
|
||||
{plan.description}
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Plan Bilgileri
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
İş Merkezi
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Tip/Öncelik
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Sıklık
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Sonraki Bakım
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Maliyet
|
||||
</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Durum
|
||||
</th>
|
||||
<th className="px-4 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
İşlemler
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{filteredPlans.map((plan) => (
|
||||
<tr key={plan.id} className="hover:bg-gray-50">
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedPlans.includes(plan.id)}
|
||||
onChange={() => handleSelectPlan(plan.id)}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<div className="flex items-center">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">{plan.planCode}</div>
|
||||
<div className="text-sm text-gray-500">{plan.description}</div>
|
||||
<div className="text-xs text-gray-400 mt-1">{plan.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-900">
|
||||
{plan.workCenterId}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">İş Merkezi Adı</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<div className="space-y-1">
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-900">{plan.workCenterId}</div>
|
||||
<div className="text-sm text-gray-500">İş Merkezi Adı</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<div className="space-y-1">
|
||||
<span
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getMaintenancePlanTypeColor(
|
||||
plan.planType,
|
||||
)}`}
|
||||
>
|
||||
{getMaintenancePlanTypeText(plan.planType)}
|
||||
</span>
|
||||
<br />
|
||||
<span
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getPriorityColor(
|
||||
plan.priority,
|
||||
)}`}
|
||||
>
|
||||
{plan.priority === PriorityEnum.Low
|
||||
? 'Düşük'
|
||||
: plan.priority === PriorityEnum.Normal
|
||||
? 'Normal'
|
||||
: plan.priority === PriorityEnum.High
|
||||
? 'Yüksek'
|
||||
: 'Acil'}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-900">
|
||||
{getFrequencyUnitText(plan.frequency, plan.frequencyUnit)}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">{plan.estimatedDuration} dakika</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
{plan.nextDue ? (
|
||||
<div
|
||||
className={`text-sm ${
|
||||
isOverdue(plan)
|
||||
? 'text-red-600 font-medium'
|
||||
: isDueSoon(plan)
|
||||
? 'text-orange-600 font-medium'
|
||||
: 'text-gray-900'
|
||||
}`}
|
||||
>
|
||||
{plan.nextDue.toLocaleDateString('tr-TR')}
|
||||
{isOverdue(plan) && <div className="text-xs">GECİKMİŞ</div>}
|
||||
{isDueSoon(plan) && !isOverdue(plan) && (
|
||||
<div className="text-xs">YAKINDA</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-sm text-gray-400">-</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-900">
|
||||
₺{getTotalMaterialCost(plan).toLocaleString()}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">
|
||||
{plan.requiredMaterials.length} malzeme
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<span
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getMaintenancePlanTypeColor(
|
||||
plan.planType
|
||||
)}`}
|
||||
>
|
||||
{getMaintenancePlanTypeText(plan.planType)}
|
||||
</span>
|
||||
<br />
|
||||
<span
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getPriorityColor(
|
||||
plan.priority
|
||||
)}`}
|
||||
>
|
||||
{plan.priority === PriorityEnum.Low
|
||||
? "Düşük"
|
||||
: plan.priority === PriorityEnum.Normal
|
||||
? "Normal"
|
||||
: plan.priority === PriorityEnum.High
|
||||
? "Yüksek"
|
||||
: "Acil"}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-900">
|
||||
{getFrequencyUnitText(plan.frequency, plan.frequencyUnit)}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">
|
||||
{plan.estimatedDuration} dakika
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
{plan.nextDue ? (
|
||||
<div
|
||||
className={`text-sm ${
|
||||
isOverdue(plan)
|
||||
? "text-red-600 font-medium"
|
||||
: isDueSoon(plan)
|
||||
? "text-orange-600 font-medium"
|
||||
: "text-gray-900"
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
plan.isActive
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}
|
||||
>
|
||||
{plan.nextDue.toLocaleDateString("tr-TR")}
|
||||
{isOverdue(plan) && (
|
||||
<div className="text-xs">GECİKMİŞ</div>
|
||||
)}
|
||||
{isDueSoon(plan) && !isOverdue(plan) && (
|
||||
<div className="text-xs">YAKINDA</div>
|
||||
)}
|
||||
{plan.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-right text-sm font-medium">
|
||||
<div className="flex items-center justify-end space-x-2">
|
||||
<button
|
||||
onClick={() => handleView(plan)}
|
||||
className="text-gray-400 hover:text-blue-600 hover:bg-blue-50 p-1 rounded"
|
||||
title="Görüntüle"
|
||||
>
|
||||
<FaEye className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleEdit(plan)}
|
||||
className="text-gray-400 hover:text-green-600 hover:bg-green-50 p-1 rounded"
|
||||
title="Düzenle"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
className="text-gray-400 hover:text-red-600 hover:bg-red-50 p-1 rounded"
|
||||
title="Sil"
|
||||
>
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
className="text-gray-400 hover:text-blue-600 hover:bg-blue-50 p-1 rounded"
|
||||
title={plan.isActive ? 'Pasif Yap' : 'Aktif Yap'}
|
||||
>
|
||||
{plan.isActive ? (
|
||||
<FaPause className="w-4 h-4" />
|
||||
) : (
|
||||
<FaPlay className="w-4 h-4" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-sm text-gray-400">-</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-900">
|
||||
₺{getTotalMaterialCost(plan).toLocaleString()}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">
|
||||
{plan.requiredMaterials.length} malzeme
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap">
|
||||
<span
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
plan.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-gray-100 text-gray-800"
|
||||
}`}
|
||||
>
|
||||
{plan.isActive ? "Aktif" : "Pasif"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-right text-sm font-medium">
|
||||
<div className="flex items-center justify-end space-x-2">
|
||||
<button
|
||||
onClick={() => handleView(plan)}
|
||||
className="text-gray-400 hover:text-blue-600 hover:bg-blue-50 p-1 rounded"
|
||||
title="Görüntüle"
|
||||
>
|
||||
<FaEye className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleEdit(plan)}
|
||||
className="text-gray-400 hover:text-green-600 hover:bg-green-50 p-1 rounded"
|
||||
title="Düzenle"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
className="text-gray-400 hover:text-red-600 hover:bg-red-50 p-1 rounded"
|
||||
title="Sil"
|
||||
>
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
className="text-gray-400 hover:text-blue-600 hover:bg-blue-50 p-1 rounded"
|
||||
title={plan.isActive ? "Pasif Yap" : "Aktif Yap"}
|
||||
>
|
||||
{plan.isActive ? (
|
||||
<FaPause className="w-4 h-4" />
|
||||
) : (
|
||||
<FaPlay className="w-4 h-4" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{filteredPlans.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<FaCalendar className="w-16 h-16 text-gray-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">Plan bulunamadı</h3>
|
||||
<p className="text-gray-500 mb-4">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir plan oluşturun.
|
||||
</p>
|
||||
<button
|
||||
onClick={handleAddPlan}
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
|
||||
>
|
||||
Yeni Plan Oluştur
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{filteredPlans.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<FaCalendar className="w-16 h-16 text-gray-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||
Plan bulunamadı
|
||||
</h3>
|
||||
<p className="text-gray-500 mb-4">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir plan oluşturun.
|
||||
</p>
|
||||
<button
|
||||
onClick={handleAddPlan}
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
|
||||
>
|
||||
Yeni Plan Oluştur
|
||||
</button>
|
||||
{/* Bulk Actions */}
|
||||
{selectedPlans.length > 0 && (
|
||||
<div className="fixed bottom-6 left-1/2 transform -translate-x-1/2 bg-white rounded-lg shadow-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
<span className="text-sm text-gray-600">{selectedPlans.length} plan seçildi</span>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={handleCreateWorkOrder}
|
||||
className="flex items-center space-x-1 bg-green-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-green-700"
|
||||
>
|
||||
<FaTasks className="w-4 h-4" />
|
||||
<span>İş Emri Oluştur</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={handleStatusChange}
|
||||
className="flex items-center space-x-1 bg-blue-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-blue-700"
|
||||
>
|
||||
<FaPlay className="w-4 h-4" />
|
||||
<span>Durum Değiştir</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedPlans([])}
|
||||
className="bg-gray-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-gray-700"
|
||||
>
|
||||
Temizle
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Bulk Actions */}
|
||||
{selectedPlans.length > 0 && (
|
||||
<div className="fixed bottom-6 left-1/2 transform -translate-x-1/2 bg-white rounded-lg shadow-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
<span className="text-sm text-gray-600">
|
||||
{selectedPlans.length} plan seçildi
|
||||
</span>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={handleCreateWorkOrder}
|
||||
className="flex items-center space-x-1 bg-green-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-green-700"
|
||||
>
|
||||
<FaTasks className="w-4 h-4" />
|
||||
<span>İş Emri Oluştur</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={handleStatusChange}
|
||||
className="flex items-center space-x-1 bg-blue-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-blue-700"
|
||||
>
|
||||
<FaPlay className="w-4 h-4" />
|
||||
<span>Durum Değiştir</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedPlans([])}
|
||||
className="bg-gray-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-gray-700"
|
||||
>
|
||||
Temizle
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Modals */}
|
||||
<NewMaintenancePlanModal
|
||||
isOpen={showNewModal}
|
||||
|
|
@ -517,21 +470,17 @@ const MaintenancePlans: React.FC = () => {
|
|||
isOpen={showWorkOrderModal}
|
||||
onClose={() => setShowWorkOrderModal(false)}
|
||||
onSave={handleSaveWorkOrders}
|
||||
selectedPlans={selectedPlans
|
||||
.map((id) => plans.find((p) => p.id === id)!)
|
||||
.filter(Boolean)}
|
||||
selectedPlans={selectedPlans.map((id) => plans.find((p) => p.id === id)!).filter(Boolean)}
|
||||
/>
|
||||
|
||||
<PlanStatusChangeModal
|
||||
isOpen={showStatusModal}
|
||||
onClose={() => setShowStatusModal(false)}
|
||||
onSave={handleSaveStatusChange}
|
||||
selectedPlans={selectedPlans
|
||||
.map((id) => plans.find((p) => p.id === id)!)
|
||||
.filter(Boolean)}
|
||||
selectedPlans={selectedPlans.map((id) => plans.find((p) => p.id === id)!).filter(Boolean)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default MaintenancePlans;
|
||||
export default MaintenancePlans
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useState } from 'react'
|
||||
import {
|
||||
FaPlus,
|
||||
FaSearch,
|
||||
|
|
@ -12,44 +12,38 @@ import {
|
|||
FaEnvelope,
|
||||
FaAward,
|
||||
FaClock,
|
||||
} from "react-icons/fa";
|
||||
import { TeamRoleEnum } from "../../../types/common";
|
||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
||||
import NewTeamModal from "./NewTeamModal";
|
||||
import ViewTeamModal from "./ViewTeamModal";
|
||||
import EditTeamModal from "./EditTeamModal";
|
||||
import AssignWorkOrderModal from "./AssignWorkOrderModal";
|
||||
import TeamStatusChangeModal from "./TeamStatusChangeModal";
|
||||
import Widget from "../../../components/common/Widget";
|
||||
import { Team } from "../../../types/common";
|
||||
import {
|
||||
getTeamRoleColor,
|
||||
getTeamRoleIcon,
|
||||
getTeamRoleText,
|
||||
} from "../../../utils/erp";
|
||||
} from 'react-icons/fa'
|
||||
import { TeamRoleEnum } from '../../../types/common'
|
||||
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||
import NewTeamModal from './NewTeamModal'
|
||||
import ViewTeamModal from './ViewTeamModal'
|
||||
import EditTeamModal from './EditTeamModal'
|
||||
import AssignWorkOrderModal from './AssignWorkOrderModal'
|
||||
import TeamStatusChangeModal from './TeamStatusChangeModal'
|
||||
import Widget from '../../../components/common/Widget'
|
||||
import { Team } from '../../../types/common'
|
||||
import { getTeamRoleColor, getTeamRoleIcon, getTeamRoleText } from '../../../utils/erp'
|
||||
import { Container } from '@/components/shared'
|
||||
|
||||
const MaintenanceTeams: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [roleFilter, setRoleFilter] = useState<TeamRoleEnum | "all">("all");
|
||||
const [statusFilter, setStatusFilter] = useState<
|
||||
"active" | "inactive" | "all"
|
||||
>("all");
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const [roleFilter, setRoleFilter] = useState<TeamRoleEnum | 'all'>('all')
|
||||
const [statusFilter, setStatusFilter] = useState<'active' | 'inactive' | 'all'>('all')
|
||||
|
||||
// Modal states
|
||||
const [showNewTeamModal, setShowNewTeamModal] = useState(false);
|
||||
const [showViewTeamModal, setShowViewTeamModal] = useState(false);
|
||||
const [showEditTeamModal, setShowEditTeamModal] = useState(false);
|
||||
const [showAssignWorkOrderModal, setShowAssignWorkOrderModal] =
|
||||
useState(false);
|
||||
const [showStatusChangeModal, setShowStatusChangeModal] = useState(false);
|
||||
const [showNewTeamModal, setShowNewTeamModal] = useState(false)
|
||||
const [showViewTeamModal, setShowViewTeamModal] = useState(false)
|
||||
const [showEditTeamModal, setShowEditTeamModal] = useState(false)
|
||||
const [showAssignWorkOrderModal, setShowAssignWorkOrderModal] = useState(false)
|
||||
const [showStatusChangeModal, setShowStatusChangeModal] = useState(false)
|
||||
|
||||
// Selected data
|
||||
const [viewingTeam, setViewingTeam] = useState<Team | null>(null);
|
||||
const [editingTeam, setEditingTeam] = useState<Team | null>(null);
|
||||
const [selectedTeams, setSelectedTeams] = useState<string[]>([]);
|
||||
const [viewingTeam, setViewingTeam] = useState<Team | null>(null)
|
||||
const [editingTeam, setEditingTeam] = useState<Team | null>(null)
|
||||
const [selectedTeams, setSelectedTeams] = useState<string[]>([])
|
||||
|
||||
// Mock data - replace with actual API calls
|
||||
const [teams, setTeams] = useState<Team[]>(mockMaintenanceTeams);
|
||||
const [teams, setTeams] = useState<Team[]>(mockMaintenanceTeams)
|
||||
|
||||
const filteredTeams = teams.filter((team) => {
|
||||
const matchesSearch =
|
||||
|
|
@ -57,441 +51,389 @@ const MaintenanceTeams: React.FC = () => {
|
|||
team.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
team.members.some(
|
||||
(member) =>
|
||||
member.employee?.firstName
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase()) ||
|
||||
member.employee?.lastName
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase())
|
||||
);
|
||||
member.employee?.firstName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
member.employee?.lastName.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
)
|
||||
const matchesRole =
|
||||
roleFilter === "all" ||
|
||||
team.members.some((member) => member.role === roleFilter);
|
||||
roleFilter === 'all' || team.members.some((member) => member.role === roleFilter)
|
||||
const matchesStatus =
|
||||
statusFilter === "all" ||
|
||||
(statusFilter === "active" && team.isActive) ||
|
||||
(statusFilter === "inactive" && !team.isActive);
|
||||
return matchesSearch && matchesRole && matchesStatus;
|
||||
});
|
||||
statusFilter === 'all' ||
|
||||
(statusFilter === 'active' && team.isActive) ||
|
||||
(statusFilter === 'inactive' && !team.isActive)
|
||||
return matchesSearch && matchesRole && matchesStatus
|
||||
})
|
||||
|
||||
const getTeamLeader = (team: Team) => {
|
||||
return team.members.find((member) => member.role === TeamRoleEnum.Lead);
|
||||
};
|
||||
return team.members.find((member) => member.role === TeamRoleEnum.Lead)
|
||||
}
|
||||
|
||||
const getActiveMembers = (team: Team) => {
|
||||
return team.members.filter((member) => member.isActive);
|
||||
};
|
||||
return team.members.filter((member) => member.isActive)
|
||||
}
|
||||
|
||||
const handleAddTeam = () => {
|
||||
setEditingTeam(null);
|
||||
setShowNewTeamModal(true);
|
||||
};
|
||||
setEditingTeam(null)
|
||||
setShowNewTeamModal(true)
|
||||
}
|
||||
|
||||
const handleViewTeam = (team: Team) => {
|
||||
setViewingTeam(team);
|
||||
setShowViewTeamModal(true);
|
||||
};
|
||||
setViewingTeam(team)
|
||||
setShowViewTeamModal(true)
|
||||
}
|
||||
|
||||
const handleEditTeam = (team: Team) => {
|
||||
setEditingTeam(team);
|
||||
setShowEditTeamModal(true);
|
||||
};
|
||||
setEditingTeam(team)
|
||||
setShowEditTeamModal(true)
|
||||
}
|
||||
|
||||
const handleSelectTeam = (teamId: string) => {
|
||||
setSelectedTeams((prev) =>
|
||||
prev.includes(teamId)
|
||||
? prev.filter((id) => id !== teamId)
|
||||
: [...prev, teamId]
|
||||
);
|
||||
};
|
||||
prev.includes(teamId) ? prev.filter((id) => id !== teamId) : [...prev, teamId],
|
||||
)
|
||||
}
|
||||
|
||||
const handleNewTeamSave = (newTeam: Partial<Team>) => {
|
||||
if (newTeam.id) {
|
||||
setTeams((prev) => [...prev, newTeam as Team]);
|
||||
setTeams((prev) => [...prev, newTeam as Team])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleEditTeamSave = (updatedTeam: Team) => {
|
||||
setTeams((prev) =>
|
||||
prev.map((team) => (team.id === updatedTeam.id ? updatedTeam : team))
|
||||
);
|
||||
};
|
||||
setTeams((prev) => prev.map((team) => (team.id === updatedTeam.id ? updatedTeam : team)))
|
||||
}
|
||||
|
||||
const handleAssignWorkOrders = (
|
||||
assignments: { teamId: string; planIds: string[] }[]
|
||||
) => {
|
||||
const handleAssignWorkOrders = (assignments: { teamId: string; planIds: string[] }[]) => {
|
||||
// Here you would typically make API calls to assign work orders
|
||||
console.log("Assigning work orders:", assignments);
|
||||
console.log('Assigning work orders:', assignments)
|
||||
// Show success message or handle the actual assignment logic
|
||||
};
|
||||
}
|
||||
|
||||
const handleStatusChange = (
|
||||
teamIds: string[],
|
||||
newStatus: boolean,
|
||||
reason?: string
|
||||
) => {
|
||||
const handleStatusChange = (teamIds: string[], newStatus: boolean, reason?: string) => {
|
||||
setTeams((prev) =>
|
||||
prev.map((team) =>
|
||||
teamIds.includes(team.id)
|
||||
? { ...team, isActive: newStatus, lastModificationTime: new Date() }
|
||||
: team
|
||||
)
|
||||
);
|
||||
setSelectedTeams([]);
|
||||
: team,
|
||||
),
|
||||
)
|
||||
setSelectedTeams([])
|
||||
// Here you would typically log the status change with reason
|
||||
console.log("Status change:", { teamIds, newStatus, reason });
|
||||
};
|
||||
console.log('Status change:', { teamIds, newStatus, reason })
|
||||
}
|
||||
|
||||
const selectedTeamObjects = teams.filter((team) =>
|
||||
selectedTeams.includes(team.id)
|
||||
);
|
||||
const selectedTeamObjects = teams.filter((team) => selectedTeams.includes(team.id))
|
||||
|
||||
return (
|
||||
<div className="space-y-4 pt-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Bakım Ekipleri</h2>
|
||||
<p className="text-gray-600">
|
||||
Bakım ekiplerini ve üyelerini yönetin
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAddTeam}
|
||||
className="bg-blue-600 text-white px-3 py-1.5 rounded-lg hover:bg-blue-700 flex items-center space-x-2 text-sm"
|
||||
>
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Ekip</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Summary Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
<Widget
|
||||
title="Toplam Ekip"
|
||||
value={teams.length}
|
||||
color="blue"
|
||||
icon="FaUsers"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Aktif Ekip"
|
||||
value={teams.filter((t) => t.isActive).length}
|
||||
color="green"
|
||||
icon="FaCheckCircle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Toplam Üye"
|
||||
value={teams.reduce(
|
||||
(total, team) => total + getActiveMembers(team).length,
|
||||
0
|
||||
)}
|
||||
color="orange"
|
||||
icon="FaUser"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Uzmanlık"
|
||||
value={teams.reduce(
|
||||
(total, team) => total + (team.specializations?.length || 0),
|
||||
0
|
||||
)}
|
||||
color="purple"
|
||||
icon="FaWrench"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex space-x-3">
|
||||
<div className="flex-1 relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Ekip veya üye ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-9 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<FaFilter className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<select
|
||||
value={roleFilter}
|
||||
onChange={(e) =>
|
||||
setRoleFilter(e.target.value as TeamRoleEnum | "all")
|
||||
}
|
||||
className="pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Roller</option>
|
||||
<option value={TeamRoleEnum.Lead}>Lider</option>
|
||||
<option value={TeamRoleEnum.Specialist}>Uzman</option>
|
||||
<option value={TeamRoleEnum.Member}>Üye</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) =>
|
||||
setStatusFilter(e.target.value as "active" | "inactive" | "all")
|
||||
}
|
||||
className="pl-4 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Durumlar</option>
|
||||
<option value="active">Aktif</option>
|
||||
<option value="inactive">Pasif</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Teams Grid */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{filteredTeams.map((team) => {
|
||||
const leader = getTeamLeader(team);
|
||||
const activeMembers = getActiveMembers(team);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={team.id}
|
||||
className="bg-white rounded-lg shadow-md border border-gray-200 p-4 hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedTeams.includes(team.id)}
|
||||
onChange={() => handleSelectTeam(team.id)}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500 mt-0.5"
|
||||
/>
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
{team.code}
|
||||
</h3>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
team.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-gray-100 text-gray-800"
|
||||
}`}
|
||||
>
|
||||
{team.isActive ? "Aktif" : "Pasif"}
|
||||
</span>
|
||||
</div>
|
||||
<h4 className="font-medium text-gray-700 mb-1 text-sm">
|
||||
{team.name}
|
||||
</h4>
|
||||
<p className="text-xs text-gray-500 mb-2">
|
||||
{team.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex space-x-1">
|
||||
<button
|
||||
onClick={() => handleViewTeam(team)}
|
||||
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||
>
|
||||
<FaEye className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleEditTeam(team)}
|
||||
className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md transition-colors"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Team Leader */}
|
||||
{leader && (
|
||||
<div className="bg-purple-50 rounded-lg p-2 mb-3">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-10 h-10 bg-purple-600 rounded-full flex items-center justify-center">
|
||||
<FaAward className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<h5 className="font-medium text-gray-900">
|
||||
{leader.employee?.firstName}{" "}
|
||||
{leader.employee?.lastName}
|
||||
</h5>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium flex items-center space-x-1 ${getTeamRoleColor(
|
||||
leader.role
|
||||
)}`}
|
||||
>
|
||||
{getTeamRoleIcon(leader.role)}
|
||||
<span>{getTeamRoleText(leader.role)}</span>
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600">
|
||||
{leader.employee?.jobPosition?.code}
|
||||
</p>
|
||||
<div className="flex items-center space-x-4 mt-1 text-xs text-gray-500">
|
||||
{leader.employee?.email && (
|
||||
<div className="flex items-center space-x-1">
|
||||
<FaEnvelope className="w-3 h-3" />
|
||||
<span>{leader.employee.email}</span>
|
||||
</div>
|
||||
)}
|
||||
{leader.employee?.phone && (
|
||||
<div className="flex items-center space-x-1">
|
||||
<FaPhone className="w-3 h-3" />
|
||||
<span>{leader.employee.phone}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Team Members */}
|
||||
<div className="space-y-2 mb-3">
|
||||
<h5 className="text-xs font-medium text-gray-700">
|
||||
Ekip Üyeleri ({activeMembers.length})
|
||||
</h5>
|
||||
<div className="space-y-2">
|
||||
{activeMembers
|
||||
.filter((member) => member.role !== TeamRoleEnum.Lead)
|
||||
.slice(0, 3)
|
||||
.map((member) => (
|
||||
<div
|
||||
key={member.id}
|
||||
className="flex items-center space-x-2 p-1.5 bg-gray-50 rounded-lg"
|
||||
>
|
||||
<div className="w-8 h-8 bg-gray-400 rounded-full flex items-center justify-center">
|
||||
<FaUser className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
{member.employee?.firstName}{" "}
|
||||
{member.employee?.lastName}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium flex items-center space-x-1 ${getTeamRoleColor(
|
||||
member.role
|
||||
)}`}
|
||||
>
|
||||
{getTeamRoleIcon(member.role)}
|
||||
<span>{getTeamRoleText(member.role)}</span>
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-600">
|
||||
{member.employee?.jobPosition?.code}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{activeMembers.filter(
|
||||
(member) => member.role !== TeamRoleEnum.Lead
|
||||
).length > 3 && (
|
||||
<div className="text-center text-xs text-gray-500 p-1.5">
|
||||
+
|
||||
{activeMembers.filter(
|
||||
(member) => member.role !== TeamRoleEnum.Lead
|
||||
).length - 3}{" "}
|
||||
kişi daha...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Specializations */}
|
||||
<div className="border-t border-gray-100 pt-2">
|
||||
<h5 className="text-xs font-medium text-gray-700 mb-2">
|
||||
Uzmanlık Alanları
|
||||
</h5>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{team.specializations ??
|
||||
[].slice(0, 6).map((specialization, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full"
|
||||
>
|
||||
{specialization}
|
||||
</span>
|
||||
))}
|
||||
{(team.specializations?.length ?? 0) > 6 && (
|
||||
<span className="px-2 py-1 bg-gray-100 text-gray-600 text-xs rounded-full">
|
||||
+{(team.specializations?.length ?? 0) - 6}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-100 pt-2 mt-2">
|
||||
<div className="flex items-center justify-between text-xs text-gray-500">
|
||||
<div className="flex items-center space-x-2">
|
||||
<FaClock className="w-3 h-3" />
|
||||
<span>
|
||||
Oluşturuldu:{" "}
|
||||
{team.creationTime.toLocaleDateString("tr-TR")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<span>
|
||||
Son Güncelleme:{" "}
|
||||
{team.lastModificationTime.toLocaleDateString("tr-TR")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{filteredTeams.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<FaUsers className="w-16 h-16 text-gray-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||
Ekip bulunamadı
|
||||
</h3>
|
||||
<p className="text-gray-500 mb-4">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir ekip oluşturun.
|
||||
</p>
|
||||
<Container>
|
||||
<div className="space-y-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Bakım Ekipleri</h2>
|
||||
<p className="text-gray-600">Bakım ekiplerini ve üyelerini yönetin</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAddTeam}
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
|
||||
className="bg-blue-600 text-white px-3 py-1.5 rounded-lg hover:bg-blue-700 flex items-center space-x-2 text-sm"
|
||||
>
|
||||
Yeni Ekip Oluştur
|
||||
<FaPlus className="w-4 h-4" />
|
||||
<span>Yeni Ekip</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Bulk Actions */}
|
||||
{selectedTeams.length > 0 && (
|
||||
<div className="fixed bottom-6 left-1/2 transform -translate-x-1/2 bg-white rounded-lg shadow-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
<span className="text-sm text-gray-600">
|
||||
{selectedTeams.length} ekip seçildi
|
||||
</span>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={() => setShowAssignWorkOrderModal(true)}
|
||||
className="bg-green-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-green-700"
|
||||
>
|
||||
İş Emri Ata
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowStatusChangeModal(true)}
|
||||
className="bg-blue-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-blue-700"
|
||||
>
|
||||
Durum Değiştir
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedTeams([])}
|
||||
className="bg-gray-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-gray-700"
|
||||
>
|
||||
Temizle
|
||||
</button>
|
||||
</div>
|
||||
{/* Summary Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
<Widget title="Toplam Ekip" value={teams.length} color="blue" icon="FaUsers" />
|
||||
|
||||
<Widget
|
||||
title="Aktif Ekip"
|
||||
value={teams.filter((t) => t.isActive).length}
|
||||
color="green"
|
||||
icon="FaCheckCircle"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Toplam Üye"
|
||||
value={teams.reduce((total, team) => total + getActiveMembers(team).length, 0)}
|
||||
color="orange"
|
||||
icon="FaUser"
|
||||
/>
|
||||
|
||||
<Widget
|
||||
title="Uzmanlık"
|
||||
value={teams.reduce((total, team) => total + (team.specializations?.length || 0), 0)}
|
||||
color="purple"
|
||||
icon="FaWrench"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex space-x-3">
|
||||
<div className="flex-1 relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Ekip veya üye ara..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-full pl-9 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<FaFilter className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<select
|
||||
value={roleFilter}
|
||||
onChange={(e) => setRoleFilter(e.target.value as TeamRoleEnum | 'all')}
|
||||
className="pl-10 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Roller</option>
|
||||
<option value={TeamRoleEnum.Lead}>Lider</option>
|
||||
<option value={TeamRoleEnum.Specialist}>Uzman</option>
|
||||
<option value={TeamRoleEnum.Member}>Üye</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value as 'active' | 'inactive' | 'all')}
|
||||
className="pl-4 pr-4 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="all">Tüm Durumlar</option>
|
||||
<option value="active">Aktif</option>
|
||||
<option value="inactive">Pasif</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Teams Grid */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{filteredTeams.map((team) => {
|
||||
const leader = getTeamLeader(team)
|
||||
const activeMembers = getActiveMembers(team)
|
||||
|
||||
return (
|
||||
<div
|
||||
key={team.id}
|
||||
className="bg-white rounded-lg shadow-md border border-gray-200 p-4 hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedTeams.includes(team.id)}
|
||||
onChange={() => handleSelectTeam(team.id)}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500 mt-0.5"
|
||||
/>
|
||||
<h3 className="text-lg font-semibold text-gray-900">{team.code}</h3>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
team.isActive
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}
|
||||
>
|
||||
{team.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
</div>
|
||||
<h4 className="font-medium text-gray-700 mb-1 text-sm">{team.name}</h4>
|
||||
<p className="text-xs text-gray-500 mb-2">{team.description}</p>
|
||||
</div>
|
||||
<div className="flex space-x-1">
|
||||
<button
|
||||
onClick={() => handleViewTeam(team)}
|
||||
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors"
|
||||
>
|
||||
<FaEye className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleEditTeam(team)}
|
||||
className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-md transition-colors"
|
||||
>
|
||||
<FaEdit className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="p-1.5 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors">
|
||||
<FaTrash className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Team Leader */}
|
||||
{leader && (
|
||||
<div className="bg-purple-50 rounded-lg p-2 mb-3">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-10 h-10 bg-purple-600 rounded-full flex items-center justify-center">
|
||||
<FaAward className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<h5 className="font-medium text-gray-900">
|
||||
{leader.employee?.firstName} {leader.employee?.lastName}
|
||||
</h5>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium flex items-center space-x-1 ${getTeamRoleColor(
|
||||
leader.role,
|
||||
)}`}
|
||||
>
|
||||
{getTeamRoleIcon(leader.role)}
|
||||
<span>{getTeamRoleText(leader.role)}</span>
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600">
|
||||
{leader.employee?.jobPosition?.code}
|
||||
</p>
|
||||
<div className="flex items-center space-x-4 mt-1 text-xs text-gray-500">
|
||||
{leader.employee?.email && (
|
||||
<div className="flex items-center space-x-1">
|
||||
<FaEnvelope className="w-3 h-3" />
|
||||
<span>{leader.employee.email}</span>
|
||||
</div>
|
||||
)}
|
||||
{leader.employee?.phone && (
|
||||
<div className="flex items-center space-x-1">
|
||||
<FaPhone className="w-3 h-3" />
|
||||
<span>{leader.employee.phone}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Team Members */}
|
||||
<div className="space-y-2 mb-3">
|
||||
<h5 className="text-xs font-medium text-gray-700">
|
||||
Ekip Üyeleri ({activeMembers.length})
|
||||
</h5>
|
||||
<div className="space-y-2">
|
||||
{activeMembers
|
||||
.filter((member) => member.role !== TeamRoleEnum.Lead)
|
||||
.slice(0, 3)
|
||||
.map((member) => (
|
||||
<div
|
||||
key={member.id}
|
||||
className="flex items-center space-x-2 p-1.5 bg-gray-50 rounded-lg"
|
||||
>
|
||||
<div className="w-8 h-8 bg-gray-400 rounded-full flex items-center justify-center">
|
||||
<FaUser className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
{member.employee?.firstName} {member.employee?.lastName}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium flex items-center space-x-1 ${getTeamRoleColor(
|
||||
member.role,
|
||||
)}`}
|
||||
>
|
||||
{getTeamRoleIcon(member.role)}
|
||||
<span>{getTeamRoleText(member.role)}</span>
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-600">
|
||||
{member.employee?.jobPosition?.code}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{activeMembers.filter((member) => member.role !== TeamRoleEnum.Lead).length >
|
||||
3 && (
|
||||
<div className="text-center text-xs text-gray-500 p-1.5">
|
||||
+
|
||||
{activeMembers.filter((member) => member.role !== TeamRoleEnum.Lead)
|
||||
.length - 3}{' '}
|
||||
kişi daha...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Specializations */}
|
||||
<div className="border-t border-gray-100 pt-2">
|
||||
<h5 className="text-xs font-medium text-gray-700 mb-2">Uzmanlık Alanları</h5>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{team.specializations ??
|
||||
[].slice(0, 6).map((specialization, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full"
|
||||
>
|
||||
{specialization}
|
||||
</span>
|
||||
))}
|
||||
{(team.specializations?.length ?? 0) > 6 && (
|
||||
<span className="px-2 py-1 bg-gray-100 text-gray-600 text-xs rounded-full">
|
||||
+{(team.specializations?.length ?? 0) - 6}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-100 pt-2 mt-2">
|
||||
<div className="flex items-center justify-between text-xs text-gray-500">
|
||||
<div className="flex items-center space-x-2">
|
||||
<FaClock className="w-3 h-3" />
|
||||
<span>Oluşturuldu: {team.creationTime.toLocaleDateString('tr-TR')}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<span>
|
||||
Son Güncelleme: {team.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{filteredTeams.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<FaUsers className="w-16 h-16 text-gray-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">Ekip bulunamadı</h3>
|
||||
<p className="text-gray-500 mb-4">
|
||||
Arama kriterlerinizi değiştirin veya yeni bir ekip oluşturun.
|
||||
</p>
|
||||
<button
|
||||
onClick={handleAddTeam}
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
|
||||
>
|
||||
Yeni Ekip Oluştur
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Bulk Actions */}
|
||||
{selectedTeams.length > 0 && (
|
||||
<div className="fixed bottom-6 left-1/2 transform -translate-x-1/2 bg-white rounded-lg shadow-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
<span className="text-sm text-gray-600">{selectedTeams.length} ekip seçildi</span>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={() => setShowAssignWorkOrderModal(true)}
|
||||
className="bg-green-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-green-700"
|
||||
>
|
||||
İş Emri Ata
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowStatusChangeModal(true)}
|
||||
className="bg-blue-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-blue-700"
|
||||
>
|
||||
Durum Değiştir
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedTeams([])}
|
||||
className="bg-gray-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-gray-700"
|
||||
>
|
||||
Temizle
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Modals */}
|
||||
<NewTeamModal
|
||||
|
|
@ -527,8 +469,8 @@ const MaintenanceTeams: React.FC = () => {
|
|||
onSave={handleStatusChange}
|
||||
selectedTeams={selectedTeamObjects}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default MaintenanceTeams;
|
||||
export default MaintenanceTeams
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +1,15 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { FaTimes, FaSave, FaCalendar, FaClock } from "react-icons/fa";
|
||||
import { PmCalendarEvent, WorkOrderStatusEnum } from "../../../types/pm";
|
||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { FaTimes, FaSave, FaCalendar, FaClock } from 'react-icons/fa'
|
||||
import { PmCalendarEvent, WorkOrderStatusEnum } from '../../../types/pm'
|
||||
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
|
||||
interface NewCalendarEventModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (event: Partial<PmCalendarEvent>) => void;
|
||||
selectedDate?: Date;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (event: Partial<PmCalendarEvent>) => void
|
||||
selectedDate?: Date
|
||||
}
|
||||
|
||||
const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||
|
|
@ -20,185 +20,165 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
|||
}) => {
|
||||
const getInitialStartTime = () => {
|
||||
if (selectedDate) {
|
||||
const hour = selectedDate.getHours();
|
||||
const minute = selectedDate.getMinutes();
|
||||
return `${hour.toString().padStart(2, "0")}:${minute
|
||||
.toString()
|
||||
.padStart(2, "0")}`;
|
||||
const hour = selectedDate.getHours()
|
||||
const minute = selectedDate.getMinutes()
|
||||
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
|
||||
}
|
||||
return "09:00";
|
||||
};
|
||||
return '09:00'
|
||||
}
|
||||
|
||||
const getInitialEndTime = () => {
|
||||
if (selectedDate) {
|
||||
const endTime = new Date(selectedDate);
|
||||
endTime.setHours(endTime.getHours() + 1);
|
||||
const hour = endTime.getHours();
|
||||
const minute = endTime.getMinutes();
|
||||
return `${hour.toString().padStart(2, "0")}:${minute
|
||||
.toString()
|
||||
.padStart(2, "0")}`;
|
||||
const endTime = new Date(selectedDate)
|
||||
endTime.setHours(endTime.getHours() + 1)
|
||||
const hour = endTime.getHours()
|
||||
const minute = endTime.getMinutes()
|
||||
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
|
||||
}
|
||||
return "10:00";
|
||||
};
|
||||
return '10:00'
|
||||
}
|
||||
|
||||
const [eventData, setEventData] = useState<Partial<PmCalendarEvent>>({
|
||||
title: "",
|
||||
type: "plan",
|
||||
title: '',
|
||||
type: 'plan',
|
||||
date: selectedDate || new Date(),
|
||||
startTime: getInitialStartTime(),
|
||||
endTime: getInitialEndTime(),
|
||||
status: WorkOrderStatusEnum.Planned,
|
||||
priority: PriorityEnum.Normal,
|
||||
assignedTo: "",
|
||||
workCenterCode: "",
|
||||
assignedTo: '',
|
||||
workCenterCode: '',
|
||||
duration: 60,
|
||||
});
|
||||
})
|
||||
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
|
||||
// Update form data when selectedDate changes
|
||||
useEffect(() => {
|
||||
if (selectedDate) {
|
||||
const getInitialStartTime = () => {
|
||||
const hour = selectedDate.getHours();
|
||||
const minute = selectedDate.getMinutes();
|
||||
return `${hour.toString().padStart(2, "0")}:${minute
|
||||
.toString()
|
||||
.padStart(2, "0")}`;
|
||||
};
|
||||
const hour = selectedDate.getHours()
|
||||
const minute = selectedDate.getMinutes()
|
||||
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
|
||||
}
|
||||
|
||||
const getInitialEndTime = () => {
|
||||
const endTime = new Date(selectedDate);
|
||||
endTime.setHours(endTime.getHours() + 1);
|
||||
const hour = endTime.getHours();
|
||||
const minute = endTime.getMinutes();
|
||||
return `${hour.toString().padStart(2, "0")}:${minute
|
||||
.toString()
|
||||
.padStart(2, "0")}`;
|
||||
};
|
||||
const endTime = new Date(selectedDate)
|
||||
endTime.setHours(endTime.getHours() + 1)
|
||||
const hour = endTime.getHours()
|
||||
const minute = endTime.getMinutes()
|
||||
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
|
||||
}
|
||||
|
||||
setEventData((prev) => ({
|
||||
...prev,
|
||||
date: selectedDate,
|
||||
startTime: getInitialStartTime(),
|
||||
endTime: getInitialEndTime(),
|
||||
}));
|
||||
}))
|
||||
}
|
||||
}, [selectedDate]);
|
||||
}, [selectedDate])
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (!eventData.title?.trim()) {
|
||||
newErrors.title = "Başlık gerekli";
|
||||
newErrors.title = 'Başlık gerekli'
|
||||
}
|
||||
if (!eventData.workCenterCode?.trim()) {
|
||||
newErrors.workCenterCode = "İş Merkezi seçimi gerekli";
|
||||
newErrors.workCenterCode = 'İş Merkezi seçimi gerekli'
|
||||
}
|
||||
if (!eventData.startTime) {
|
||||
newErrors.startTime = "Başlangıç saati gerekli";
|
||||
newErrors.startTime = 'Başlangıç saati gerekli'
|
||||
}
|
||||
if (!eventData.endTime) {
|
||||
newErrors.endTime = "Bitiş saati gerekli";
|
||||
newErrors.endTime = 'Bitiş saati gerekli'
|
||||
}
|
||||
if (eventData.startTime && eventData.endTime) {
|
||||
const start = new Date(`2000-01-01 ${eventData.startTime}`);
|
||||
const end = new Date(`2000-01-01 ${eventData.endTime}`);
|
||||
const start = new Date(`2000-01-01 ${eventData.startTime}`)
|
||||
const end = new Date(`2000-01-01 ${eventData.endTime}`)
|
||||
if (start >= end) {
|
||||
newErrors.endTime = "Bitiş saati başlangıç saatinden sonra olmalı";
|
||||
newErrors.endTime = 'Bitiş saati başlangıç saatinden sonra olmalı'
|
||||
}
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
if (validateForm()) {
|
||||
// Calculate duration
|
||||
if (eventData.startTime && eventData.endTime) {
|
||||
const start = new Date(`2000-01-01 ${eventData.startTime}`);
|
||||
const end = new Date(`2000-01-01 ${eventData.endTime}`);
|
||||
const durationMinutes = (end.getTime() - start.getTime()) / (1000 * 60);
|
||||
eventData.duration = durationMinutes;
|
||||
const start = new Date(`2000-01-01 ${eventData.startTime}`)
|
||||
const end = new Date(`2000-01-01 ${eventData.endTime}`)
|
||||
const durationMinutes = (end.getTime() - start.getTime()) / (1000 * 60)
|
||||
eventData.duration = durationMinutes
|
||||
}
|
||||
|
||||
onSave({
|
||||
...eventData,
|
||||
id: `E${Date.now()}`, // Generate a simple ID
|
||||
});
|
||||
onClose();
|
||||
})
|
||||
onClose()
|
||||
// Reset form
|
||||
setEventData({
|
||||
title: "",
|
||||
type: "plan",
|
||||
title: '',
|
||||
type: 'plan',
|
||||
date: selectedDate || new Date(),
|
||||
startTime: getInitialStartTime(),
|
||||
endTime: getInitialEndTime(),
|
||||
status: WorkOrderStatusEnum.Planned,
|
||||
priority: PriorityEnum.Normal,
|
||||
assignedTo: "",
|
||||
workCenterCode: "",
|
||||
assignedTo: '',
|
||||
workCenterCode: '',
|
||||
duration: 60,
|
||||
});
|
||||
setErrors({});
|
||||
})
|
||||
setErrors({})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleInputChange = (
|
||||
field: keyof PmCalendarEvent,
|
||||
value:
|
||||
| string
|
||||
| Date
|
||||
| PriorityEnum
|
||||
| WorkOrderStatusEnum
|
||||
| "plan"
|
||||
| "workorder"
|
||||
| "scheduled"
|
||||
value: string | Date | PriorityEnum | WorkOrderStatusEnum | 'plan' | 'workorder' | 'scheduled',
|
||||
) => {
|
||||
setEventData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
}))
|
||||
// Clear error when user starts typing
|
||||
if (errors[field]) {
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
[field]: "",
|
||||
}));
|
||||
[field]: '',
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const generateTimeOptions = () => {
|
||||
const options = [];
|
||||
const options = []
|
||||
for (let hour = 0; hour < 24; hour++) {
|
||||
for (let minute = 0; minute < 60; minute += 30) {
|
||||
const timeString = `${hour.toString().padStart(2, "0")}:${minute
|
||||
const timeString = `${hour.toString().padStart(2, '0')}:${minute
|
||||
.toString()
|
||||
.padStart(2, "0")}`;
|
||||
options.push(timeString);
|
||||
.padStart(2, '0')}`
|
||||
options.push(timeString)
|
||||
}
|
||||
}
|
||||
return options;
|
||||
};
|
||||
return options
|
||||
}
|
||||
|
||||
const timeOptions = generateTimeOptions();
|
||||
const timeOptions = generateTimeOptions()
|
||||
|
||||
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-lg 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-xl font-bold text-gray-900">
|
||||
Yeni Bakım Planlaması
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
||||
>
|
||||
<h2 className="text-xl font-bold text-gray-900">Yeni Bakım Planlaması</h2>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -208,35 +188,24 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
|||
{/* Basic Info */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="md:col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Başlık *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Başlık *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={eventData.title || ""}
|
||||
onChange={(e) => handleInputChange("title", e.target.value)}
|
||||
value={eventData.title || ''}
|
||||
onChange={(e) => handleInputChange('title', 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.title ? "border-red-500" : "border-gray-300"
|
||||
errors.title ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Bakım planı başlığı"
|
||||
/>
|
||||
{errors.title && (
|
||||
<p className="text-red-500 text-xs mt-1">{errors.title}</p>
|
||||
)}
|
||||
{errors.title && <p className="text-red-500 text-xs mt-1">{errors.title}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Tip
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Tip</label>
|
||||
<select
|
||||
value={eventData.type || "plan"}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"type",
|
||||
e.target.value as "plan" | "workorder"
|
||||
)
|
||||
}
|
||||
value={eventData.type || 'plan'}
|
||||
onChange={(e) => handleInputChange('type', e.target.value as 'plan' | 'workorder')}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="plan">Bakım Planı</option>
|
||||
|
|
@ -245,14 +214,10 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Öncelik
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Öncelik</label>
|
||||
<select
|
||||
value={eventData.priority || PriorityEnum.Normal}
|
||||
onChange={(e) =>
|
||||
handleInputChange("priority", e.target.value as PriorityEnum)
|
||||
}
|
||||
onChange={(e) => handleInputChange('priority', e.target.value as PriorityEnum)}
|
||||
className="w-full px-2.5 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>
|
||||
|
|
@ -263,16 +228,12 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
İş Merkezi *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">İş Merkezi *</label>
|
||||
<select
|
||||
value={eventData.workCenterCode || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("workCenterCode", e.target.value)
|
||||
}
|
||||
value={eventData.workCenterCode || ''}
|
||||
onChange={(e) => handleInputChange('workCenterCode', 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.workCenterCode ? "border-red-500" : "border-gray-300"
|
||||
errors.workCenterCode ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">İş merkezi seçin</option>
|
||||
|
|
@ -283,21 +244,15 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
|||
))}
|
||||
</select>
|
||||
{errors.workCenterCode && (
|
||||
<p className="text-red-500 text-xs mt-1">
|
||||
{errors.workCenterCode}
|
||||
</p>
|
||||
<p className="text-red-500 text-xs mt-1">{errors.workCenterCode}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Atanan Kişi
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Atanan Kişi</label>
|
||||
<select
|
||||
value={eventData.assignedTo || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("assignedTo", e.target.value)
|
||||
}
|
||||
value={eventData.assignedTo || ''}
|
||||
onChange={(e) => handleInputChange('assignedTo', e.target.value)}
|
||||
className="w-full px-2.5 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>
|
||||
|
|
@ -319,10 +274,8 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
|||
</label>
|
||||
<input
|
||||
type="date"
|
||||
value={eventData.date?.toISOString().split("T")[0] || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("date", new Date(e.target.value))
|
||||
}
|
||||
value={eventData.date?.toISOString().split('T')[0] || ''}
|
||||
onChange={(e) => handleInputChange('date', new Date(e.target.value))}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -333,10 +286,10 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
|||
Başlangıç Saati *
|
||||
</label>
|
||||
<select
|
||||
value={eventData.startTime || "09:00"}
|
||||
onChange={(e) => handleInputChange("startTime", e.target.value)}
|
||||
value={eventData.startTime || '09:00'}
|
||||
onChange={(e) => handleInputChange('startTime', 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.startTime ? "border-red-500" : "border-gray-300"
|
||||
errors.startTime ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
{timeOptions.map((time) => (
|
||||
|
|
@ -345,20 +298,16 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
|||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.startTime && (
|
||||
<p className="text-red-500 text-xs mt-1">{errors.startTime}</p>
|
||||
)}
|
||||
{errors.startTime && <p className="text-red-500 text-xs mt-1">{errors.startTime}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Bitiş Saati *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Bitiş Saati *</label>
|
||||
<select
|
||||
value={eventData.endTime || "10:00"}
|
||||
onChange={(e) => handleInputChange("endTime", e.target.value)}
|
||||
value={eventData.endTime || '10:00'}
|
||||
onChange={(e) => handleInputChange('endTime', 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.endTime ? "border-red-500" : "border-gray-300"
|
||||
errors.endTime ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
{timeOptions.map((time) => (
|
||||
|
|
@ -367,44 +316,29 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
|||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.endTime && (
|
||||
<p className="text-red-500 text-xs mt-1">{errors.endTime}</p>
|
||||
)}
|
||||
{errors.endTime && <p className="text-red-500 text-xs mt-1">{errors.endTime}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Status */}
|
||||
{eventData.type === "workorder" && (
|
||||
{eventData.type === 'workorder' && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Durum
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Durum</label>
|
||||
<select
|
||||
value={eventData.status || "scheduled"}
|
||||
value={eventData.status || 'scheduled'}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"status",
|
||||
e.target.value as WorkOrderStatusEnum | "scheduled"
|
||||
)
|
||||
handleInputChange('status', e.target.value as WorkOrderStatusEnum | 'scheduled')
|
||||
}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="scheduled">Planlanmış</option>
|
||||
<option value={WorkOrderStatusEnum.Created}>Oluşturuldu</option>
|
||||
<option value={WorkOrderStatusEnum.Planned}>Planlandı</option>
|
||||
<option value={WorkOrderStatusEnum.Released}>
|
||||
Serbest Bırakıldı
|
||||
</option>
|
||||
<option value={WorkOrderStatusEnum.InProgress}>
|
||||
Devam Ediyor
|
||||
</option>
|
||||
<option value={WorkOrderStatusEnum.Released}>Serbest Bırakıldı</option>
|
||||
<option value={WorkOrderStatusEnum.InProgress}>Devam Ediyor</option>
|
||||
<option value={WorkOrderStatusEnum.OnHold}>Beklemede</option>
|
||||
<option value={WorkOrderStatusEnum.Completed}>
|
||||
Tamamlandı
|
||||
</option>
|
||||
<option value={WorkOrderStatusEnum.Cancelled}>
|
||||
İptal Edildi
|
||||
</option>
|
||||
<option value={WorkOrderStatusEnum.Completed}>Tamamlandı</option>
|
||||
<option value={WorkOrderStatusEnum.Cancelled}>İptal Edildi</option>
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -428,7 +362,7 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default NewCalendarEventModal;
|
||||
export default NewCalendarEventModal
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaSave, FaUpload, FaMinus } from "react-icons/fa";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaSave, FaUpload, FaMinus } from 'react-icons/fa'
|
||||
import {
|
||||
PmFaultNotification,
|
||||
FaultTypeEnum,
|
||||
CriticalityLevelEnum,
|
||||
NotificationStatusEnum,
|
||||
} from "../../../types/pm";
|
||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
} from '../../../types/pm'
|
||||
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
|
||||
interface NewFaultNotificationModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (notification: Partial<PmFaultNotification>) => void;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (notification: Partial<PmFaultNotification>) => void
|
||||
}
|
||||
|
||||
const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||
|
|
@ -21,12 +21,8 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
onClose,
|
||||
onSave,
|
||||
}) => {
|
||||
const [notificationData, setNotificationData] = useState<
|
||||
Partial<PmFaultNotification>
|
||||
>({
|
||||
notificationCode: `ARZ-${new Date().getFullYear()}-${String(
|
||||
Date.now()
|
||||
).slice(-3)}`,
|
||||
const [notificationData, setNotificationData] = useState<Partial<PmFaultNotification>>({
|
||||
notificationCode: `ARZ-${new Date().getFullYear()}-${String(Date.now()).slice(-3)}`,
|
||||
faultType: FaultTypeEnum.Mechanical,
|
||||
priority: PriorityEnum.Normal,
|
||||
severity: CriticalityLevelEnum.Medium,
|
||||
|
|
@ -34,57 +30,55 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
followUpRequired: false,
|
||||
isActive: true,
|
||||
images: [],
|
||||
});
|
||||
})
|
||||
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [uploadedImages, setUploadedImages] = useState<string[]>([]);
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
const [uploadedImages, setUploadedImages] = useState<string[]>([])
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (!notificationData.workCenterId) {
|
||||
newErrors.workCenterId = "İş merkezi seçimi gerekli";
|
||||
newErrors.workCenterId = 'İş merkezi seçimi gerekli'
|
||||
}
|
||||
if (!notificationData.title?.trim()) {
|
||||
newErrors.title = "Başlık gerekli";
|
||||
newErrors.title = 'Başlık gerekli'
|
||||
}
|
||||
if (!notificationData.description?.trim()) {
|
||||
newErrors.description = "Açıklama gerekli";
|
||||
newErrors.description = 'Açıklama gerekli'
|
||||
}
|
||||
if (!notificationData.location?.trim()) {
|
||||
newErrors.location = "Konum gerekli";
|
||||
newErrors.location = 'Konum gerekli'
|
||||
}
|
||||
if (!notificationData.reportedBy?.trim()) {
|
||||
newErrors.reportedBy = "Bildiren kişi gerekli";
|
||||
newErrors.reportedBy = 'Bildiren kişi gerekli'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleInputChange = (
|
||||
field: keyof PmFaultNotification,
|
||||
value: string | number | boolean | undefined
|
||||
value: string | number | boolean | undefined,
|
||||
) => {
|
||||
setNotificationData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
}))
|
||||
// Clear error when user starts typing
|
||||
if (errors[field]) {
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
[field]: "",
|
||||
}));
|
||||
[field]: '',
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleWorkCenterChange = (workCenterId: string) => {
|
||||
const selectedWorkCenter = mockWorkCenters.find(
|
||||
(eq) => eq.id === workCenterId
|
||||
);
|
||||
const selectedWorkCenter = mockWorkCenters.find((eq) => eq.id === workCenterId)
|
||||
if (selectedWorkCenter) {
|
||||
setNotificationData((prev) => ({
|
||||
...prev,
|
||||
|
|
@ -92,29 +86,29 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
workCenterCode: selectedWorkCenter.code,
|
||||
workCenterName: selectedWorkCenter.name,
|
||||
location: selectedWorkCenter.location,
|
||||
}));
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleImageUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = event.target.files;
|
||||
const files = event.target.files
|
||||
if (files) {
|
||||
const imageNames = Array.from(files).map((file) => file.name);
|
||||
setUploadedImages((prev) => [...prev, ...imageNames]);
|
||||
const imageNames = Array.from(files).map((file) => file.name)
|
||||
setUploadedImages((prev) => [...prev, ...imageNames])
|
||||
setNotificationData((prev) => ({
|
||||
...prev,
|
||||
images: [...(prev.images || []), ...imageNames],
|
||||
}));
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const removeImage = (index: number) => {
|
||||
setUploadedImages((prev) => prev.filter((_, i) => i !== index));
|
||||
setUploadedImages((prev) => prev.filter((_, i) => i !== index))
|
||||
setNotificationData((prev) => ({
|
||||
...prev,
|
||||
images: prev.images?.filter((_, i) => i !== index) || [],
|
||||
}));
|
||||
};
|
||||
}))
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
if (validateForm()) {
|
||||
|
|
@ -124,16 +118,14 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
reportedAt: new Date(),
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
onSave(notificationToSave);
|
||||
onClose();
|
||||
onSave(notificationToSave)
|
||||
onClose()
|
||||
|
||||
// Reset form
|
||||
setNotificationData({
|
||||
notificationCode: `ARZ-${new Date().getFullYear()}-${String(
|
||||
Date.now()
|
||||
).slice(-3)}`,
|
||||
notificationCode: `ARZ-${new Date().getFullYear()}-${String(Date.now()).slice(-3)}`,
|
||||
faultType: FaultTypeEnum.Mechanical,
|
||||
priority: PriorityEnum.Normal,
|
||||
severity: CriticalityLevelEnum.Medium,
|
||||
|
|
@ -141,24 +133,19 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
followUpRequired: false,
|
||||
isActive: true,
|
||||
images: [],
|
||||
});
|
||||
setUploadedImages([]);
|
||||
setErrors({});
|
||||
})
|
||||
setUploadedImages([])
|
||||
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-3xl max-h-[90vh] overflow-y-auto">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||
<h2 className="text-xl font-bold text-gray-900">
|
||||
Yeni Arıza Bildirimi
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
||||
>
|
||||
<h2 className="text-xl font-bold text-gray-900">Yeni Arıza Bildirimi</h2>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -173,24 +160,20 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={notificationData.notificationCode || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("notificationCode", e.target.value)
|
||||
}
|
||||
value={notificationData.notificationCode || ''}
|
||||
onChange={(e) => handleInputChange('notificationCode', e.target.value)}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="ARZ-2024-001"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
İş Merkezi *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||
<select
|
||||
value={notificationData.workCenterId || ""}
|
||||
value={notificationData.workCenterId || ''}
|
||||
onChange={(e) => handleWorkCenterChange(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.workCenterId ? "border-red-500" : "border-gray-300"
|
||||
errors.workCenterId ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">İş merkezi seçin</option>
|
||||
|
|
@ -201,9 +184,7 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
))}
|
||||
</select>
|
||||
{errors.workCenterId && (
|
||||
<p className="text-red-500 text-xs mt-1">
|
||||
{errors.workCenterId}
|
||||
</p>
|
||||
<p className="text-red-500 text-xs mt-1">{errors.workCenterId}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -216,16 +197,14 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={notificationData.title || ""}
|
||||
onChange={(e) => handleInputChange("title", e.target.value)}
|
||||
value={notificationData.title || ''}
|
||||
onChange={(e) => handleInputChange('title', 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.title ? "border-red-500" : "border-gray-300"
|
||||
errors.title ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Arıza başlığı"
|
||||
/>
|
||||
{errors.title && (
|
||||
<p className="text-red-500 text-xs mt-1">{errors.title}</p>
|
||||
)}
|
||||
{errors.title && <p className="text-red-500 text-xs mt-1">{errors.title}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
@ -233,20 +212,16 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
Detaylı Açıklama *
|
||||
</label>
|
||||
<textarea
|
||||
value={notificationData.description || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("description", e.target.value)
|
||||
}
|
||||
value={notificationData.description || ''}
|
||||
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||
rows={2}
|
||||
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.description ? "border-red-500" : "border-gray-300"
|
||||
errors.description ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Arızanın detaylı açıklaması, belirtiler ve gözlemler"
|
||||
/>
|
||||
{errors.description && (
|
||||
<p className="text-red-500 text-xs mt-1">
|
||||
{errors.description}
|
||||
</p>
|
||||
<p className="text-red-500 text-xs mt-1">{errors.description}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -254,21 +229,17 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
{/* Location and Reporter */}
|
||||
<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">
|
||||
Konum *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Konum *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={notificationData.location || ""}
|
||||
onChange={(e) => handleInputChange("location", e.target.value)}
|
||||
value={notificationData.location || ''}
|
||||
onChange={(e) => handleInputChange('location', 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.location ? "border-red-500" : "border-gray-300"
|
||||
errors.location ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Atölye A - Hat 1"
|
||||
/>
|
||||
{errors.location && (
|
||||
<p className="text-red-500 text-xs mt-1">{errors.location}</p>
|
||||
)}
|
||||
{errors.location && <p className="text-red-500 text-xs mt-1">{errors.location}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
@ -276,12 +247,10 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
Bildiren Kişi *
|
||||
</label>
|
||||
<select
|
||||
value={notificationData.reportedBy || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("reportedBy", e.target.value)
|
||||
}
|
||||
value={notificationData.reportedBy || ''}
|
||||
onChange={(e) => handleInputChange('reportedBy', 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.reportedBy ? "border-red-500" : "border-gray-300"
|
||||
errors.reportedBy ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">Bildiren kişi seçin</option>
|
||||
|
|
@ -299,17 +268,10 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Arıza Türü
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Arıza Türü</label>
|
||||
<select
|
||||
value={notificationData.faultType || FaultTypeEnum.Mechanical}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"faultType",
|
||||
e.target.value as FaultTypeEnum
|
||||
)
|
||||
}
|
||||
onChange={(e) => handleInputChange('faultType', e.target.value as FaultTypeEnum)}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value={FaultTypeEnum.Mechanical}>Mekanik</option>
|
||||
|
|
@ -324,14 +286,10 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Öncelik
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Öncelik</label>
|
||||
<select
|
||||
value={notificationData.priority || PriorityEnum.Normal}
|
||||
onChange={(e) =>
|
||||
handleInputChange("priority", e.target.value as PriorityEnum)
|
||||
}
|
||||
onChange={(e) => handleInputChange('priority', e.target.value as PriorityEnum)}
|
||||
className="w-full px-2.5 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>
|
||||
|
|
@ -342,16 +300,11 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Kritiklik
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Kritiklik</label>
|
||||
<select
|
||||
value={notificationData.severity || CriticalityLevelEnum.Medium}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"severity",
|
||||
e.target.value as CriticalityLevelEnum
|
||||
)
|
||||
handleInputChange('severity', e.target.value as CriticalityLevelEnum)
|
||||
}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
|
|
@ -371,12 +324,9 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={notificationData.estimatedRepairTime || ""}
|
||||
value={notificationData.estimatedRepairTime || ''}
|
||||
onChange={(e) =>
|
||||
handleInputChange(
|
||||
"estimatedRepairTime",
|
||||
parseInt(e.target.value) || undefined
|
||||
)
|
||||
handleInputChange('estimatedRepairTime', parseInt(e.target.value) || undefined)
|
||||
}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="120"
|
||||
|
|
@ -389,23 +339,17 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
<input
|
||||
type="checkbox"
|
||||
checked={notificationData.followUpRequired || false}
|
||||
onChange={(e) =>
|
||||
handleInputChange("followUpRequired", e.target.checked)
|
||||
}
|
||||
onChange={(e) => handleInputChange('followUpRequired', e.target.checked)}
|
||||
className="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
|
||||
/>
|
||||
<span className="ml-2 text-sm text-gray-700">
|
||||
Takip gerekli
|
||||
</span>
|
||||
<span className="ml-2 text-sm text-gray-700">Takip gerekli</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Image Upload */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Fotoğraflar
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Fotoğraflar</label>
|
||||
<div className="border-2 border-dashed border-gray-300 rounded-lg p-4">
|
||||
<div className="text-center">
|
||||
<FaUpload className="mx-auto h-10 w-10 text-gray-400" />
|
||||
|
|
@ -433,9 +377,7 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
|
||||
{uploadedImages.length > 0 && (
|
||||
<div className="mt-4">
|
||||
<h4 className="text-sm font-medium text-gray-700 mb-1">
|
||||
Yüklenen Fotoğraflar:
|
||||
</h4>
|
||||
<h4 className="text-sm font-medium text-gray-700 mb-1">Yüklenen Fotoğraflar:</h4>
|
||||
<div className="space-y-2">
|
||||
{uploadedImages.map((image, index) => (
|
||||
<div
|
||||
|
|
@ -476,7 +418,7 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default NewFaultNotificationModal;
|
||||
export default NewFaultNotificationModal
|
||||
|
|
|
|||
|
|
@ -1,109 +1,93 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||
import {
|
||||
PmMaintenancePlan,
|
||||
MaintenancePlanTypeEnum,
|
||||
FrequencyUnitEnum,
|
||||
PmPlanMaterial,
|
||||
} from "../../../types/pm";
|
||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
||||
import { mockMaterials } from "../../../mocks/mockMaterials";
|
||||
import { mockUnits } from "../../../mocks/mockUnits";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
} from '../../../types/pm'
|
||||
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||
import { mockMaterials } from '../../../mocks/mockMaterials'
|
||||
import { mockUnits } from '../../../mocks/mockUnits'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
|
||||
interface NewMaintenancePlanModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (plan: Partial<PmMaintenancePlan>) => void;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (plan: Partial<PmMaintenancePlan>) => void
|
||||
}
|
||||
|
||||
const initialPlan: Partial<PmMaintenancePlan> = {
|
||||
planCode: "",
|
||||
workCenterId: "",
|
||||
planCode: '',
|
||||
workCenterId: '',
|
||||
planType: MaintenancePlanTypeEnum.Preventive,
|
||||
description: "",
|
||||
description: '',
|
||||
frequency: 1,
|
||||
frequencyUnit: FrequencyUnitEnum.Months,
|
||||
estimatedDuration: 60,
|
||||
priority: PriorityEnum.Normal,
|
||||
instructions: "",
|
||||
instructions: '',
|
||||
requiredMaterials: [],
|
||||
requiredSkills: [],
|
||||
nextDue: new Date(),
|
||||
isActive: true,
|
||||
};
|
||||
}
|
||||
|
||||
const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSave,
|
||||
}) => {
|
||||
const [planData, setPlanData] =
|
||||
useState<Partial<PmMaintenancePlan>>(initialPlan);
|
||||
const [requiredSkills, setRequiredSkills] = useState<string[]>([]);
|
||||
const [requiredMaterials, setRequiredMaterials] = useState<PmPlanMaterial[]>(
|
||||
[]
|
||||
);
|
||||
const [newSkill, setNewSkill] = useState("");
|
||||
const [planData, setPlanData] = useState<Partial<PmMaintenancePlan>>(initialPlan)
|
||||
const [requiredSkills, setRequiredSkills] = useState<string[]>([])
|
||||
const [requiredMaterials, setRequiredMaterials] = useState<PmPlanMaterial[]>([])
|
||||
const [newSkill, setNewSkill] = useState('')
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<
|
||||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
||||
>
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
const { name, value, type } = e.target;
|
||||
const { name, value, type } = e.target
|
||||
setPlanData((prev) => ({
|
||||
...prev,
|
||||
[name]:
|
||||
type === "date"
|
||||
? new Date(value)
|
||||
: type === "number"
|
||||
? Number(value)
|
||||
: value,
|
||||
}));
|
||||
};
|
||||
[name]: type === 'date' ? new Date(value) : type === 'number' ? Number(value) : value,
|
||||
}))
|
||||
}
|
||||
|
||||
const addSkill = () => {
|
||||
if (newSkill.trim() && !requiredSkills.includes(newSkill.trim())) {
|
||||
setRequiredSkills([...requiredSkills, newSkill.trim()]);
|
||||
setNewSkill("");
|
||||
setRequiredSkills([...requiredSkills, newSkill.trim()])
|
||||
setNewSkill('')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const removeSkill = (skillToRemove: string) => {
|
||||
setRequiredSkills(
|
||||
requiredSkills.filter((skill) => skill !== skillToRemove)
|
||||
);
|
||||
};
|
||||
setRequiredSkills(requiredSkills.filter((skill) => skill !== skillToRemove))
|
||||
}
|
||||
|
||||
const addMaterial = () => {
|
||||
const newMaterial: PmPlanMaterial = {
|
||||
id: `MAT${Date.now()}`,
|
||||
planId: "",
|
||||
materialId: "",
|
||||
planId: '',
|
||||
materialId: '',
|
||||
quantity: 1,
|
||||
unitId: "",
|
||||
unitId: '',
|
||||
isRequired: true,
|
||||
};
|
||||
setRequiredMaterials([...requiredMaterials, newMaterial]);
|
||||
};
|
||||
}
|
||||
setRequiredMaterials([...requiredMaterials, newMaterial])
|
||||
}
|
||||
|
||||
const removeMaterial = (index: number) => {
|
||||
setRequiredMaterials(requiredMaterials.filter((_, i) => i !== index));
|
||||
};
|
||||
setRequiredMaterials(requiredMaterials.filter((_, i) => i !== index))
|
||||
}
|
||||
|
||||
const updateMaterial = (
|
||||
index: number,
|
||||
field: string,
|
||||
value: string | number | boolean
|
||||
) => {
|
||||
const updateMaterial = (index: number, field: string, value: string | number | boolean) => {
|
||||
const updated = requiredMaterials.map((material, i) =>
|
||||
i === index ? { ...material, [field]: value } : material
|
||||
);
|
||||
setRequiredMaterials(updated);
|
||||
};
|
||||
i === index ? { ...material, [field]: value } : material,
|
||||
)
|
||||
setRequiredMaterials(updated)
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
const planToSave = {
|
||||
|
|
@ -112,26 +96,21 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
requiredMaterials,
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
onSave(planToSave);
|
||||
setPlanData(initialPlan);
|
||||
setRequiredSkills([]);
|
||||
setRequiredMaterials([]);
|
||||
onClose();
|
||||
};
|
||||
}
|
||||
onSave(planToSave)
|
||||
setPlanData(initialPlan)
|
||||
setRequiredSkills([])
|
||||
setRequiredMaterials([])
|
||||
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-4xl 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">
|
||||
Yeni Bakım Planı
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
>
|
||||
<h2 className="text-lg font-semibold text-gray-900">Yeni Bakım Planı</h2>
|
||||
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -140,18 +119,14 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
<div className="p-4 space-y-4">
|
||||
{/* Basic Information */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Temel Bilgiler
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Temel Bilgiler</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-2">
|
||||
Plan Kodu *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Plan Kodu *</label>
|
||||
<input
|
||||
type="text"
|
||||
name="planCode"
|
||||
value={planData.planCode || ""}
|
||||
value={planData.planCode || ''}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="örn: PM-CNC-001"
|
||||
|
|
@ -159,12 +134,10 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
İş Merkezi *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||
<select
|
||||
name="workCenterId"
|
||||
value={planData.workCenterId || ""}
|
||||
value={planData.workCenterId || ''}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
required
|
||||
|
|
@ -178,35 +151,21 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Plan Tipi *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Plan Tipi *</label>
|
||||
<select
|
||||
name="planType"
|
||||
value={
|
||||
planData.planType || MaintenancePlanTypeEnum.Preventive
|
||||
}
|
||||
value={planData.planType || MaintenancePlanTypeEnum.Preventive}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 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>
|
||||
<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-2">
|
||||
Öncelik
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Öncelik</label>
|
||||
<select
|
||||
name="priority"
|
||||
value={planData.priority || PriorityEnum.Normal}
|
||||
|
|
@ -220,12 +179,10 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
</select>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Açıklama *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama *</label>
|
||||
<textarea
|
||||
name="description"
|
||||
value={planData.description || ""}
|
||||
value={planData.description || ''}
|
||||
onChange={handleInputChange}
|
||||
rows={2}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
|
|
@ -238,14 +195,10 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
|
||||
{/* Frequency Settings */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Sıklık Ayarları
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Sıklık Ayarları</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Sıklık *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Sıklık *</label>
|
||||
<input
|
||||
type="number"
|
||||
name="frequency"
|
||||
|
|
@ -295,11 +248,7 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
<input
|
||||
type="date"
|
||||
name="nextDue"
|
||||
value={
|
||||
planData.nextDue
|
||||
? planData.nextDue.toISOString().split("T")[0]
|
||||
: ""
|
||||
}
|
||||
value={planData.nextDue ? planData.nextDue.toISOString().split('T')[0] : ''}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
|
|
@ -309,12 +258,10 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
|
||||
{/* Instructions */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Bakım Talimatları
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Bakım Talimatları</h3>
|
||||
<textarea
|
||||
name="instructions"
|
||||
value={planData.instructions || ""}
|
||||
value={planData.instructions || ''}
|
||||
onChange={handleInputChange}
|
||||
rows={3}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
|
|
@ -325,9 +272,7 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
{/* Required Skills */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-base font-medium text-gray-900">
|
||||
Gerekli Yetenekler
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900">Gerekli Yetenekler</h3>
|
||||
<div className="flex space-x-1.5">
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -335,7 +280,7 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
onChange={(e) => setNewSkill(e.target.value)}
|
||||
placeholder="Yetenek ekle..."
|
||||
className="px-2.5 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()}
|
||||
onKeyPress={(e) => e.key === 'Enter' && addSkill()}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -368,9 +313,7 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
{/* Required Materials */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-base font-medium text-gray-900">
|
||||
Gerekli Malzemeler
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900">Gerekli Malzemeler</h3>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addMaterial}
|
||||
|
|
@ -382,23 +325,14 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
</div>
|
||||
<div className="space-y-3">
|
||||
{requiredMaterials.map((material, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg"
|
||||
>
|
||||
<div key={index} className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg">
|
||||
<select
|
||||
value={material.materialId}
|
||||
onChange={(e) => {
|
||||
const selectedMaterial = mockMaterials.find(
|
||||
(m) => m.id === e.target.value
|
||||
);
|
||||
updateMaterial(index, "materialId", e.target.value);
|
||||
const selectedMaterial = mockMaterials.find((m) => m.id === e.target.value)
|
||||
updateMaterial(index, 'materialId', e.target.value)
|
||||
if (selectedMaterial) {
|
||||
updateMaterial(
|
||||
index,
|
||||
"unitId",
|
||||
selectedMaterial.baseUnitId
|
||||
);
|
||||
updateMaterial(index, 'unitId', selectedMaterial.baseUnitId)
|
||||
}
|
||||
}}
|
||||
className="flex-1 px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
|
|
@ -413,18 +347,14 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
<input
|
||||
type="number"
|
||||
value={material.quantity}
|
||||
onChange={(e) =>
|
||||
updateMaterial(index, "quantity", Number(e.target.value))
|
||||
}
|
||||
onChange={(e) => updateMaterial(index, 'quantity', Number(e.target.value))}
|
||||
placeholder="Miktar"
|
||||
min="1"
|
||||
className="w-20 px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
<select
|
||||
value={material.unitId}
|
||||
onChange={(e) =>
|
||||
updateMaterial(index, "unitId", e.target.value)
|
||||
}
|
||||
onChange={(e) => updateMaterial(index, 'unitId', e.target.value)}
|
||||
className="w-24 px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="">Birim</option>
|
||||
|
|
@ -437,32 +367,25 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
{material.materialId &&
|
||||
(() => {
|
||||
const selectedMaterial = mockMaterials.find(
|
||||
(m) => m.id === material.materialId
|
||||
);
|
||||
const baseUnitCode = selectedMaterial?.baseUnit;
|
||||
const existsInList = mockUnits.some(
|
||||
(unit) => unit.id === baseUnitCode?.id
|
||||
);
|
||||
(m) => m.id === material.materialId,
|
||||
)
|
||||
const baseUnitCode = selectedMaterial?.baseUnit
|
||||
const existsInList = mockUnits.some((unit) => unit.id === baseUnitCode?.id)
|
||||
if (baseUnitCode && !existsInList) {
|
||||
return (
|
||||
<option
|
||||
key={baseUnitCode.id}
|
||||
value={baseUnitCode.id}
|
||||
>
|
||||
<option key={baseUnitCode.id} value={baseUnitCode.id}>
|
||||
{baseUnitCode.code}
|
||||
</option>
|
||||
);
|
||||
)
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
})()}
|
||||
</select>
|
||||
<label className="flex items-center space-x-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={material.isRequired}
|
||||
onChange={(e) =>
|
||||
updateMaterial(index, "isRequired", e.target.checked)
|
||||
}
|
||||
onChange={(e) => updateMaterial(index, 'isRequired', e.target.checked)}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">Zorunlu</span>
|
||||
|
|
@ -478,8 +401,8 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
))}
|
||||
{requiredMaterials.length === 0 && (
|
||||
<p className="text-gray-500 text-center py-4">
|
||||
Henüz malzeme eklenmedi. "Malzeme Ekle" butonunu kullanarak
|
||||
malzeme ekleyebilirsiniz.
|
||||
Henüz malzeme eklenmedi. "Malzeme Ekle" butonunu kullanarak malzeme
|
||||
ekleyebilirsiniz.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -506,7 +429,7 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default NewMaintenancePlanModal;
|
||||
export default NewMaintenancePlanModal
|
||||
|
|
|
|||
|
|
@ -1,170 +1,151 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
||||
import { TeamRoleEnum } from "../../../types/common";
|
||||
import MultiSelectEmployee from "../../../components/common/MultiSelectEmployee";
|
||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
||||
import { Team, TeamMember } from "../../../types/common";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||
import { TeamRoleEnum } from '../../../types/common'
|
||||
import MultiSelectEmployee from '../../../components/common/MultiSelectEmployee'
|
||||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||
import { Team, TeamMember } from '../../../types/common'
|
||||
|
||||
interface NewTeamModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (team: Partial<Team>) => void;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (team: Partial<Team>) => void
|
||||
}
|
||||
|
||||
const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSave,
|
||||
}) => {
|
||||
const NewTeamModal: React.FC<NewTeamModalProps> = ({ isOpen, onClose, onSave }) => {
|
||||
const [teamData, setTeamData] = useState<Partial<Team>>({
|
||||
code: "",
|
||||
name: "",
|
||||
description: "",
|
||||
code: '',
|
||||
name: '',
|
||||
description: '',
|
||||
isActive: true,
|
||||
specializations: [],
|
||||
members: [],
|
||||
});
|
||||
})
|
||||
|
||||
const [selectedEmployees, setSelectedEmployees] = useState<string[]>([]);
|
||||
const [newSpecialization, setNewSpecialization] = useState("");
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [selectedEmployees, setSelectedEmployees] = useState<string[]>([])
|
||||
const [newSpecialization, setNewSpecialization] = useState('')
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (!teamData.code?.trim()) {
|
||||
newErrors.teamCode = "Ekip kodu gerekli";
|
||||
newErrors.teamCode = 'Ekip kodu gerekli'
|
||||
}
|
||||
if (!teamData.name?.trim()) {
|
||||
newErrors.teamName = "Ekip adı gerekli";
|
||||
newErrors.teamName = 'Ekip adı gerekli'
|
||||
}
|
||||
if (!teamData.description?.trim()) {
|
||||
newErrors.description = "Açıklama gerekli";
|
||||
newErrors.description = 'Açıklama gerekli'
|
||||
}
|
||||
if (!teamData.managerId) {
|
||||
newErrors.managerId = "Ekip yöneticisi seçilmeli";
|
||||
newErrors.managerId = 'Ekip yöneticisi seçilmeli'
|
||||
}
|
||||
if (selectedEmployees.length === 0) {
|
||||
newErrors.members = "En az bir ekip üyesi seçilmeli";
|
||||
newErrors.members = 'En az bir ekip üyesi seçilmeli'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleInputChange = (
|
||||
field: keyof Team,
|
||||
value: string | boolean | string[]
|
||||
) => {
|
||||
const handleInputChange = (field: keyof Team, value: string | boolean | string[]) => {
|
||||
setTeamData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
}))
|
||||
// Clear error when user starts typing
|
||||
if (errors[field]) {
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
[field]: "",
|
||||
}));
|
||||
[field]: '',
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const addSpecialization = () => {
|
||||
if (
|
||||
newSpecialization.trim() &&
|
||||
!teamData.specializations?.includes(newSpecialization.trim())
|
||||
) {
|
||||
if (newSpecialization.trim() && !teamData.specializations?.includes(newSpecialization.trim())) {
|
||||
setTeamData((prev) => ({
|
||||
...prev,
|
||||
specializations: [
|
||||
...(prev.specializations || []),
|
||||
newSpecialization.trim(),
|
||||
],
|
||||
}));
|
||||
setNewSpecialization("");
|
||||
specializations: [...(prev.specializations || []), newSpecialization.trim()],
|
||||
}))
|
||||
setNewSpecialization('')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const removeSpecialization = (index: number) => {
|
||||
setTeamData((prev) => ({
|
||||
...prev,
|
||||
specializations:
|
||||
prev.specializations?.filter((_, i) => i !== index) || [],
|
||||
}));
|
||||
};
|
||||
specializations: prev.specializations?.filter((_, i) => i !== index) || [],
|
||||
}))
|
||||
}
|
||||
|
||||
const handleEmployeeSelection = (employees: string[]) => {
|
||||
setSelectedEmployees(employees);
|
||||
setSelectedEmployees(employees)
|
||||
if (errors.members) {
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
members: "",
|
||||
}));
|
||||
members: '',
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleMemberRoleChange = (employeeName: string, role: TeamRoleEnum) => {
|
||||
const employee = mockEmployees.find((emp) => emp.fullName === employeeName);
|
||||
const employee = mockEmployees.find((emp) => emp.fullName === employeeName)
|
||||
if (role === TeamRoleEnum.Lead && employee) {
|
||||
handleInputChange("managerId", employee.id);
|
||||
handleInputChange('managerId', employee.id)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
if (validateForm()) {
|
||||
const members: TeamMember[] = selectedEmployees.map(
|
||||
(employeeName, index) => {
|
||||
const employee = mockEmployees.find(
|
||||
(emp) => emp.fullName === employeeName
|
||||
);
|
||||
const isLeader = index === 0; // First selected employee becomes leader
|
||||
const members: TeamMember[] = selectedEmployees.map((employeeName, index) => {
|
||||
const employee = mockEmployees.find((emp) => emp.fullName === employeeName)
|
||||
const isLeader = index === 0 // First selected employee becomes leader
|
||||
|
||||
return {
|
||||
id: `TM${Date.now()}-${index}`,
|
||||
teamId: "",
|
||||
employeeId: employee?.id || "",
|
||||
employee: employee,
|
||||
role: isLeader ? TeamRoleEnum.Lead : TeamRoleEnum.Member,
|
||||
joinDate: new Date(),
|
||||
isActive: true,
|
||||
};
|
||||
return {
|
||||
id: `TM${Date.now()}-${index}`,
|
||||
teamId: '',
|
||||
employeeId: employee?.id || '',
|
||||
employee: employee,
|
||||
role: isLeader ? TeamRoleEnum.Lead : TeamRoleEnum.Member,
|
||||
joinDate: new Date(),
|
||||
isActive: true,
|
||||
}
|
||||
);
|
||||
})
|
||||
|
||||
const teamId = `MT${Date.now()}`;
|
||||
const teamId = `MT${Date.now()}`
|
||||
|
||||
// Update members with the teamId
|
||||
members.forEach((member) => {
|
||||
member.teamId = teamId;
|
||||
});
|
||||
member.teamId = teamId
|
||||
})
|
||||
|
||||
const teamToSave: Partial<Team> = {
|
||||
...teamData,
|
||||
id: teamId,
|
||||
managerId: members.find((m) => m.role === TeamRoleEnum.Lead)
|
||||
?.employeeId,
|
||||
managerId: members.find((m) => m.role === TeamRoleEnum.Lead)?.employeeId,
|
||||
members,
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
onSave(teamToSave);
|
||||
onClose();
|
||||
onSave(teamToSave)
|
||||
onClose()
|
||||
|
||||
// Reset form
|
||||
setTeamData({
|
||||
code: "",
|
||||
name: "",
|
||||
description: "",
|
||||
code: '',
|
||||
name: '',
|
||||
description: '',
|
||||
isActive: true,
|
||||
specializations: [],
|
||||
members: [],
|
||||
});
|
||||
setSelectedEmployees([]);
|
||||
setNewSpecialization("");
|
||||
setErrors({});
|
||||
})
|
||||
setSelectedEmployees([])
|
||||
setNewSpecialization('')
|
||||
setErrors({})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
isOpen && (
|
||||
|
|
@ -172,9 +153,7 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
|||
<div className="bg-white rounded-lg w-full max-w-3xl 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">
|
||||
Yeni Ekip Oluştur
|
||||
</h2>
|
||||
<h2 className="text-lg font-semibold text-gray-900">Yeni Ekip Oluştur</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
||||
|
|
@ -188,91 +167,69 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
|||
{/* Basic Info */}
|
||||
<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">
|
||||
Ekip Kodu *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Ekip Kodu *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={teamData.code || ""}
|
||||
onChange={(e) => handleInputChange("code", e.target.value)}
|
||||
value={teamData.code || ''}
|
||||
onChange={(e) => handleInputChange('code', 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.code ? "border-red-500" : "border-gray-300"
|
||||
errors.code ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="MEC-001"
|
||||
/>
|
||||
{errors.code && (
|
||||
<p className="text-red-500 text-xs mt-1">{errors.code}</p>
|
||||
)}
|
||||
{errors.code && <p className="text-red-500 text-xs mt-1">{errors.code}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Ekip Adı *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Ekip Adı *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={teamData.name || ""}
|
||||
onChange={(e) => handleInputChange("name", e.target.value)}
|
||||
value={teamData.name || ''}
|
||||
onChange={(e) => handleInputChange('name', 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.name ? "border-red-500" : "border-gray-300"
|
||||
errors.name ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Mekanik Bakım Ekibi"
|
||||
/>
|
||||
{errors.name && (
|
||||
<p className="text-red-500 text-xs mt-1">{errors.name}</p>
|
||||
)}
|
||||
{errors.name && <p className="text-red-500 text-xs mt-1">{errors.name}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Açıklama *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama *</label>
|
||||
<textarea
|
||||
value={teamData.description || ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange("description", e.target.value)
|
||||
}
|
||||
value={teamData.description || ''}
|
||||
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||
rows={2}
|
||||
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.description ? "border-red-500" : "border-gray-300"
|
||||
errors.description ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Ekip açıklaması ve sorumlulukları"
|
||||
/>
|
||||
{errors.description && (
|
||||
<p className="text-red-500 text-xs mt-1">
|
||||
{errors.description}
|
||||
</p>
|
||||
<p className="text-red-500 text-xs mt-1">{errors.description}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Team Members */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Ekip Üyeleri *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Ekip Üyeleri *</label>
|
||||
<MultiSelectEmployee
|
||||
selectedEmployees={selectedEmployees}
|
||||
onChange={handleEmployeeSelection}
|
||||
placeholder="Ekip üyelerini seçin"
|
||||
className={errors.members ? "border-red-500" : ""}
|
||||
className={errors.members ? 'border-red-500' : ''}
|
||||
/>
|
||||
{errors.members && (
|
||||
<p className="text-red-500 text-xs mt-1">{errors.members}</p>
|
||||
)}
|
||||
{errors.members && <p className="text-red-500 text-xs mt-1">{errors.members}</p>}
|
||||
|
||||
{/* Selected Members Display */}
|
||||
{selectedEmployees.length > 0 && (
|
||||
<div className="mt-3 p-3 bg-gray-50 rounded-lg">
|
||||
<h4 className="text-sm font-medium text-gray-700 mb-2">
|
||||
Seçili Ekip Üyeleri:
|
||||
</h4>
|
||||
<h4 className="text-sm font-medium text-gray-700 mb-2">Seçili Ekip Üyeleri:</h4>
|
||||
<div className="space-y-1.5">
|
||||
{selectedEmployees.map((employeeName, index) => {
|
||||
const employee = mockEmployees.find(
|
||||
(emp) => emp.fullName === employeeName
|
||||
);
|
||||
const employee = mockEmployees.find((emp) => emp.fullName === employeeName)
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
|
|
@ -286,12 +243,8 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
|||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{employeeName}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
{employee?.jobPosition?.name}
|
||||
</p>
|
||||
<p className="text-sm font-medium text-gray-900">{employeeName}</p>
|
||||
<p className="text-sm text-gray-600">{employee?.jobPosition?.name}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
|
|
@ -302,25 +255,18 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
|||
: TeamRoleEnum.Member
|
||||
}
|
||||
onChange={(e) =>
|
||||
handleMemberRoleChange(
|
||||
employeeName,
|
||||
e.target.value as TeamRoleEnum
|
||||
)
|
||||
handleMemberRoleChange(employeeName, e.target.value as TeamRoleEnum)
|
||||
}
|
||||
className="text-xs px-1.5 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<option value={TeamRoleEnum.Member}>Üye</option>
|
||||
<option value={TeamRoleEnum.Lead}>Lider</option>
|
||||
<option value={TeamRoleEnum.Specialist}>
|
||||
Uzman
|
||||
</option>
|
||||
<option value={TeamRoleEnum.Manager}>
|
||||
Yönetici
|
||||
</option>
|
||||
<option value={TeamRoleEnum.Specialist}>Uzman</option>
|
||||
<option value={TeamRoleEnum.Manager}>Yönetici</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -341,9 +287,9 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
|||
className="flex-1 px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="Uzmanlık alanı ekle"
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
addSpecialization();
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
addSpecialization()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
|
@ -355,26 +301,25 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
|||
<FaPlus className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
{teamData.specializations &&
|
||||
teamData.specializations.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{teamData.specializations.map((spec, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="inline-flex items-center px-2.5 py-1 rounded-full text-xs bg-blue-100 text-blue-800"
|
||||
{teamData.specializations && teamData.specializations.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{teamData.specializations.map((spec, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="inline-flex items-center px-2.5 py-1 rounded-full text-xs bg-blue-100 text-blue-800"
|
||||
>
|
||||
{spec}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeSpecialization(index)}
|
||||
className="ml-2 text-blue-600 hover:text-blue-800"
|
||||
>
|
||||
{spec}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeSpecialization(index)}
|
||||
className="ml-2 text-blue-600 hover:text-blue-800"
|
||||
>
|
||||
<FaMinus className="w-3 h-3" />
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<FaMinus className="w-3 h-3" />
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -384,9 +329,7 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
|||
<input
|
||||
type="checkbox"
|
||||
checked={teamData.isActive || false}
|
||||
onChange={(e) =>
|
||||
handleInputChange("isActive", e.target.checked)
|
||||
}
|
||||
onChange={(e) => handleInputChange('isActive', e.target.checked)}
|
||||
className="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
|
||||
/>
|
||||
<span className="ml-2 text-sm text-gray-700">Ekip aktif</span>
|
||||
|
|
@ -413,7 +356,7 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default NewTeamModal;
|
||||
export default NewTeamModal
|
||||
|
|
|
|||
|
|
@ -1,95 +1,82 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||
import {
|
||||
PmWorkCenter,
|
||||
WorkCenterStatusEnum,
|
||||
CriticalityLevelEnum,
|
||||
PmWorkCenterSpecification,
|
||||
} from "../../../types/pm";
|
||||
} from '../../../types/pm'
|
||||
|
||||
interface NewWorkCenterModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (workCenter: Partial<PmWorkCenter>) => void;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (workCenter: Partial<PmWorkCenter>) => void
|
||||
}
|
||||
|
||||
const initialWorkCenter: Partial<PmWorkCenter> = {
|
||||
code: "",
|
||||
name: "",
|
||||
description: "",
|
||||
manufacturer: "",
|
||||
model: "",
|
||||
serialNumber: "",
|
||||
code: '',
|
||||
name: '',
|
||||
description: '',
|
||||
manufacturer: '',
|
||||
model: '',
|
||||
serialNumber: '',
|
||||
installationDate: new Date(),
|
||||
location: "",
|
||||
departmentId: "",
|
||||
location: '',
|
||||
departmentId: '',
|
||||
status: WorkCenterStatusEnum.Operational,
|
||||
criticality: CriticalityLevelEnum.Medium,
|
||||
specifications: [],
|
||||
isActive: true,
|
||||
};
|
||||
}
|
||||
|
||||
const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSave,
|
||||
}) => {
|
||||
const [workCenter, setWorkCenter] =
|
||||
useState<Partial<PmWorkCenter>>(initialWorkCenter);
|
||||
const [specifications, setSpecifications] = useState<
|
||||
PmWorkCenterSpecification[]
|
||||
>([]);
|
||||
const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({ isOpen, onClose, onSave }) => {
|
||||
const [workCenter, setWorkCenter] = useState<Partial<PmWorkCenter>>(initialWorkCenter)
|
||||
const [specifications, setSpecifications] = useState<PmWorkCenterSpecification[]>([])
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<
|
||||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
||||
>
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
const { name, value, type } = e.target;
|
||||
const { name, value, type } = e.target
|
||||
setWorkCenter((prev) => ({
|
||||
...prev,
|
||||
[name]: type === "date" ? new Date(value) : value,
|
||||
}));
|
||||
};
|
||||
[name]: type === 'date' ? new Date(value) : value,
|
||||
}))
|
||||
}
|
||||
|
||||
const addSpecification = () => {
|
||||
const newSpec: PmWorkCenterSpecification = {
|
||||
id: `SPEC${Date.now()}`,
|
||||
workCenterId: "",
|
||||
specificationName: "",
|
||||
specificationValue: "",
|
||||
unit: "",
|
||||
workCenterId: '',
|
||||
specificationName: '',
|
||||
specificationValue: '',
|
||||
unit: '',
|
||||
isRequired: false,
|
||||
};
|
||||
setSpecifications([...specifications, newSpec]);
|
||||
};
|
||||
}
|
||||
setSpecifications([...specifications, newSpec])
|
||||
}
|
||||
|
||||
const removeSpecification = (index: number) => {
|
||||
setSpecifications(specifications.filter((_, i) => i !== index));
|
||||
};
|
||||
setSpecifications(specifications.filter((_, i) => i !== index))
|
||||
}
|
||||
|
||||
const updateSpecification = (
|
||||
index: number,
|
||||
field: string,
|
||||
value: string | boolean
|
||||
) => {
|
||||
const updateSpecification = (index: number, field: string, value: string | boolean) => {
|
||||
const updated = specifications.map((spec, i) =>
|
||||
i === index ? { ...spec, [field]: value } : spec
|
||||
);
|
||||
setSpecifications(updated);
|
||||
};
|
||||
i === index ? { ...spec, [field]: value } : spec,
|
||||
)
|
||||
setSpecifications(updated)
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
const workCenterData = {
|
||||
...workCenter,
|
||||
specifications,
|
||||
workCenterType: {
|
||||
id: "ET001",
|
||||
code: "GENERAL",
|
||||
name: "Genel İş Merkezi",
|
||||
category: "Genel",
|
||||
id: 'ET001',
|
||||
code: 'GENERAL',
|
||||
name: 'Genel İş Merkezi',
|
||||
category: 'Genel',
|
||||
isActive: true,
|
||||
},
|
||||
maintenancePlans: [],
|
||||
|
|
@ -97,25 +84,20 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
downTimeHistory: [],
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
};
|
||||
onSave(workCenterData);
|
||||
setWorkCenter(initialWorkCenter);
|
||||
setSpecifications([]);
|
||||
onClose();
|
||||
};
|
||||
}
|
||||
onSave(workCenterData)
|
||||
setWorkCenter(initialWorkCenter)
|
||||
setSpecifications([])
|
||||
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-4xl 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">
|
||||
Yeni İş Merkezi Ekle
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
>
|
||||
<h2 className="text-lg font-semibold text-gray-900">Yeni İş Merkezi Ekle</h2>
|
||||
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -124,9 +106,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
<div className="p-4 space-y-4">
|
||||
{/* Basic Information */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Temel Bilgiler
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Temel Bilgiler</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-2">
|
||||
|
|
@ -135,7 +115,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
<input
|
||||
type="text"
|
||||
name="workCenterCode"
|
||||
value={workCenter.code || ""}
|
||||
value={workCenter.code || ''}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="örn: CNC-001"
|
||||
|
|
@ -149,7 +129,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={workCenter.name || ""}
|
||||
value={workCenter.name || ''}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="örn: CNC Torna Tezgahı"
|
||||
|
|
@ -157,12 +137,10 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Açıklama
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama</label>
|
||||
<textarea
|
||||
name="description"
|
||||
value={workCenter.description || ""}
|
||||
value={workCenter.description || ''}
|
||||
onChange={handleInputChange}
|
||||
rows={2}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
|
|
@ -174,31 +152,25 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
|
||||
{/* Technical Details */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Teknik Detaylar
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Teknik 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-2">
|
||||
Üretici
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Üretici</label>
|
||||
<input
|
||||
type="text"
|
||||
name="manufacturer"
|
||||
value={workCenter.manufacturer || ""}
|
||||
value={workCenter.manufacturer || ''}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="örn: HAAS Automation"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Model
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Model</label>
|
||||
<input
|
||||
type="text"
|
||||
name="model"
|
||||
value={workCenter.model || ""}
|
||||
value={workCenter.model || ''}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="örn: ST-30"
|
||||
|
|
@ -211,7 +183,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
<input
|
||||
type="text"
|
||||
name="serialNumber"
|
||||
value={workCenter.serialNumber || ""}
|
||||
value={workCenter.serialNumber || ''}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="örn: SN123456789"
|
||||
|
|
@ -226,8 +198,8 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
name="installationDate"
|
||||
value={
|
||||
workCenter.installationDate
|
||||
? workCenter.installationDate.toISOString().split("T")[0]
|
||||
: ""
|
||||
? workCenter.installationDate.toISOString().split('T')[0]
|
||||
: ''
|
||||
}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
|
|
@ -243,21 +215,19 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
name="warrantyExpiry"
|
||||
value={
|
||||
workCenter.warrantyExpiry
|
||||
? workCenter.warrantyExpiry.toISOString().split("T")[0]
|
||||
: ""
|
||||
? workCenter.warrantyExpiry.toISOString().split('T')[0]
|
||||
: ''
|
||||
}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Lokasyon *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Lokasyon *</label>
|
||||
<input
|
||||
type="text"
|
||||
name="location"
|
||||
value={workCenter.location || ""}
|
||||
value={workCenter.location || ''}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="örn: Atölye A - Hat 1"
|
||||
|
|
@ -269,29 +239,19 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
|
||||
{/* Status and Priority */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Durum ve Öncelik
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Durum ve Öncelik</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-2">
|
||||
Durum
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
||||
<select
|
||||
name="status"
|
||||
value={workCenter.status || WorkCenterStatusEnum.Operational}
|
||||
onChange={handleInputChange}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value={WorkCenterStatusEnum.Operational}>
|
||||
Operasyonel
|
||||
</option>
|
||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>
|
||||
Bakımda
|
||||
</option>
|
||||
<option value={WorkCenterStatusEnum.OutOfOrder}>
|
||||
Arızalı
|
||||
</option>
|
||||
<option value={WorkCenterStatusEnum.Operational}>Operasyonel</option>
|
||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>Bakımda</option>
|
||||
<option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option>
|
||||
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -317,9 +277,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
{/* Specifications */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-base font-medium text-gray-900">
|
||||
Teknik Özellikler
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900">Teknik Özellikler</h3>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addSpecification}
|
||||
|
|
@ -331,19 +289,12 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
</div>
|
||||
<div className="space-y-3">
|
||||
{specifications.map((spec, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg"
|
||||
>
|
||||
<div key={index} className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg">
|
||||
<input
|
||||
type="text"
|
||||
value={spec.specificationName}
|
||||
onChange={(e) =>
|
||||
updateSpecification(
|
||||
index,
|
||||
"specificationName",
|
||||
e.target.value
|
||||
)
|
||||
updateSpecification(index, 'specificationName', e.target.value)
|
||||
}
|
||||
placeholder="Özellik adı"
|
||||
className="flex-1 px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
|
|
@ -352,21 +303,15 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
type="text"
|
||||
value={spec.specificationValue}
|
||||
onChange={(e) =>
|
||||
updateSpecification(
|
||||
index,
|
||||
"specificationValue",
|
||||
e.target.value
|
||||
)
|
||||
updateSpecification(index, 'specificationValue', e.target.value)
|
||||
}
|
||||
placeholder="Değer"
|
||||
className="flex-1 px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={spec.unit || ""}
|
||||
onChange={(e) =>
|
||||
updateSpecification(index, "unit", e.target.value)
|
||||
}
|
||||
value={spec.unit || ''}
|
||||
onChange={(e) => updateSpecification(index, 'unit', e.target.value)}
|
||||
placeholder="Birim"
|
||||
className="w-20 px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
|
|
@ -374,13 +319,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
<input
|
||||
type="checkbox"
|
||||
checked={spec.isRequired}
|
||||
onChange={(e) =>
|
||||
updateSpecification(
|
||||
index,
|
||||
"isRequired",
|
||||
e.target.checked
|
||||
)
|
||||
}
|
||||
onChange={(e) => updateSpecification(index, 'isRequired', e.target.checked)}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500 h-4 w-4"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">Zorunlu</span>
|
||||
|
|
@ -396,8 +335,8 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
))}
|
||||
{specifications.length === 0 && (
|
||||
<p className="text-gray-500 text-center py-4">
|
||||
Henüz teknik özellik eklenmedi. "Özellik Ekle" butonunu
|
||||
kullanarak özellik ekleyebilirsiniz.
|
||||
Henüz teknik özellik eklenmedi. "Özellik Ekle" butonunu kullanarak özellik
|
||||
ekleyebilirsiniz.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -424,7 +363,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default NewWorkCenterModal;
|
||||
export default NewWorkCenterModal
|
||||
|
|
|
|||
|
|
@ -1,85 +1,76 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaPlus, FaTrash, FaCalendar, FaClock } from "react-icons/fa";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaPlus, FaTrash, FaCalendar, FaClock } from 'react-icons/fa'
|
||||
import {
|
||||
PmMaintenanceWorkOrder,
|
||||
WorkOrderTypeEnum,
|
||||
WorkOrderStatusEnum,
|
||||
PmWorkOrderMaterial,
|
||||
PmWorkOrderActivity,
|
||||
} from "../../../types/pm";
|
||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
||||
import { mockMaterials } from "../../../mocks/mockMaterials";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
} from '../../../types/pm'
|
||||
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||
import { mockMaterials } from '../../../mocks/mockMaterials'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
|
||||
interface NewWorkOrderModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (
|
||||
workOrder: Omit<
|
||||
PmMaintenanceWorkOrder,
|
||||
"id" | "creationTime" | "lastModificationTime"
|
||||
>
|
||||
) => void;
|
||||
workOrder: Omit<PmMaintenanceWorkOrder, 'id' | 'creationTime' | 'lastModificationTime'>,
|
||||
) => void
|
||||
}
|
||||
|
||||
const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSave,
|
||||
}) => {
|
||||
const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({ isOpen, onClose, onSave }) => {
|
||||
const [formData, setFormData] = useState({
|
||||
workOrderNumber: `WO-${Date.now()}`,
|
||||
workCenterId: "",
|
||||
workCenterId: '',
|
||||
orderType: WorkOrderTypeEnum.Corrective,
|
||||
priority: PriorityEnum.Normal,
|
||||
status: WorkOrderStatusEnum.Created,
|
||||
description: "",
|
||||
reportedBy: "",
|
||||
assignedTo: "",
|
||||
maintenanceTeamId: "",
|
||||
scheduledStart: "",
|
||||
scheduledEnd: "",
|
||||
description: '',
|
||||
reportedBy: '',
|
||||
assignedTo: '',
|
||||
maintenanceTeamId: '',
|
||||
scheduledStart: '',
|
||||
scheduledEnd: '',
|
||||
estimatedCost: 0,
|
||||
notes: "",
|
||||
});
|
||||
notes: '',
|
||||
})
|
||||
|
||||
const [materials, setMaterials] = useState<
|
||||
Omit<PmWorkOrderMaterial, "id" | "workOrderId">[]
|
||||
>([]);
|
||||
const [activities, setActivities] = useState<
|
||||
Omit<PmWorkOrderActivity, "id" | "workOrderId">[]
|
||||
>([]);
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [materials, setMaterials] = useState<Omit<PmWorkOrderMaterial, 'id' | 'workOrderId'>[]>([])
|
||||
const [activities, setActivities] = useState<Omit<PmWorkOrderActivity, 'id' | 'workOrderId'>[]>(
|
||||
[],
|
||||
)
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (!formData.description.trim()) {
|
||||
newErrors.description = "Açıklama alanı zorunludur";
|
||||
newErrors.description = 'Açıklama alanı zorunludur'
|
||||
}
|
||||
if (!formData.workCenterId) {
|
||||
newErrors.workCenterId = "İş Merkezi seçimi zorunludur";
|
||||
newErrors.workCenterId = 'İş Merkezi seçimi zorunludur'
|
||||
}
|
||||
if (!formData.reportedBy.trim()) {
|
||||
newErrors.reportedBy = "Bildiren kişi zorunludur";
|
||||
newErrors.reportedBy = 'Bildiren kişi zorunludur'
|
||||
}
|
||||
if (formData.estimatedCost < 0) {
|
||||
newErrors.estimatedCost = "Tahmini maliyet negatif olamaz";
|
||||
newErrors.estimatedCost = 'Tahmini maliyet negatif olamaz'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!validateForm()) return;
|
||||
e.preventDefault()
|
||||
if (!validateForm()) return
|
||||
|
||||
const newWorkOrder: Omit<
|
||||
PmMaintenanceWorkOrder,
|
||||
"id" | "creationTime" | "lastModificationTime"
|
||||
'id' | 'creationTime' | 'lastModificationTime'
|
||||
> = {
|
||||
workOrderNumber: formData.workOrderNumber,
|
||||
workCenterId: formData.workCenterId,
|
||||
|
|
@ -90,12 +81,8 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
reportedBy: formData.reportedBy,
|
||||
assignedTo: formData.assignedTo || undefined,
|
||||
maintenanceTeamId: formData.maintenanceTeamId || undefined,
|
||||
scheduledStart: formData.scheduledStart
|
||||
? new Date(formData.scheduledStart)
|
||||
: undefined,
|
||||
scheduledEnd: formData.scheduledEnd
|
||||
? new Date(formData.scheduledEnd)
|
||||
: undefined,
|
||||
scheduledStart: formData.scheduledStart ? new Date(formData.scheduledStart) : undefined,
|
||||
scheduledEnd: formData.scheduledEnd ? new Date(formData.scheduledEnd) : undefined,
|
||||
actualStart: undefined,
|
||||
actualEnd: undefined,
|
||||
estimatedCost: formData.estimatedCost,
|
||||
|
|
@ -103,51 +90,47 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
materials: materials.map((material, index) => ({
|
||||
...material,
|
||||
id: `mat-${index}`,
|
||||
workOrderId: "",
|
||||
workOrderId: '',
|
||||
totalCost: material.plannedQuantity * material.unitCost,
|
||||
})),
|
||||
activities: activities.map((activity, index) => ({
|
||||
...activity,
|
||||
id: `act-${index}`,
|
||||
workOrderId: "",
|
||||
workOrderId: '',
|
||||
actualDuration: 0,
|
||||
completedAt: undefined,
|
||||
})),
|
||||
notes: formData.notes || undefined,
|
||||
completionNotes: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
onSave(newWorkOrder);
|
||||
onClose();
|
||||
};
|
||||
onSave(newWorkOrder)
|
||||
onClose()
|
||||
}
|
||||
|
||||
const addMaterial = () => {
|
||||
setMaterials([
|
||||
...materials,
|
||||
{
|
||||
materialId: "",
|
||||
materialCode: "",
|
||||
materialName: "",
|
||||
materialId: '',
|
||||
materialCode: '',
|
||||
materialName: '',
|
||||
plannedQuantity: 1,
|
||||
actualQuantity: 0,
|
||||
unitCost: 0,
|
||||
totalCost: 0,
|
||||
},
|
||||
]);
|
||||
};
|
||||
])
|
||||
}
|
||||
|
||||
const removeMaterial = (index: number) => {
|
||||
setMaterials(materials.filter((_, i) => i !== index));
|
||||
};
|
||||
setMaterials(materials.filter((_, i) => i !== index))
|
||||
}
|
||||
|
||||
const updateMaterial = (
|
||||
index: number,
|
||||
field: string,
|
||||
value: string | number
|
||||
) => {
|
||||
const updated = [...materials];
|
||||
if (field === "materialId") {
|
||||
const selectedMaterial = mockMaterials.find((m) => m.id === value);
|
||||
const updateMaterial = (index: number, field: string, value: string | number) => {
|
||||
const updated = [...materials]
|
||||
if (field === 'materialId') {
|
||||
const selectedMaterial = mockMaterials.find((m) => m.id === value)
|
||||
if (selectedMaterial) {
|
||||
updated[index] = {
|
||||
...updated[index],
|
||||
|
|
@ -155,66 +138,59 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
materialCode: selectedMaterial.code,
|
||||
materialName: selectedMaterial.name,
|
||||
unitCost: selectedMaterial.costPrice,
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const material = updated[index];
|
||||
if (field === "plannedQuantity") {
|
||||
material.plannedQuantity = value as number;
|
||||
} else if (field === "unitCost") {
|
||||
material.unitCost = value as number;
|
||||
const material = updated[index]
|
||||
if (field === 'plannedQuantity') {
|
||||
material.plannedQuantity = value as number
|
||||
} else if (field === 'unitCost') {
|
||||
material.unitCost = value as number
|
||||
}
|
||||
}
|
||||
setMaterials(updated);
|
||||
};
|
||||
setMaterials(updated)
|
||||
}
|
||||
|
||||
const addActivity = () => {
|
||||
setActivities([
|
||||
...activities,
|
||||
{
|
||||
activityDescription: "",
|
||||
activityDescription: '',
|
||||
plannedDuration: 60,
|
||||
actualDuration: 0,
|
||||
performedBy: "",
|
||||
notes: "",
|
||||
performedBy: '',
|
||||
notes: '',
|
||||
},
|
||||
]);
|
||||
};
|
||||
])
|
||||
}
|
||||
|
||||
const removeActivity = (index: number) => {
|
||||
setActivities(activities.filter((_, i) => i !== index));
|
||||
};
|
||||
setActivities(activities.filter((_, i) => i !== index))
|
||||
}
|
||||
|
||||
const updateActivity = (
|
||||
index: number,
|
||||
field: string,
|
||||
value: string | number
|
||||
) => {
|
||||
const updated = [...activities];
|
||||
const activity = updated[index];
|
||||
if (field === "activityDescription") {
|
||||
activity.activityDescription = value as string;
|
||||
} else if (field === "plannedDuration") {
|
||||
activity.plannedDuration = value as number;
|
||||
} else if (field === "performedBy") {
|
||||
activity.performedBy = value as string;
|
||||
} else if (field === "notes") {
|
||||
activity.notes = value as string;
|
||||
const updateActivity = (index: number, field: string, value: string | number) => {
|
||||
const updated = [...activities]
|
||||
const activity = updated[index]
|
||||
if (field === 'activityDescription') {
|
||||
activity.activityDescription = value as string
|
||||
} else if (field === 'plannedDuration') {
|
||||
activity.plannedDuration = value as number
|
||||
} else if (field === 'performedBy') {
|
||||
activity.performedBy = value as string
|
||||
} else if (field === 'notes') {
|
||||
activity.notes = value as string
|
||||
}
|
||||
setActivities(updated);
|
||||
};
|
||||
setActivities(updated)
|
||||
}
|
||||
|
||||
if (!isOpen) return null;
|
||||
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-5xl 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">Yeni İş Emri</h3>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -223,31 +199,23 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
{/* Basic Information */}
|
||||
<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">
|
||||
İş Emri No
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">İş Emri No</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.workOrderNumber}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, workOrderNumber: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, workOrderNumber: e.target.value })}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
İş Merkezi *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||
<select
|
||||
value={formData.workCenterId}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, workCenterId: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, workCenterId: 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.workCenterId ? "border-red-500" : "border-gray-300"
|
||||
errors.workCenterId ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">İş Merkezi Seçin</option>
|
||||
|
|
@ -258,16 +226,12 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
))}
|
||||
</select>
|
||||
{errors.workCenterId && (
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
{errors.workCenterId}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-red-600">{errors.workCenterId}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
İş Emri Tipi
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">İş Emri Tipi</label>
|
||||
<select
|
||||
value={formData.orderType}
|
||||
onChange={(e) =>
|
||||
|
|
@ -282,16 +246,12 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
<option value={WorkOrderTypeEnum.Corrective}>Düzeltici</option>
|
||||
<option value={WorkOrderTypeEnum.Emergency}>Acil</option>
|
||||
<option value={WorkOrderTypeEnum.Inspection}>İnceleme</option>
|
||||
<option value={WorkOrderTypeEnum.Calibration}>
|
||||
Kalibrasyon
|
||||
</option>
|
||||
<option value={WorkOrderTypeEnum.Calibration}>Kalibrasyon</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Öncelik
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Öncelik</label>
|
||||
<select
|
||||
value={formData.priority}
|
||||
onChange={(e) =>
|
||||
|
|
@ -311,17 +271,13 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Açıklama *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama *</label>
|
||||
<textarea
|
||||
value={formData.description}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, description: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||
rows={2}
|
||||
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.description ? "border-red-500" : "border-gray-300"
|
||||
errors.description ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="İş emri açıklaması..."
|
||||
/>
|
||||
|
|
@ -333,17 +289,13 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
{/* Assignment */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Bildiren *
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Bildiren *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.reportedBy}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, reportedBy: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, reportedBy: 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.reportedBy ? "border-red-500" : "border-gray-300"
|
||||
errors.reportedBy ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Bildiren kişi adı"
|
||||
/>
|
||||
|
|
@ -353,14 +305,10 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Atanan Kişi
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Atanan Kişi</label>
|
||||
<select
|
||||
value={formData.assignedTo}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, assignedTo: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, assignedTo: e.target.value })}
|
||||
className="w-full px-2.5 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>
|
||||
|
|
@ -373,9 +321,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Bakım Ekibi
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Bakım Ekibi</label>
|
||||
<select
|
||||
value={formData.maintenanceTeamId}
|
||||
onChange={(e) =>
|
||||
|
|
@ -406,9 +352,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
<input
|
||||
type="datetime-local"
|
||||
value={formData.scheduledStart}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, scheduledStart: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, scheduledStart: e.target.value })}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -421,9 +365,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
<input
|
||||
type="datetime-local"
|
||||
value={formData.scheduledEnd}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, scheduledEnd: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, scheduledEnd: e.target.value })}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -444,14 +386,12 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
})
|
||||
}
|
||||
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.estimatedCost ? "border-red-500" : "border-gray-300"
|
||||
errors.estimatedCost ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
{errors.estimatedCost && (
|
||||
<p className="mt-1 text-xs text-red-600">
|
||||
{errors.estimatedCost}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-red-600">{errors.estimatedCost}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -459,9 +399,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
{/* Materials */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h4 className="text-base font-medium text-gray-900">
|
||||
Malzemeler
|
||||
</h4>
|
||||
<h4 className="text-base font-medium text-gray-900">Malzemeler</h4>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addMaterial}
|
||||
|
|
@ -476,14 +414,10 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
<div key={index} className="bg-gray-50 p-3 rounded-lg mb-2">
|
||||
<div className="grid grid-cols-1 md:grid-cols-5 gap-3">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Malzeme
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Malzeme</label>
|
||||
<select
|
||||
value={material.materialId}
|
||||
onChange={(e) =>
|
||||
updateMaterial(index, "materialId", e.target.value)
|
||||
}
|
||||
onChange={(e) => updateMaterial(index, 'materialId', e.target.value)}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="">Malzeme Seçin</option>
|
||||
|
|
@ -503,11 +437,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
min="0"
|
||||
value={material.plannedQuantity}
|
||||
onChange={(e) =>
|
||||
updateMaterial(
|
||||
index,
|
||||
"plannedQuantity",
|
||||
parseInt(e.target.value) || 0
|
||||
)
|
||||
updateMaterial(index, 'plannedQuantity', parseInt(e.target.value) || 0)
|
||||
}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
|
|
@ -522,24 +452,16 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
step="0.01"
|
||||
value={material.unitCost}
|
||||
onChange={(e) =>
|
||||
updateMaterial(
|
||||
index,
|
||||
"unitCost",
|
||||
parseFloat(e.target.value) || 0
|
||||
)
|
||||
updateMaterial(index, 'unitCost', parseFloat(e.target.value) || 0)
|
||||
}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Toplam
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Toplam</label>
|
||||
<input
|
||||
type="text"
|
||||
value={`₺${(
|
||||
material.plannedQuantity * material.unitCost
|
||||
).toFixed(2)}`}
|
||||
value={`₺${(material.plannedQuantity * material.unitCost).toFixed(2)}`}
|
||||
readOnly
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg bg-gray-100"
|
||||
/>
|
||||
|
|
@ -561,9 +483,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
{/* Activities */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h4 className="text-base font-medium text-gray-900">
|
||||
Aktiviteler
|
||||
</h4>
|
||||
<h4 className="text-base font-medium text-gray-900">Aktiviteler</h4>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addActivity}
|
||||
|
|
@ -584,13 +504,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
<input
|
||||
type="text"
|
||||
value={activity.activityDescription}
|
||||
onChange={(e) =>
|
||||
updateActivity(
|
||||
index,
|
||||
"activityDescription",
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
onChange={(e) => updateActivity(index, 'activityDescription', e.target.value)}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="Aktivite açıklaması"
|
||||
/>
|
||||
|
|
@ -605,11 +519,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
min="0"
|
||||
value={activity.plannedDuration}
|
||||
onChange={(e) =>
|
||||
updateActivity(
|
||||
index,
|
||||
"plannedDuration",
|
||||
parseInt(e.target.value) || 0
|
||||
)
|
||||
updateActivity(index, 'plannedDuration', parseInt(e.target.value) || 0)
|
||||
}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
|
|
@ -630,14 +540,10 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
|
||||
{/* Notes */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Notlar
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Notlar</label>
|
||||
<textarea
|
||||
value={formData.notes}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, notes: e.target.value })
|
||||
}
|
||||
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
|
||||
rows={2}
|
||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="Ek notlar..."
|
||||
|
|
@ -663,7 +569,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default NewWorkOrderModal;
|
||||
export default NewWorkOrderModal
|
||||
|
|
|
|||
|
|
@ -1,17 +1,12 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
FaTimes,
|
||||
FaSave,
|
||||
FaExclamationTriangle,
|
||||
FaInfoCircle,
|
||||
} from "react-icons/fa";
|
||||
import { PmMaintenancePlan } from "../../../types/pm";
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { FaTimes, FaSave, FaExclamationTriangle, FaInfoCircle } from 'react-icons/fa'
|
||||
import { PmMaintenancePlan } from '../../../types/pm'
|
||||
|
||||
interface PlanStatusChangeModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (planIds: string[], isActive: boolean, reason: string) => void;
|
||||
selectedPlans: PmMaintenancePlan[];
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (planIds: string[], isActive: boolean, reason: string) => void
|
||||
selectedPlans: PmMaintenancePlan[]
|
||||
}
|
||||
|
||||
const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||
|
|
@ -20,47 +15,43 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
onSave,
|
||||
selectedPlans,
|
||||
}) => {
|
||||
const [targetStatus, setTargetStatus] = useState<boolean>(true);
|
||||
const [reason, setReason] = useState("");
|
||||
const [targetStatus, setTargetStatus] = useState<boolean>(true)
|
||||
const [reason, setReason] = useState('')
|
||||
|
||||
const activePlans = selectedPlans.filter((plan) => plan.isActive);
|
||||
const inactivePlans = selectedPlans.filter((plan) => !plan.isActive);
|
||||
const activePlans = selectedPlans.filter((plan) => plan.isActive)
|
||||
const inactivePlans = selectedPlans.filter((plan) => !plan.isActive)
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setTargetStatus(true);
|
||||
setReason("");
|
||||
setTargetStatus(true)
|
||||
setReason('')
|
||||
}
|
||||
}, [isOpen]);
|
||||
}, [isOpen])
|
||||
|
||||
const handleSave = () => {
|
||||
if (!reason.trim()) {
|
||||
alert("Lütfen durum değişikliği için bir açıklama girin.");
|
||||
return;
|
||||
alert('Lütfen durum değişikliği için bir açıklama girin.')
|
||||
return
|
||||
}
|
||||
const planIds = selectedPlans.map((plan) => plan.id);
|
||||
onSave(planIds, targetStatus, reason.trim());
|
||||
onClose();
|
||||
};
|
||||
const planIds = selectedPlans.map((plan) => plan.id)
|
||||
onSave(planIds, targetStatus, reason.trim())
|
||||
onClose()
|
||||
}
|
||||
|
||||
const getStatusText = (isActive: boolean) => {
|
||||
return isActive ? "Aktif" : "Pasif";
|
||||
};
|
||||
return isActive ? 'Aktif' : 'Pasif'
|
||||
}
|
||||
|
||||
const getStatusColor = (isActive: boolean) => {
|
||||
return isActive ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800";
|
||||
};
|
||||
return isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
||||
}
|
||||
|
||||
const getTargetStatusColor = (isActive: boolean) => {
|
||||
return isActive ? "text-green-600" : "text-red-600";
|
||||
};
|
||||
return isActive ? 'text-green-600' : 'text-red-600'
|
||||
}
|
||||
|
||||
const willChange = selectedPlans.filter(
|
||||
(plan) => plan.isActive !== targetStatus
|
||||
);
|
||||
const willNotChange = selectedPlans.filter(
|
||||
(plan) => plan.isActive === targetStatus
|
||||
);
|
||||
const willChange = selectedPlans.filter((plan) => plan.isActive !== targetStatus)
|
||||
const willNotChange = selectedPlans.filter((plan) => plan.isActive === targetStatus)
|
||||
|
||||
return (
|
||||
isOpen && (
|
||||
|
|
@ -69,12 +60,8 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
Plan Durumu Değiştir
|
||||
</h2>
|
||||
<p className="text-gray-600">
|
||||
{selectedPlans.length} plan için durum değişikliği
|
||||
</p>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Plan Durumu Değiştir</h2>
|
||||
<p className="text-gray-600">{selectedPlans.length} plan için durum değişikliği</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
|
|
@ -88,40 +75,28 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
<div className="p-3 space-y-3">
|
||||
{/* Current Status Summary */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||||
Mevcut Durum
|
||||
</h3>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">Mevcut Durum</h3>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="bg-green-50 border border-green-200 rounded-lg p-3">
|
||||
<div className="flex items-center space-x-1.5">
|
||||
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
|
||||
<span className="text-sm font-medium text-green-800">
|
||||
Aktif Planlar
|
||||
</span>
|
||||
<span className="text-sm font-medium text-green-800">Aktif Planlar</span>
|
||||
</div>
|
||||
<p className="text-base font-bold text-green-600 mt-1">
|
||||
{activePlans.length}
|
||||
</p>
|
||||
<p className="text-base font-bold text-green-600 mt-1">{activePlans.length}</p>
|
||||
</div>
|
||||
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
|
||||
<div className="flex items-center space-x-1.5">
|
||||
<div className="w-3 h-3 bg-red-500 rounded-full"></div>
|
||||
<span className="text-sm font-medium text-red-800">
|
||||
Pasif Planlar
|
||||
</span>
|
||||
<span className="text-sm font-medium text-red-800">Pasif Planlar</span>
|
||||
</div>
|
||||
<p className="text-base font-bold text-red-600 mt-1">
|
||||
{inactivePlans.length}
|
||||
</p>
|
||||
<p className="text-base font-bold text-red-600 mt-1">{inactivePlans.length}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Target Status Selection */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||||
Hedef Durum
|
||||
</h3>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">Hedef Durum</h3>
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center space-x-3 p-2 border border-gray-200 rounded-lg hover:bg-gray-50">
|
||||
<input
|
||||
|
|
@ -133,9 +108,7 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
/>
|
||||
<div className="flex items-center space-x-1.5">
|
||||
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
Aktif
|
||||
</span>
|
||||
<span className="text-sm font-medium text-gray-900">Aktif</span>
|
||||
<span className="text-xs text-gray-500">
|
||||
(Planlar bakım takvimine dahil edilir)
|
||||
</span>
|
||||
|
|
@ -151,9 +124,7 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
/>
|
||||
<div className="flex items-center space-x-1.5">
|
||||
<div className="w-3 h-3 bg-red-500 rounded-full"></div>
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
Pasif
|
||||
</span>
|
||||
<span className="text-sm font-medium text-gray-900">Pasif</span>
|
||||
<span className="text-xs text-gray-500">
|
||||
(Planlar bakım takvimine dahil edilmez)
|
||||
</span>
|
||||
|
|
@ -164,9 +135,7 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
|
||||
{/* Change Impact */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||||
Değişiklik Etkisi
|
||||
</h3>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">Değişiklik Etkisi</h3>
|
||||
|
||||
{willChange.length > 0 && (
|
||||
<div className="mb-2">
|
||||
|
|
@ -179,23 +148,16 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-2 max-h-28 overflow-y-auto">
|
||||
<div className="space-y-2">
|
||||
{willChange.map((plan) => (
|
||||
<div
|
||||
key={plan.id}
|
||||
className="flex items-center justify-between text-sm"
|
||||
>
|
||||
<div key={plan.id} className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="font-mono text-gray-600">
|
||||
{plan.planCode}
|
||||
</span>
|
||||
<span className="font-mono text-gray-600">{plan.planCode}</span>
|
||||
<span className="text-gray-500">-</span>
|
||||
<span className="text-gray-700">
|
||||
{plan.description}
|
||||
</span>
|
||||
<span className="text-gray-700">{plan.description}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs ${getStatusColor(
|
||||
plan.isActive
|
||||
plan.isActive,
|
||||
)}`}
|
||||
>
|
||||
{getStatusText(plan.isActive)}
|
||||
|
|
@ -203,7 +165,7 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
<span className="text-gray-400">→</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs ${getStatusColor(
|
||||
targetStatus
|
||||
targetStatus,
|
||||
)}`}
|
||||
>
|
||||
{getStatusText(targetStatus)}
|
||||
|
|
@ -227,22 +189,15 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-2 max-h-28 overflow-y-auto">
|
||||
<div className="space-y-2">
|
||||
{willNotChange.map((plan) => (
|
||||
<div
|
||||
key={plan.id}
|
||||
className="flex items-center justify-between text-sm"
|
||||
>
|
||||
<div key={plan.id} className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="font-mono text-gray-600">
|
||||
{plan.planCode}
|
||||
</span>
|
||||
<span className="font-mono text-gray-600">{plan.planCode}</span>
|
||||
<span className="text-gray-500">-</span>
|
||||
<span className="text-gray-700">
|
||||
{plan.description}
|
||||
</span>
|
||||
<span className="text-gray-700">{plan.description}</span>
|
||||
</div>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs ${getStatusColor(
|
||||
plan.isActive
|
||||
plan.isActive,
|
||||
)}`}
|
||||
>
|
||||
{getStatusText(plan.isActive)}
|
||||
|
|
@ -281,10 +236,9 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
<div>
|
||||
<h4 className="text-sm font-medium text-red-800">Uyarı</h4>
|
||||
<p className="text-sm text-red-700 mt-1">
|
||||
Planları pasif hale getirmek, gelecek bakım işlemlerinin
|
||||
otomatik olarak planlanmasını durdurur. Bu planlar manuel
|
||||
olarak aktif hale getirilene kadar bakım takviminde
|
||||
görünmeyecektir.
|
||||
Planları pasif hale getirmek, gelecek bakım işlemlerinin otomatik olarak
|
||||
planlanmasını durdurur. Bu planlar manuel olarak aktif hale getirilene kadar
|
||||
bakım takviminde görünmeyecektir.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -296,13 +250,10 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
<div className="flex items-start space-x-2">
|
||||
<FaInfoCircle className="w-5 h-5 text-green-600 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-green-800">
|
||||
Bilgi
|
||||
</h4>
|
||||
<h4 className="text-sm font-medium text-green-800">Bilgi</h4>
|
||||
<p className="text-sm text-green-700 mt-1">
|
||||
Planları aktif hale getirmek, bakım takviminde
|
||||
görünmelerini ve otomatik iş emri oluşturulmalarını
|
||||
sağlar. Sonraki bakım tarihleri plan ayarlarına göre
|
||||
Planları aktif hale getirmek, bakım takviminde görünmelerini ve otomatik iş
|
||||
emri oluşturulmalarını sağlar. Sonraki bakım tarihleri plan ayarlarına göre
|
||||
hesaplanacaktır.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -326,11 +277,11 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
disabled={!reason.trim()}
|
||||
className={`flex items-center space-x-2 px-3 py-1.5 text-sm rounded-lg transition-colors ${
|
||||
!reason.trim()
|
||||
? "bg-gray-300 text-gray-500 cursor-not-allowed"
|
||||
? 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
||||
: `${getTargetStatusColor(targetStatus)} ${
|
||||
targetStatus
|
||||
? "bg-green-600 hover:bg-green-700 text-white"
|
||||
: "bg-red-600 hover:bg-red-700 text-white"
|
||||
? 'bg-green-600 hover:bg-green-700 text-white'
|
||||
: 'bg-red-600 hover:bg-red-700 text-white'
|
||||
}`
|
||||
}`}
|
||||
>
|
||||
|
|
@ -343,7 +294,7 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default PlanStatusChangeModal;
|
||||
export default PlanStatusChangeModal
|
||||
|
|
|
|||
|
|
@ -1,24 +1,21 @@
|
|||
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";
|
||||
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[];
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onStart: (workOrders: PmMaintenanceWorkOrder[], startData: StartWorkOrderData) => void
|
||||
workOrders: PmMaintenanceWorkOrder[]
|
||||
}
|
||||
|
||||
interface StartWorkOrderData {
|
||||
actualStart: Date;
|
||||
assignedTo?: string;
|
||||
maintenanceTeamId?: string;
|
||||
notes?: string;
|
||||
actualStart: Date
|
||||
assignedTo?: string
|
||||
maintenanceTeamId?: string
|
||||
notes?: string
|
||||
}
|
||||
|
||||
const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||
|
|
@ -29,57 +26,57 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
}) => {
|
||||
const [formData, setFormData] = useState({
|
||||
actualStart: new Date().toISOString().slice(0, 16),
|
||||
assignedTo: "",
|
||||
maintenanceTeamId: "",
|
||||
notes: "",
|
||||
});
|
||||
assignedTo: '',
|
||||
maintenanceTeamId: '',
|
||||
notes: '',
|
||||
})
|
||||
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (!formData.actualStart) {
|
||||
newErrors.actualStart = "Başlangıç tarihi zorunludur";
|
||||
newErrors.actualStart = 'Başlangıç tarihi zorunludur'
|
||||
}
|
||||
|
||||
const startDate = new Date(formData.actualStart);
|
||||
const now = new Date();
|
||||
const startDate = new Date(formData.actualStart)
|
||||
const now = new Date()
|
||||
if (startDate > now) {
|
||||
newErrors.actualStart = "Başlangıç tarihi gelecekte olamaz";
|
||||
newErrors.actualStart = 'Başlangıç tarihi gelecekte olamaz'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!validateForm()) return;
|
||||
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();
|
||||
};
|
||||
onStart(workOrders, startData)
|
||||
onClose()
|
||||
}
|
||||
|
||||
const canStartWorkOrders = workOrders.every(
|
||||
(wo) =>
|
||||
wo.status === WorkOrderStatusEnum.Created ||
|
||||
wo.status === WorkOrderStatusEnum.Planned ||
|
||||
wo.status === WorkOrderStatusEnum.Released
|
||||
);
|
||||
wo.status === WorkOrderStatusEnum.Released,
|
||||
)
|
||||
|
||||
const inProgressWorkOrders = workOrders.filter(
|
||||
(wo) => wo.status === WorkOrderStatusEnum.InProgress
|
||||
);
|
||||
(wo) => wo.status === WorkOrderStatusEnum.InProgress,
|
||||
)
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -89,10 +86,7 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
<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"
|
||||
>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -104,47 +98,33 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
</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 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>
|
||||
<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"
|
||||
? '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"
|
||||
? '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"}
|
||||
{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>
|
||||
|
|
@ -161,9 +141,8 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
<div>
|
||||
<h5 className="font-medium text-yellow-800 mb-1">Uyarı</h5>
|
||||
<p className="text-yellow-700 text-sm">
|
||||
Bazı iş emirleri başlatılamaz durumda. Sadece "Oluşturuldu",
|
||||
"Planlandı" veya "Serbest Bırakıldı" durumundaki iş emirleri
|
||||
başlatılabilir.
|
||||
Bazı iş emirleri başlatılamaz durumda. Sadece "Oluşturuldu", "Planlandı" veya
|
||||
"Serbest Bırakıldı" durumundaki iş emirleri başlatılabilir.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -177,9 +156,8 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
<div>
|
||||
<h5 className="font-medium text-blue-800 mb-1">Bilgi</h5>
|
||||
<p className="text-blue-700 text-sm">
|
||||
{inProgressWorkOrders.length} iş emri zaten devam ediyor
|
||||
durumunda. Bu iş emirleri için sadece atama ve not
|
||||
güncellemesi yapılacak.
|
||||
{inProgressWorkOrders.length} iş emri zaten devam ediyor durumunda. Bu iş emirleri
|
||||
için sadece atama ve not güncellemesi yapılacak.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -196,11 +174,9 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
<input
|
||||
type="datetime-local"
|
||||
value={formData.actualStart}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, actualStart: e.target.value })
|
||||
}
|
||||
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 ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
/>
|
||||
{errors.actualStart && (
|
||||
|
|
@ -217,9 +193,7 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
</label>
|
||||
<select
|
||||
value={formData.assignedTo}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, assignedTo: e.target.value })
|
||||
}
|
||||
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>
|
||||
|
|
@ -232,9 +206,7 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Bakım Ekibi
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Bakım Ekibi</label>
|
||||
<select
|
||||
value={formData.maintenanceTeamId}
|
||||
onChange={(e) =>
|
||||
|
|
@ -262,9 +234,7 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
</label>
|
||||
<textarea
|
||||
value={formData.notes}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, notes: e.target.value })
|
||||
}
|
||||
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..."
|
||||
|
|
@ -287,7 +257,7 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
(wo) =>
|
||||
wo.status === WorkOrderStatusEnum.Created ||
|
||||
wo.status === WorkOrderStatusEnum.Planned ||
|
||||
wo.status === WorkOrderStatusEnum.Released
|
||||
wo.status === WorkOrderStatusEnum.Released,
|
||||
).length
|
||||
}
|
||||
</span>
|
||||
|
|
@ -301,10 +271,10 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
<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",
|
||||
{new Date(formData.actualStart).toLocaleDateString('tr-TR')}{' '}
|
||||
{new Date(formData.actualStart).toLocaleTimeString('tr-TR', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -331,7 +301,7 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default StartWorkOrderModal;
|
||||
export default StartWorkOrderModal
|
||||
|
|
|
|||
|
|
@ -1,20 +1,13 @@
|
|||
import React, { useState } from "react";
|
||||
import { FaTimes, FaSave, FaExclamationTriangle } from "react-icons/fa";
|
||||
import {
|
||||
PmWorkCenter,
|
||||
WorkCenterStatusEnum,
|
||||
CriticalityLevelEnum,
|
||||
} from "../../../types/pm";
|
||||
import {
|
||||
getCriticalityLevelText,
|
||||
getWorkCenterStatusText,
|
||||
} from "../../../utils/erp";
|
||||
import React, { useState } from 'react'
|
||||
import { FaTimes, FaSave, FaExclamationTriangle } from 'react-icons/fa'
|
||||
import { PmWorkCenter, WorkCenterStatusEnum, CriticalityLevelEnum } from '../../../types/pm'
|
||||
import { getCriticalityLevelText, getWorkCenterStatusText } from '../../../utils/erp'
|
||||
|
||||
interface StatusUpdateModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (updatedWorkCenters: PmWorkCenter[]) => void;
|
||||
selectedWorkCenters: PmWorkCenter[];
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (updatedWorkCenters: PmWorkCenter[]) => void
|
||||
selectedWorkCenters: PmWorkCenter[]
|
||||
}
|
||||
|
||||
const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
||||
|
|
@ -23,17 +16,15 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
|||
onSave,
|
||||
selectedWorkCenters,
|
||||
}) => {
|
||||
const [newStatus, setNewStatus] = useState<WorkCenterStatusEnum>(
|
||||
WorkCenterStatusEnum.Operational
|
||||
);
|
||||
const [newStatus, setNewStatus] = useState<WorkCenterStatusEnum>(WorkCenterStatusEnum.Operational)
|
||||
const [newCriticality, setNewCriticality] = useState<CriticalityLevelEnum>(
|
||||
CriticalityLevelEnum.Medium
|
||||
);
|
||||
const [updateStatus, setUpdateStatus] = useState(true);
|
||||
const [updateCriticality, setUpdateCriticality] = useState(false);
|
||||
const [reason, setReason] = useState("");
|
||||
CriticalityLevelEnum.Medium,
|
||||
)
|
||||
const [updateStatus, setUpdateStatus] = useState(true)
|
||||
const [updateCriticality, setUpdateCriticality] = useState(false)
|
||||
const [reason, setReason] = useState('')
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
const handleSave = () => {
|
||||
const updatedWorkCenters = selectedWorkCenters.map((workCenter) => ({
|
||||
|
|
@ -41,15 +32,15 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
|||
status: updateStatus ? newStatus : workCenter.status,
|
||||
criticality: updateCriticality ? newCriticality : workCenter.criticality,
|
||||
lastModificationTime: new Date(),
|
||||
}));
|
||||
}))
|
||||
|
||||
onSave(updatedWorkCenters);
|
||||
onClose();
|
||||
};
|
||||
onSave(updatedWorkCenters)
|
||||
onClose()
|
||||
}
|
||||
|
||||
const isStatusCritical =
|
||||
newStatus === WorkCenterStatusEnum.OutOfOrder ||
|
||||
newStatus === WorkCenterStatusEnum.UnderMaintenance;
|
||||
newStatus === WorkCenterStatusEnum.UnderMaintenance
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -59,10 +50,7 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
|||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
Durum Güncelle ({selectedWorkCenters.length} iş merkezi)
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
>
|
||||
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -71,15 +59,10 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
|||
<div className="p-4 space-y-4">
|
||||
{/* Selected WorkCenter List */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Seçili İş Merkezleri
|
||||
</h3>
|
||||
<h3 className="text-base 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"
|
||||
>
|
||||
<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>
|
||||
|
|
@ -106,35 +89,22 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
|||
onChange={(e) => setUpdateStatus(e.target.checked)}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
<label
|
||||
htmlFor="updateStatus"
|
||||
className="text-sm font-medium text-gray-700"
|
||||
>
|
||||
<label htmlFor="updateStatus" className="text-sm font-medium text-gray-700">
|
||||
Operasyonel durumu güncelle
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{updateStatus && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Yeni Durum
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Yeni Durum</label>
|
||||
<select
|
||||
value={newStatus}
|
||||
onChange={(e) =>
|
||||
setNewStatus(e.target.value as WorkCenterStatusEnum)
|
||||
}
|
||||
onChange={(e) => setNewStatus(e.target.value as WorkCenterStatusEnum)}
|
||||
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={WorkCenterStatusEnum.Operational}>
|
||||
Operasyonel
|
||||
</option>
|
||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>
|
||||
Bakımda
|
||||
</option>
|
||||
<option value={WorkCenterStatusEnum.OutOfOrder}>
|
||||
Arızalı
|
||||
</option>
|
||||
<option value={WorkCenterStatusEnum.Operational}>Operasyonel</option>
|
||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>Bakımda</option>
|
||||
<option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option>
|
||||
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
||||
</select>
|
||||
|
||||
|
|
@ -142,13 +112,10 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
|||
<div className="mt-2 p-2 bg-yellow-50 border border-yellow-200 rounded-lg">
|
||||
<div className="flex items-center space-x-2">
|
||||
<FaExclamationTriangle className="w-4 h-4 text-yellow-600" />
|
||||
<span className="text-sm text-yellow-800 font-medium">
|
||||
Dikkat!
|
||||
</span>
|
||||
<span className="text-sm text-yellow-800 font-medium">Dikkat!</span>
|
||||
</div>
|
||||
<p className="text-sm text-yellow-700 mt-1">
|
||||
Bu durum değişikliği iş merkezinin üretim kapasitesini
|
||||
etkileyebilir.
|
||||
Bu durum değişikliği iş merkezinin üretim kapasitesini etkileyebilir.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -163,10 +130,7 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
|||
onChange={(e) => setUpdateCriticality(e.target.checked)}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
<label
|
||||
htmlFor="updateCriticality"
|
||||
className="text-sm font-medium text-gray-700"
|
||||
>
|
||||
<label htmlFor="updateCriticality" className="text-sm font-medium text-gray-700">
|
||||
Kritiklik seviyesini güncelle
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -178,9 +142,7 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
|||
</label>
|
||||
<select
|
||||
value={newCriticality}
|
||||
onChange={(e) =>
|
||||
setNewCriticality(e.target.value as CriticalityLevelEnum)
|
||||
}
|
||||
onChange={(e) => setNewCriticality(e.target.value as CriticalityLevelEnum)}
|
||||
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={CriticalityLevelEnum.Low}>Düşük</option>
|
||||
|
|
@ -212,15 +174,11 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
|||
<div className="text-sm text-blue-800 space-y-1">
|
||||
<p>• {selectedWorkCenters.length} iş merkezi güncellenecek</p>
|
||||
{updateStatus && (
|
||||
<p>
|
||||
• Durum "{getWorkCenterStatusText(newStatus)}" olarak
|
||||
değiştirilecek
|
||||
</p>
|
||||
<p>• Durum "{getWorkCenterStatusText(newStatus)}" olarak değiştirilecek</p>
|
||||
)}
|
||||
{updateCriticality && (
|
||||
<p>
|
||||
• Kritiklik seviyesi "
|
||||
{getCriticalityLevelText(newCriticality)}" olarak
|
||||
• Kritiklik seviyesi "{getCriticalityLevelText(newCriticality)}" olarak
|
||||
değiştirilecek
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -249,7 +207,7 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default StatusUpdateModal;
|
||||
export default StatusUpdateModal
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import {
|
||||
FaTimes,
|
||||
FaSave,
|
||||
FaExclamationTriangle,
|
||||
FaCheckCircle,
|
||||
FaTimesCircle,
|
||||
} from "react-icons/fa";
|
||||
import { Team } from "../../../types/common";
|
||||
} from 'react-icons/fa'
|
||||
import { Team } from '../../../types/common'
|
||||
|
||||
interface TeamStatusChangeModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (teamIds: string[], newStatus: boolean, reason?: string) => void;
|
||||
selectedTeams: Team[];
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSave: (teamIds: string[], newStatus: boolean, reason?: string) => void
|
||||
selectedTeams: Team[]
|
||||
}
|
||||
|
||||
const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
||||
|
|
@ -21,60 +21,60 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
|||
onSave,
|
||||
selectedTeams,
|
||||
}) => {
|
||||
const [newStatus, setNewStatus] = useState<boolean>(true);
|
||||
const [reason, setReason] = useState("");
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [newStatus, setNewStatus] = useState<boolean>(true)
|
||||
const [reason, setReason] = useState('')
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
|
||||
const activeTeams = selectedTeams.filter((team) => team.isActive);
|
||||
const inactiveTeams = selectedTeams.filter((team) => !team.isActive);
|
||||
const activeTeams = selectedTeams.filter((team) => team.isActive)
|
||||
const inactiveTeams = selectedTeams.filter((team) => !team.isActive)
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
// Reset form when modal opens
|
||||
setNewStatus(true);
|
||||
setReason("");
|
||||
setErrors({});
|
||||
setNewStatus(true)
|
||||
setReason('')
|
||||
setErrors({})
|
||||
}
|
||||
}, [isOpen]);
|
||||
}, [isOpen])
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {}
|
||||
|
||||
if (!newStatus && !reason.trim()) {
|
||||
newErrors.reason = "Ekipleri pasif yaparken sebep belirtmeniz gerekli";
|
||||
newErrors.reason = 'Ekipleri pasif yaparken sebep belirtmeniz gerekli'
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
if (validateForm()) {
|
||||
const teamIds = selectedTeams.map((team) => team.id);
|
||||
onSave(teamIds, newStatus, reason.trim() || undefined);
|
||||
onClose();
|
||||
const teamIds = selectedTeams.map((team) => team.id)
|
||||
onSave(teamIds, newStatus, reason.trim() || undefined)
|
||||
onClose()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const getImpactAnalysis = () => {
|
||||
const teamsToActivate = newStatus ? inactiveTeams : [];
|
||||
const teamsToDeactivate = newStatus ? [] : activeTeams;
|
||||
const teamsToActivate = newStatus ? inactiveTeams : []
|
||||
const teamsToDeactivate = newStatus ? [] : activeTeams
|
||||
|
||||
return {
|
||||
teamsToActivate,
|
||||
teamsToDeactivate,
|
||||
totalMembers: selectedTeams.reduce(
|
||||
(total, team) => total + team.members.filter((m) => m.isActive).length,
|
||||
0
|
||||
0,
|
||||
),
|
||||
activeMembers: activeTeams.reduce(
|
||||
(total, team) => total + team.members.filter((m) => m.isActive).length,
|
||||
0
|
||||
0,
|
||||
),
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const impact = getImpactAnalysis();
|
||||
const impact = getImpactAnalysis()
|
||||
|
||||
return (
|
||||
isOpen && (
|
||||
|
|
@ -83,12 +83,8 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
|||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
Ekip Durumu Değiştir
|
||||
</h2>
|
||||
<p className="text-gray-600">
|
||||
{selectedTeams.length} ekibin durumunu değiştirin
|
||||
</p>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Ekip Durumu Değiştir</h2>
|
||||
<p className="text-gray-600">{selectedTeams.length} ekibin durumunu değiştirin</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
|
|
@ -102,9 +98,7 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
|||
<div className="p-3 space-y-3">
|
||||
{/* Status Selection */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||||
Yeni Durum
|
||||
</h3>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">Yeni Durum</h3>
|
||||
<div className="space-y-3">
|
||||
<label className="flex items-center p-2 border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer">
|
||||
<input
|
||||
|
|
@ -148,41 +142,31 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
|||
<textarea
|
||||
value={reason}
|
||||
onChange={(e) => {
|
||||
setReason(e.target.value);
|
||||
setReason(e.target.value)
|
||||
if (errors.reason) {
|
||||
setErrors((prev) => ({ ...prev, reason: "" }));
|
||||
setErrors((prev) => ({ ...prev, reason: '' }))
|
||||
}
|
||||
}}
|
||||
rows={2}
|
||||
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.reason ? "border-red-500" : "border-gray-300"
|
||||
errors.reason ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="Ekiplerin neden pasif yapıldığını açıklayın..."
|
||||
/>
|
||||
{errors.reason && (
|
||||
<p className="text-red-500 text-xs mt-1">{errors.reason}</p>
|
||||
)}
|
||||
{errors.reason && <p className="text-red-500 text-xs mt-1">{errors.reason}</p>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Impact Analysis */}
|
||||
<div className="bg-gray-50 rounded-lg p-3">
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-3">
|
||||
Etki Analizi
|
||||
</h3>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-3">Etki Analizi</h3>
|
||||
<div className="grid grid-cols-2 gap-3 mb-3">
|
||||
<div className="text-center">
|
||||
<div className="text-base font-bold text-blue-600">
|
||||
{selectedTeams.length}
|
||||
</div>
|
||||
<div className="text-sm text-gray-600">
|
||||
Toplam Seçili Ekip
|
||||
</div>
|
||||
<div className="text-base font-bold text-blue-600">{selectedTeams.length}</div>
|
||||
<div className="text-sm text-gray-600">Toplam Seçili Ekip</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-base font-bold text-green-600">
|
||||
{impact.totalMembers}
|
||||
</div>
|
||||
<div className="text-base font-bold text-green-600">{impact.totalMembers}</div>
|
||||
<div className="text-sm text-gray-600">Toplam Üye Sayısı</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -192,12 +176,10 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
|||
<div className="flex items-start space-x-3">
|
||||
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-yellow-800">
|
||||
Uyarı
|
||||
</h4>
|
||||
<h4 className="text-sm font-medium text-yellow-800">Uyarı</h4>
|
||||
<p className="text-sm text-yellow-700 mt-1">
|
||||
{impact.teamsToDeactivate.length} aktif ekip pasif
|
||||
yapılacak. Bu ekipler artık yeni iş emirleri alamayacak.
|
||||
{impact.teamsToDeactivate.length} aktif ekip pasif yapılacak. Bu ekipler
|
||||
artık yeni iş emirleri alamayacak.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -209,12 +191,10 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
|||
<div className="flex items-start space-x-3">
|
||||
<FaCheckCircle className="w-5 h-5 text-green-600 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-green-800">
|
||||
Bilgi
|
||||
</h4>
|
||||
<h4 className="text-sm font-medium text-green-800">Bilgi</h4>
|
||||
<p className="text-sm text-green-700 mt-1">
|
||||
{impact.teamsToActivate.length} pasif ekip aktif hale
|
||||
gelecek. Bu ekipler tekrar iş emirleri alabilecek.
|
||||
{impact.teamsToActivate.length} pasif ekip aktif hale gelecek. Bu ekipler
|
||||
tekrar iş emirleri alabilecek.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -224,9 +204,7 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
|||
|
||||
{/* Selected Teams List */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
||||
Etkilenecek Ekipler
|
||||
</h3>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">Etkilenecek Ekipler</h3>
|
||||
<div className="max-h-32 overflow-y-auto border border-gray-200 rounded-lg">
|
||||
{selectedTeams.map((team) => (
|
||||
<div
|
||||
|
|
@ -234,30 +212,26 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
|||
className="flex items-center justify-between p-2 border-b border-gray-100 last:border-b-0"
|
||||
>
|
||||
<div>
|
||||
<h4 className="font-medium text-sm text-gray-900">
|
||||
{team.name}
|
||||
</h4>
|
||||
<h4 className="font-medium text-sm text-gray-900">{team.name}</h4>
|
||||
<p className="text-sm text-gray-600">{team.code}</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<span
|
||||
className={`px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
team.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-gray-100 text-gray-800"
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}
|
||||
>
|
||||
{team.isActive ? "Aktif" : "Pasif"}
|
||||
{team.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500">→</span>
|
||||
<span
|
||||
className={`px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
newStatus
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-gray-100 text-gray-800"
|
||||
newStatus ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'
|
||||
}`}
|
||||
>
|
||||
{newStatus ? "Aktif" : "Pasif"}
|
||||
{newStatus ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -277,9 +251,7 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
|||
<button
|
||||
onClick={handleSave}
|
||||
className={`px-3 py-1.5 text-sm text-white rounded-lg transition-colors flex items-center space-x-2 ${
|
||||
newStatus
|
||||
? "bg-green-600 hover:bg-green-700"
|
||||
: "bg-red-600 hover:bg-red-700"
|
||||
newStatus ? 'bg-green-600 hover:bg-green-700' : 'bg-red-600 hover:bg-red-700'
|
||||
}`}
|
||||
>
|
||||
<FaSave className="w-4 h-4" />
|
||||
|
|
@ -289,7 +261,7 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default TeamStatusChangeModal;
|
||||
export default TeamStatusChangeModal
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React from "react";
|
||||
import React from 'react'
|
||||
import {
|
||||
FaTimes,
|
||||
FaEdit,
|
||||
|
|
@ -10,8 +10,8 @@ import {
|
|||
FaFileAlt,
|
||||
FaTools,
|
||||
FaCalendarAlt,
|
||||
} from "react-icons/fa";
|
||||
import { PmFaultNotification } from "../../../types/pm";
|
||||
} from 'react-icons/fa'
|
||||
import { PmFaultNotification } from '../../../types/pm'
|
||||
import {
|
||||
getFaultTypeColor,
|
||||
getFaultTypeText,
|
||||
|
|
@ -21,13 +21,13 @@ import {
|
|||
getCriticalityLevelText,
|
||||
getNotificationStatusColor,
|
||||
getNotificationStatusText,
|
||||
} from "../../../utils/erp";
|
||||
} from '../../../utils/erp'
|
||||
|
||||
interface ViewFaultNotificationModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onEdit: (notification: PmFaultNotification) => void;
|
||||
notification: PmFaultNotification | null;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onEdit: (notification: PmFaultNotification) => void
|
||||
notification: PmFaultNotification | null
|
||||
}
|
||||
|
||||
const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
||||
|
|
@ -36,7 +36,7 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
|||
onEdit,
|
||||
notification,
|
||||
}) => {
|
||||
if (!isOpen || !notification) return null;
|
||||
if (!isOpen || !notification) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -44,9 +44,7 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
|||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
{notification.notificationCode}
|
||||
</h2>
|
||||
<h2 className="text-lg font-semibold text-gray-900">{notification.notificationCode}</h2>
|
||||
<p className="text-sm text-gray-500 mt-1">{notification.title}</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
|
|
@ -72,28 +70,28 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
|||
<div className="flex flex-wrap gap-1.5">
|
||||
<span
|
||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getNotificationStatusColor(
|
||||
notification.status
|
||||
notification.status,
|
||||
)}`}
|
||||
>
|
||||
{getNotificationStatusText(notification.status)}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getPriorityColor(
|
||||
notification.priority
|
||||
notification.priority,
|
||||
)}`}
|
||||
>
|
||||
{getPriorityText(notification.priority)}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getFaultTypeColor(
|
||||
notification.faultType
|
||||
notification.faultType,
|
||||
)}`}
|
||||
>
|
||||
{getFaultTypeText(notification.faultType)}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getCriticalityLevelColor(
|
||||
notification.severity
|
||||
notification.severity,
|
||||
)}`}
|
||||
>
|
||||
Kritiklik: {getCriticalityLevelText(notification.severity)}
|
||||
|
|
@ -104,42 +102,26 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
|||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Arıza Bilgileri
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Arıza Bilgileri</h3>
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Açıklama
|
||||
</label>
|
||||
<p className="text-sm text-gray-800 mt-1">
|
||||
{notification.description}
|
||||
</p>
|
||||
<label className="text-sm font-medium text-gray-500">Açıklama</label>
|
||||
<p className="text-sm text-gray-800 mt-1">{notification.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
İş Merkezi Kodu
|
||||
</label>
|
||||
<p className="text-sm text-gray-800">
|
||||
{notification.workCenter.code}
|
||||
</p>
|
||||
<label className="text-sm font-medium text-gray-500">İş Merkezi Kodu</label>
|
||||
<p className="text-sm text-gray-800">{notification.workCenter.code}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
İş Merkezi Adı
|
||||
</label>
|
||||
<p className="text-sm text-gray-800">
|
||||
{notification.workCenter.name}
|
||||
</p>
|
||||
<label className="text-sm font-medium text-gray-500">İş Merkezi Adı</label>
|
||||
<p className="text-sm text-gray-800">{notification.workCenter.name}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Konum
|
||||
</label>
|
||||
<label className="text-sm font-medium text-gray-500">Konum</label>
|
||||
<p className="text-sm text-gray-800 flex items-center">
|
||||
<FaMapMarkerAlt className="w-4 h-4 mr-2 text-gray-400" />
|
||||
{notification.location}
|
||||
|
|
@ -175,14 +157,10 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
|||
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Süreç Bilgileri
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Süreç Bilgileri</h3>
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Bildiren Kişi
|
||||
</label>
|
||||
<label className="text-sm font-medium text-gray-500">Bildiren Kişi</label>
|
||||
<p className="text-sm text-gray-800 flex items-center">
|
||||
<FaUser className="w-4 h-4 mr-2 text-gray-400" />
|
||||
{notification.reportedBy}
|
||||
|
|
@ -190,21 +168,17 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Bildirim Tarihi
|
||||
</label>
|
||||
<label className="text-sm font-medium text-gray-500">Bildirim Tarihi</label>
|
||||
<p className="text-sm text-gray-800 flex items-center">
|
||||
<FaCalendarAlt className="w-4 h-4 mr-2 text-gray-400" />
|
||||
{notification.reportedAt.toLocaleDateString("tr-TR")}{" "}
|
||||
{notification.reportedAt.toLocaleTimeString("tr-TR")}
|
||||
{notification.reportedAt.toLocaleDateString('tr-TR')}{' '}
|
||||
{notification.reportedAt.toLocaleTimeString('tr-TR')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{notification.assignedTo && (
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Atanan Kişi
|
||||
</label>
|
||||
<label className="text-sm font-medium text-gray-500">Atanan Kişi</label>
|
||||
<p className="text-sm text-gray-800 flex items-center">
|
||||
<FaTools className="w-4 h-4 mr-2 text-gray-400" />
|
||||
{notification.assignedTo}
|
||||
|
|
@ -214,26 +188,18 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
|||
|
||||
{notification.workOrderId && (
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
İş Emri
|
||||
</label>
|
||||
<p className="text-sm text-gray-800">
|
||||
{notification.workOrderId}
|
||||
</p>
|
||||
<label className="text-sm font-medium text-gray-500">İş Emri</label>
|
||||
<p className="text-sm text-gray-800">{notification.workOrderId}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{notification.closedBy && notification.closedAt && (
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Kapatan
|
||||
</label>
|
||||
<p className="text-sm text-gray-800">
|
||||
{notification.closedBy}
|
||||
</p>
|
||||
<label className="text-sm font-medium text-gray-500">Kapatan</label>
|
||||
<p className="text-sm text-gray-800">{notification.closedBy}</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
{notification.closedAt.toLocaleDateString("tr-TR")}{" "}
|
||||
{notification.closedAt.toLocaleTimeString("tr-TR")}
|
||||
{notification.closedAt.toLocaleDateString('tr-TR')}{' '}
|
||||
{notification.closedAt.toLocaleTimeString('tr-TR')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -257,12 +223,8 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
|||
<div className="flex items-start space-x-2">
|
||||
<FaFileAlt className="w-5 h-5 text-green-600 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<h4 className="text-sm font-medium text-green-800 mb-1">
|
||||
Çözüm Notları
|
||||
</h4>
|
||||
<p className="text-sm text-green-700">
|
||||
{notification.resolutionNotes}
|
||||
</p>
|
||||
<h4 className="text-sm font-medium text-green-800 mb-1">Çözüm Notları</h4>
|
||||
<p className="text-sm text-green-700">{notification.resolutionNotes}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -279,16 +241,11 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
|||
</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-3">
|
||||
{notification.images.map((image, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border border-gray-200 rounded-lg p-2"
|
||||
>
|
||||
<div key={index} className="border border-gray-200 rounded-lg p-2">
|
||||
<div className="aspect-square bg-gray-100 rounded flex items-center justify-center">
|
||||
<FaCamera className="w-8 h-8 text-gray-400" />
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-1 truncate">
|
||||
{image}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1 truncate">{image}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -297,28 +254,22 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
|||
|
||||
{/* Timeline */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Zaman Çizelgesi
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Zaman Çizelgesi</h3>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-3 text-sm">
|
||||
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
|
||||
<span className="text-gray-500">Oluşturuldu:</span>
|
||||
<span className="font-medium">
|
||||
{notification.creationTime.toLocaleDateString("tr-TR")}{" "}
|
||||
{notification.creationTime.toLocaleTimeString("tr-TR")}
|
||||
{notification.creationTime.toLocaleDateString('tr-TR')}{' '}
|
||||
{notification.creationTime.toLocaleTimeString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3 text-sm">
|
||||
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
||||
<span className="text-gray-500">Son Güncelleme:</span>
|
||||
<span className="font-medium">
|
||||
{notification.lastModificationTime.toLocaleDateString(
|
||||
"tr-TR"
|
||||
)}{" "}
|
||||
{notification.lastModificationTime.toLocaleTimeString(
|
||||
"tr-TR"
|
||||
)}
|
||||
{notification.lastModificationTime.toLocaleDateString('tr-TR')}{' '}
|
||||
{notification.lastModificationTime.toLocaleTimeString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -336,7 +287,7 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default ViewFaultNotificationModal;
|
||||
export default ViewFaultNotificationModal
|
||||
|
|
|
|||
|
|
@ -1,23 +1,13 @@
|
|||
import React from "react";
|
||||
import {
|
||||
FaTimes,
|
||||
FaTools,
|
||||
FaClock,
|
||||
FaUser,
|
||||
FaExclamationTriangle,
|
||||
} from "react-icons/fa";
|
||||
import {
|
||||
PmMaintenancePlan,
|
||||
MaintenancePlanTypeEnum,
|
||||
FrequencyUnitEnum,
|
||||
} from "../../../types/pm";
|
||||
import { mockMaterials } from "../../../mocks/mockMaterials";
|
||||
import { PriorityEnum } from "../../../types/common";
|
||||
import React from 'react'
|
||||
import { FaTimes, FaTools, FaClock, FaUser, FaExclamationTriangle } from 'react-icons/fa'
|
||||
import { PmMaintenancePlan, MaintenancePlanTypeEnum, FrequencyUnitEnum } from '../../../types/pm'
|
||||
import { mockMaterials } from '../../../mocks/mockMaterials'
|
||||
import { PriorityEnum } from '../../../types/common'
|
||||
|
||||
interface ViewMaintenancePlanModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
plan: PmMaintenancePlan | null;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
plan: PmMaintenancePlan | null
|
||||
}
|
||||
|
||||
const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
||||
|
|
@ -25,93 +15,93 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
onClose,
|
||||
plan,
|
||||
}) => {
|
||||
if (!isOpen || !plan) return null;
|
||||
if (!isOpen || !plan) return null
|
||||
|
||||
const getPlanTypeDisplay = (type: MaintenancePlanTypeEnum) => {
|
||||
const types = {
|
||||
[MaintenancePlanTypeEnum.Preventive]: {
|
||||
label: "Önleyici Bakım",
|
||||
color: "bg-green-100 text-green-800",
|
||||
label: 'Önleyici Bakım',
|
||||
color: 'bg-green-100 text-green-800',
|
||||
},
|
||||
[MaintenancePlanTypeEnum.Predictive]: {
|
||||
label: "Kestirimci Bakım",
|
||||
color: "bg-blue-100 text-blue-800",
|
||||
label: 'Kestirimci Bakım',
|
||||
color: 'bg-blue-100 text-blue-800',
|
||||
},
|
||||
[MaintenancePlanTypeEnum.Corrective]: {
|
||||
label: "Düzeltici Bakım",
|
||||
color: "bg-orange-100 text-orange-800",
|
||||
label: 'Düzeltici Bakım',
|
||||
color: 'bg-orange-100 text-orange-800',
|
||||
},
|
||||
[MaintenancePlanTypeEnum.Condition]: {
|
||||
label: "Durum Bazlı Bakım",
|
||||
color: "bg-purple-100 text-purple-800",
|
||||
label: 'Durum Bazlı Bakım',
|
||||
color: 'bg-purple-100 text-purple-800',
|
||||
},
|
||||
};
|
||||
return types[type] || { label: type, color: "bg-gray-100 text-gray-800" };
|
||||
};
|
||||
}
|
||||
return types[type] || { label: type, color: 'bg-gray-100 text-gray-800' }
|
||||
}
|
||||
|
||||
const getPriorityDisplay = (priority: PriorityEnum) => {
|
||||
const priorities = {
|
||||
[PriorityEnum.Low]: {
|
||||
label: "Düşük",
|
||||
color: "bg-gray-100 text-gray-800",
|
||||
label: 'Düşük',
|
||||
color: 'bg-gray-100 text-gray-800',
|
||||
icon: null,
|
||||
},
|
||||
[PriorityEnum.Normal]: {
|
||||
label: "Normal",
|
||||
color: "bg-blue-100 text-blue-800",
|
||||
label: 'Normal',
|
||||
color: 'bg-blue-100 text-blue-800',
|
||||
icon: null,
|
||||
},
|
||||
[PriorityEnum.High]: {
|
||||
label: "Yüksek",
|
||||
color: "bg-yellow-100 text-yellow-800",
|
||||
label: 'Yüksek',
|
||||
color: 'bg-yellow-100 text-yellow-800',
|
||||
icon: FaExclamationTriangle,
|
||||
},
|
||||
[PriorityEnum.Urgent]: {
|
||||
label: "Acil",
|
||||
color: "bg-red-100 text-red-800",
|
||||
label: 'Acil',
|
||||
color: 'bg-red-100 text-red-800',
|
||||
icon: FaExclamationTriangle,
|
||||
},
|
||||
};
|
||||
}
|
||||
return (
|
||||
priorities[priority] || {
|
||||
label: priority,
|
||||
color: "bg-gray-100 text-gray-800",
|
||||
color: 'bg-gray-100 text-gray-800',
|
||||
icon: null,
|
||||
}
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
const getFrequencyDisplay = (frequency: number, unit: FrequencyUnitEnum) => {
|
||||
const units = {
|
||||
[FrequencyUnitEnum.Days]: frequency === 1 ? "gün" : "gün",
|
||||
[FrequencyUnitEnum.Weeks]: frequency === 1 ? "hafta" : "hafta",
|
||||
[FrequencyUnitEnum.Months]: frequency === 1 ? "ay" : "ay",
|
||||
[FrequencyUnitEnum.Years]: frequency === 1 ? "yıl" : "yıl",
|
||||
[FrequencyUnitEnum.Hours]: frequency === 1 ? "saat" : "saat",
|
||||
[FrequencyUnitEnum.Cycles]: frequency === 1 ? "çevrim" : "çevrim",
|
||||
};
|
||||
return `Her ${frequency} ${units[unit]}`;
|
||||
};
|
||||
[FrequencyUnitEnum.Days]: frequency === 1 ? 'gün' : 'gün',
|
||||
[FrequencyUnitEnum.Weeks]: frequency === 1 ? 'hafta' : 'hafta',
|
||||
[FrequencyUnitEnum.Months]: frequency === 1 ? 'ay' : 'ay',
|
||||
[FrequencyUnitEnum.Years]: frequency === 1 ? 'yıl' : 'yıl',
|
||||
[FrequencyUnitEnum.Hours]: frequency === 1 ? 'saat' : 'saat',
|
||||
[FrequencyUnitEnum.Cycles]: frequency === 1 ? 'çevrim' : 'çevrim',
|
||||
}
|
||||
return `Her ${frequency} ${units[unit]}`
|
||||
}
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
return new Date(date).toLocaleDateString("tr-TR", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
};
|
||||
return new Date(date).toLocaleDateString('tr-TR', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})
|
||||
}
|
||||
|
||||
const formatDuration = (minutes: number) => {
|
||||
if (minutes < 60) return `${minutes} dakika`;
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const remainingMinutes = minutes % 60;
|
||||
if (remainingMinutes === 0) return `${hours} saat`;
|
||||
return `${hours} saat ${remainingMinutes} dakika`;
|
||||
};
|
||||
if (minutes < 60) return `${minutes} dakika`
|
||||
const hours = Math.floor(minutes / 60)
|
||||
const remainingMinutes = minutes % 60
|
||||
if (remainingMinutes === 0) return `${hours} saat`
|
||||
return `${hours} saat ${remainingMinutes} dakika`
|
||||
}
|
||||
|
||||
const isOverdue = plan.nextDue && new Date(plan.nextDue) < new Date();
|
||||
const planTypeInfo = getPlanTypeDisplay(plan.planType);
|
||||
const priorityInfo = getPriorityDisplay(plan.priority);
|
||||
const isOverdue = plan.nextDue && new Date(plan.nextDue) < new Date()
|
||||
const planTypeInfo = getPlanTypeDisplay(plan.planType)
|
||||
const priorityInfo = getPriorityDisplay(plan.priority)
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -121,16 +111,11 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
<div className="flex items-center space-x-3">
|
||||
<FaTools className="w-5 h-5 text-blue-600" />
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
{plan.planCode}
|
||||
</h2>
|
||||
<h2 className="text-lg font-semibold text-gray-900">{plan.planCode}</h2>
|
||||
<p className="text-sm text-gray-500">Bakım Planı Detayları</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
>
|
||||
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -147,19 +132,15 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
<span
|
||||
className={`inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium ${priorityInfo.color}`}
|
||||
>
|
||||
{priorityInfo.icon && (
|
||||
<priorityInfo.icon className="w-3 h-3 mr-1" />
|
||||
)}
|
||||
{priorityInfo.icon && <priorityInfo.icon className="w-3 h-3 mr-1" />}
|
||||
{priorityInfo.label}
|
||||
</span>
|
||||
<span
|
||||
className={`inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium ${
|
||||
plan.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
plan.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
||||
}`}
|
||||
>
|
||||
{plan.isActive ? "Aktif" : "Pasif"}
|
||||
{plan.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
{isOverdue && (
|
||||
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-red-100 text-red-800">
|
||||
|
|
@ -173,39 +154,25 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Temel Bilgiler
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Temel Bilgiler</h3>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-500">
|
||||
Plan Kodu
|
||||
</label>
|
||||
<p className="text-sm text-gray-900 font-mono">
|
||||
{plan.planCode}
|
||||
</p>
|
||||
<label className="block text-sm font-medium text-gray-500">Plan Kodu</label>
|
||||
<p className="text-sm text-gray-900 font-mono">{plan.planCode}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-500">
|
||||
İş Merkezi
|
||||
</label>
|
||||
<p className="text-sm text-gray-900 font-mono">
|
||||
{plan.workCenter?.code}
|
||||
</p>
|
||||
<label className="block text-sm font-medium text-gray-500">İş Merkezi</label>
|
||||
<p className="text-sm text-gray-900 font-mono">{plan.workCenter?.code}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-500">
|
||||
Açıklama
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-500">Açıklama</label>
|
||||
<p className="text-sm text-gray-900">{plan.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Sıklık ve Süre
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Sıklık ve Süre</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<FaClock className="w-4 h-4 text-gray-400" />
|
||||
|
|
@ -214,25 +181,19 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-500">
|
||||
Tahmini Süre
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-500">Tahmini Süre</label>
|
||||
<p className="text-sm text-gray-900">
|
||||
{formatDuration(plan.estimatedDuration)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-500">
|
||||
Sonraki Bakım
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-500">Sonraki Bakım</label>
|
||||
<p
|
||||
className={`text-sm font-medium ${
|
||||
isOverdue ? "text-red-600" : "text-gray-900"
|
||||
isOverdue ? 'text-red-600' : 'text-gray-900'
|
||||
}`}
|
||||
>
|
||||
{plan.nextDue
|
||||
? formatDate(plan.nextDue)
|
||||
: "Belirtilmemiş"}
|
||||
{plan.nextDue ? formatDate(plan.nextDue) : 'Belirtilmemiş'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -241,46 +202,32 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Tarih Bilgileri
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Tarih Bilgileri</h3>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-500">
|
||||
Oluşturulma
|
||||
</label>
|
||||
<p className="text-sm text-gray-900">
|
||||
{formatDate(plan.creationTime)}
|
||||
</p>
|
||||
<label className="block text-sm font-medium text-gray-500">Oluşturulma</label>
|
||||
<p className="text-sm text-gray-900">{formatDate(plan.creationTime)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-500">
|
||||
Son Güncelleme
|
||||
</label>
|
||||
<p className="text-sm text-gray-900">
|
||||
{formatDate(plan.lastModificationTime)}
|
||||
</p>
|
||||
<p className="text-sm text-gray-900">{formatDate(plan.lastModificationTime)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-3">
|
||||
İstatistikler
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-3">İstatistikler</h3>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-500">
|
||||
Durum
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-500">Durum</label>
|
||||
<p className="text-sm text-gray-900">
|
||||
{plan.isActive ? "Aktif plan" : "Pasif plan"}
|
||||
{plan.isActive ? 'Aktif plan' : 'Pasif plan'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-500">
|
||||
Tahmini Süre
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-500">Tahmini Süre</label>
|
||||
<p className="text-sm text-gray-900">
|
||||
{formatDuration(plan.estimatedDuration)}
|
||||
</p>
|
||||
|
|
@ -293,9 +240,7 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
{/* Instructions */}
|
||||
{plan.instructions && (
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Bakım Talimatları
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Bakım Talimatları</h3>
|
||||
<div className="bg-gray-50 rounded-lg p-3">
|
||||
<pre className="text-sm text-gray-700 whitespace-pre-wrap font-sans">
|
||||
{plan.instructions}
|
||||
|
|
@ -327,9 +272,7 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
{/* Required Materials */}
|
||||
{plan.requiredMaterials && plan.requiredMaterials.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Gerekli Malzemeler
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Gerekli Malzemeler</h3>
|
||||
<div className="bg-white border border-gray-200 rounded-lg overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
|
|
@ -350,9 +293,7 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{plan.requiredMaterials.map((material, index) => {
|
||||
const materialInfo = mockMaterials.find(
|
||||
(m) => m.id === material.materialId
|
||||
);
|
||||
const materialInfo = mockMaterials.find((m) => m.id === material.materialId)
|
||||
return (
|
||||
<tr key={index} className="hover:bg-gray-50">
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
|
|
@ -361,9 +302,7 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
{materialInfo?.code || material.materialId}
|
||||
</div>
|
||||
{materialInfo && (
|
||||
<div className="text-xs text-gray-500">
|
||||
{materialInfo.name}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">{materialInfo.name}</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
|
|
@ -377,15 +316,15 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
<span
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
material.isRequired
|
||||
? "bg-red-100 text-red-800"
|
||||
: "bg-green-100 text-green-800"
|
||||
? 'bg-red-100 text-red-800'
|
||||
: 'bg-green-100 text-green-800'
|
||||
}`}
|
||||
>
|
||||
{material.isRequired ? "Zorunlu" : "Opsiyonel"}
|
||||
{material.isRequired ? 'Zorunlu' : 'Opsiyonel'}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -406,7 +345,7 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default ViewMaintenancePlanModal;
|
||||
export default ViewMaintenancePlanModal
|
||||
|
|
|
|||
|
|
@ -1,39 +1,20 @@
|
|||
import React from "react";
|
||||
import {
|
||||
FaTimes,
|
||||
FaEdit,
|
||||
FaUser,
|
||||
FaAward,
|
||||
FaEnvelope,
|
||||
FaPhone,
|
||||
FaClock,
|
||||
} from "react-icons/fa";
|
||||
import { Team, TeamRoleEnum } from "../../../types/common";
|
||||
import {
|
||||
getTeamRoleColor,
|
||||
getTeamRoleIcon,
|
||||
getTeamRoleText,
|
||||
} from "../../../utils/erp";
|
||||
import React from 'react'
|
||||
import { FaTimes, FaEdit, FaUser, FaAward, FaEnvelope, FaPhone, FaClock } from 'react-icons/fa'
|
||||
import { Team, TeamRoleEnum } from '../../../types/common'
|
||||
import { getTeamRoleColor, getTeamRoleIcon, getTeamRoleText } from '../../../utils/erp'
|
||||
|
||||
interface ViewTeamModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onEdit: (team: Team) => void;
|
||||
team: Team | null;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onEdit: (team: Team) => void
|
||||
team: Team | null
|
||||
}
|
||||
|
||||
const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onEdit,
|
||||
team,
|
||||
}) => {
|
||||
if (!team) return null;
|
||||
const ViewTeamModal: React.FC<ViewTeamModalProps> = ({ isOpen, onClose, onEdit, team }) => {
|
||||
if (!team) return null
|
||||
|
||||
const leader = team.members.find(
|
||||
(member) => member.role === TeamRoleEnum.Lead
|
||||
);
|
||||
const activeMembers = team.members.filter((member) => member.isActive);
|
||||
const leader = team.members.find((member) => member.role === TeamRoleEnum.Lead)
|
||||
const activeMembers = team.members.filter((member) => member.isActive)
|
||||
|
||||
return (
|
||||
isOpen &&
|
||||
|
|
@ -43,9 +24,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
|||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
{team.name}
|
||||
</h2>
|
||||
<h2 className="text-lg font-semibold text-gray-900">{team.name}</h2>
|
||||
<p className="text-sm text-gray-500 mt-1">{team.code}</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
|
|
@ -71,42 +50,30 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
|||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Ekip Bilgileri
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Ekip Bilgileri</h3>
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Ekip Kodu
|
||||
</label>
|
||||
<label className="text-sm font-medium text-gray-500">Ekip Kodu</label>
|
||||
<p className="text-sm text-gray-900">{team.code}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Ekip Adı
|
||||
</label>
|
||||
<label className="text-sm font-medium text-gray-500">Ekip Adı</label>
|
||||
<p className="text-sm text-gray-900">{team.name}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Açıklama
|
||||
</label>
|
||||
<p className="text-sm text-gray-900">
|
||||
{team.description}
|
||||
</p>
|
||||
<label className="text-sm font-medium text-gray-500">Açıklama</label>
|
||||
<p className="text-sm text-gray-900">{team.description}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-500">
|
||||
Durum
|
||||
</label>
|
||||
<label className="text-sm font-medium text-gray-500">Durum</label>
|
||||
<p
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
team.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-gray-100 text-gray-800"
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}
|
||||
>
|
||||
{team.isActive ? "Aktif" : "Pasif"}
|
||||
{team.isActive ? 'Aktif' : 'Pasif'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -115,9 +82,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
|||
{/* Specializations */}
|
||||
{team.specializations && team.specializations.length > 0 && (
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-gray-500 mb-1.5">
|
||||
Uzmanlık Alanları
|
||||
</h4>
|
||||
<h4 className="text-sm font-medium text-gray-500 mb-1.5">Uzmanlık Alanları</h4>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{team.specializations.map((spec, index) => (
|
||||
<span
|
||||
|
|
@ -133,22 +98,16 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Zaman Bilgileri
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Zaman Bilgileri</h3>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2 text-sm text-gray-600">
|
||||
<FaClock className="w-4 h-4" />
|
||||
<span>
|
||||
Oluşturuldu:{" "}
|
||||
{team.creationTime.toLocaleDateString("tr-TR")}
|
||||
</span>
|
||||
<span>Oluşturuldu: {team.creationTime.toLocaleDateString('tr-TR')}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 text-sm text-gray-600">
|
||||
<FaClock className="w-4 h-4" />
|
||||
<span>
|
||||
Son Güncelleme:{" "}
|
||||
{team.lastModificationTime.toLocaleDateString("tr-TR")}
|
||||
Son Güncelleme: {team.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -158,9 +117,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
|||
{/* Team Leader */}
|
||||
{leader && (
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
||||
Ekip Lideri
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-3">Ekip Lideri</h3>
|
||||
<div className="bg-purple-50 rounded-lg p-3">
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="w-10 h-10 bg-purple-600 rounded-full flex items-center justify-center">
|
||||
|
|
@ -169,12 +126,11 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
|||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2 mb-1">
|
||||
<h4 className="font-medium text-gray-900">
|
||||
{leader.employee?.firstName}{" "}
|
||||
{leader.employee?.lastName}
|
||||
{leader.employee?.firstName} {leader.employee?.lastName}
|
||||
</h4>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium flex items-center space-x-1 ${getTeamRoleColor(
|
||||
leader.role
|
||||
leader.role,
|
||||
)}`}
|
||||
>
|
||||
{getTeamRoleIcon(leader.role)}
|
||||
|
|
@ -199,8 +155,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
|||
)}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 mt-2">
|
||||
Katılma Tarihi:{" "}
|
||||
{leader.joinDate.toLocaleDateString("tr-TR")}
|
||||
Katılma Tarihi: {leader.joinDate.toLocaleDateString('tr-TR')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -230,12 +185,11 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
|||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-2 mb-1">
|
||||
<h4 className="font-medium text-gray-900">
|
||||
{member.employee?.firstName}{" "}
|
||||
{member.employee?.lastName}
|
||||
{member.employee?.firstName} {member.employee?.lastName}
|
||||
</h4>
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium flex items-center space-x-1 ${getTeamRoleColor(
|
||||
member.role
|
||||
member.role,
|
||||
)}`}
|
||||
>
|
||||
{getTeamRoleIcon(member.role)}
|
||||
|
|
@ -259,8 +213,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
|||
</div>
|
||||
)}
|
||||
<div className="text-xs text-gray-500">
|
||||
Katılma Tarihi:{" "}
|
||||
{member.joinDate.toLocaleDateString("tr-TR")}
|
||||
Katılma Tarihi: {member.joinDate.toLocaleDateString('tr-TR')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -283,7 +236,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default ViewTeamModal;
|
||||
export default ViewTeamModal
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React from "react";
|
||||
import React from 'react'
|
||||
import {
|
||||
FaTimes,
|
||||
FaCog,
|
||||
|
|
@ -7,20 +7,20 @@ import {
|
|||
FaUser,
|
||||
FaWrench,
|
||||
FaExclamationTriangle,
|
||||
} from "react-icons/fa";
|
||||
import { PmWorkCenter, WorkOrderStatusEnum } from "../../../types/pm";
|
||||
} from 'react-icons/fa'
|
||||
import { PmWorkCenter, WorkOrderStatusEnum } from '../../../types/pm'
|
||||
import {
|
||||
getWorkCenterStatusColor,
|
||||
getWorkCenterStatusIcon,
|
||||
getWorkCenterStatusText,
|
||||
getCriticalityLevelColor,
|
||||
getCriticalityLevelText,
|
||||
} from "../../../utils/erp";
|
||||
} from '../../../utils/erp'
|
||||
|
||||
interface ViewWorkCenterModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
workCenter: PmWorkCenter;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
workCenter: PmWorkCenter
|
||||
}
|
||||
|
||||
const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||
|
|
@ -28,28 +28,28 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
onClose,
|
||||
workCenter,
|
||||
}) => {
|
||||
if (!isOpen || !workCenter) return null;
|
||||
if (!isOpen || !workCenter) return null
|
||||
|
||||
const isWarrantyExpiring = (workCenter: PmWorkCenter) => {
|
||||
if (!workCenter.warrantyExpiry) return false;
|
||||
const today = new Date();
|
||||
const diffTime = workCenter.warrantyExpiry.getTime() - today.getTime();
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||
return diffDays <= 90 && diffDays > 0;
|
||||
};
|
||||
if (!workCenter.warrantyExpiry) return false
|
||||
const today = new Date()
|
||||
const diffTime = workCenter.warrantyExpiry.getTime() - today.getTime()
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
||||
return diffDays <= 90 && diffDays > 0
|
||||
}
|
||||
|
||||
const isWarrantyExpired = (workCenter: PmWorkCenter) => {
|
||||
if (!workCenter.warrantyExpiry) return false;
|
||||
const today = new Date();
|
||||
return workCenter.warrantyExpiry < today;
|
||||
};
|
||||
if (!workCenter.warrantyExpiry) return false
|
||||
const today = new Date()
|
||||
return workCenter.warrantyExpiry < today
|
||||
}
|
||||
|
||||
const getWorkCenterAge = (installationDate: Date) => {
|
||||
const today = new Date();
|
||||
const diffTime = today.getTime() - installationDate.getTime();
|
||||
const diffYears = Math.floor(diffTime / (1000 * 60 * 60 * 24 * 365));
|
||||
return diffYears;
|
||||
};
|
||||
const today = new Date()
|
||||
const diffTime = today.getTime() - installationDate.getTime()
|
||||
const diffYears = Math.floor(diffTime / (1000 * 60 * 60 * 24 * 365))
|
||||
return diffYears
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -59,16 +59,11 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
<div className="flex items-center space-x-3">
|
||||
<FaCog className="w-5 h-5 text-blue-600" />
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
{workCenter.name}
|
||||
</h2>
|
||||
<h2 className="text-lg font-semibold text-gray-900">{workCenter.name}</h2>
|
||||
<p className="text-sm text-gray-500">{workCenter.code}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
>
|
||||
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -79,15 +74,13 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Durum Bilgileri
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Durum Bilgileri</h3>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-gray-700">Operasyonel Durum:</span>
|
||||
<span
|
||||
className={`px-2.5 py-1 rounded-full text-xs font-medium flex items-center space-x-1.5 ${getWorkCenterStatusColor(
|
||||
workCenter.status
|
||||
workCenter.status,
|
||||
)}`}
|
||||
>
|
||||
{getWorkCenterStatusIcon(workCenter.status)}
|
||||
|
|
@ -98,7 +91,7 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
<span className="text-gray-700">Kritiklik Seviyesi:</span>
|
||||
<span
|
||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getCriticalityLevelColor(
|
||||
workCenter.criticality
|
||||
workCenter.criticality,
|
||||
)}`}
|
||||
>
|
||||
{getCriticalityLevelText(workCenter.criticality)}
|
||||
|
|
@ -109,20 +102,18 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
<span
|
||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${
|
||||
workCenter.isActive
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-red-100 text-red-800'
|
||||
}`}
|
||||
>
|
||||
{workCenter.isActive ? "Aktif" : "Pasif"}
|
||||
{workCenter.isActive ? 'Aktif' : 'Pasif'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Lokasyon Bilgileri
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Lokasyon Bilgileri</h3>
|
||||
<div className="flex items-center space-x-2 text-gray-700">
|
||||
<FaMapMarkerAlt className="w-4 h-4 text-blue-600" />
|
||||
<span>{workCenter.location}</span>
|
||||
|
|
@ -132,38 +123,34 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Temel Bilgiler
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Temel Bilgiler</h3>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">Üretici:</span>
|
||||
<span className="text-gray-900 font-medium">
|
||||
{workCenter.manufacturer || "-"}
|
||||
{workCenter.manufacturer || '-'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">Model:</span>
|
||||
<span className="text-gray-900 font-medium">
|
||||
{workCenter.model || "-"}
|
||||
</span>
|
||||
<span className="text-gray-900 font-medium">{workCenter.model || '-'}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">Seri No:</span>
|
||||
<span className="text-gray-900 font-medium">
|
||||
{workCenter.serialNumber || "-"}
|
||||
{workCenter.serialNumber || '-'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">İş Merkezi Tipi:</span>
|
||||
<span className="text-gray-900 font-medium">
|
||||
{workCenter.workCenterType?.name || "-"}
|
||||
{workCenter.workCenterType?.name || '-'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">Kategori:</span>
|
||||
<span className="text-gray-900 font-medium">
|
||||
{workCenter.workCenterType?.category || "-"}
|
||||
{workCenter.workCenterType?.category || '-'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -174,9 +161,7 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
{/* Description */}
|
||||
{workCenter.description && (
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Açıklama
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Açıklama</h3>
|
||||
<p className="text-sm text-gray-700 bg-gray-50 p-3 rounded-lg">
|
||||
{workCenter.description}
|
||||
</p>
|
||||
|
|
@ -185,19 +170,15 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
|
||||
{/* Date Information */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Tarih Bilgileri
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Tarih Bilgileri</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div className="bg-gray-50 p-3 rounded-lg">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<FaCalendar className="w-4 h-4 text-blue-600" />
|
||||
<span className="font-medium text-gray-900">
|
||||
Kurulum Tarihi
|
||||
</span>
|
||||
<span className="font-medium text-gray-900">Kurulum Tarihi</span>
|
||||
</div>
|
||||
<p className="text-gray-700">
|
||||
{workCenter.installationDate.toLocaleDateString("tr-TR")}
|
||||
{workCenter.installationDate.toLocaleDateString('tr-TR')}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Yaş: {getWorkCenterAge(workCenter.installationDate)} yıl
|
||||
|
|
@ -208,30 +189,23 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
<div className="bg-gray-50 p-3 rounded-lg">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<FaCalendar className="w-4 h-4 text-orange-600" />
|
||||
<span className="font-medium text-gray-900">
|
||||
Garanti Bitiş Tarihi
|
||||
</span>
|
||||
<span className="font-medium text-gray-900">Garanti Bitiş Tarihi</span>
|
||||
</div>
|
||||
<p
|
||||
className={`font-medium ${
|
||||
isWarrantyExpired(workCenter)
|
||||
? "text-red-600"
|
||||
? 'text-red-600'
|
||||
: isWarrantyExpiring(workCenter)
|
||||
? "text-orange-600"
|
||||
: "text-gray-700"
|
||||
? 'text-orange-600'
|
||||
: 'text-gray-700'
|
||||
}`}
|
||||
>
|
||||
{workCenter.warrantyExpiry.toLocaleDateString("tr-TR")}
|
||||
{isWarrantyExpiring(workCenter) &&
|
||||
!isWarrantyExpired(workCenter) && (
|
||||
<span className="text-xs text-orange-600 ml-2">
|
||||
(Yakında sona eriyor)
|
||||
</span>
|
||||
)}
|
||||
{workCenter.warrantyExpiry.toLocaleDateString('tr-TR')}
|
||||
{isWarrantyExpiring(workCenter) && !isWarrantyExpired(workCenter) && (
|
||||
<span className="text-xs text-orange-600 ml-2">(Yakında sona eriyor)</span>
|
||||
)}
|
||||
{isWarrantyExpired(workCenter) && (
|
||||
<span className="text-xs text-red-600 ml-2">
|
||||
(Garanti doldu)
|
||||
</span>
|
||||
<span className="text-xs text-red-600 ml-2">(Garanti doldu)</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -243,58 +217,49 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
<span className="font-medium text-gray-900">Oluşturan</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
{workCenter.creationTime.toLocaleDateString("tr-TR")}
|
||||
{workCenter.creationTime.toLocaleDateString('tr-TR')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 p-3 rounded-lg">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<FaCalendar className="w-4 h-4 text-gray-600" />
|
||||
<span className="font-medium text-gray-900">
|
||||
Son Güncelleme
|
||||
</span>
|
||||
<span className="font-medium text-gray-900">Son Güncelleme</span>
|
||||
</div>
|
||||
<p className="text-gray-700">
|
||||
{workCenter.lastModificationTime.toLocaleDateString("tr-TR")}
|
||||
{workCenter.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Technical Specifications */}
|
||||
{workCenter.specifications &&
|
||||
workCenter.specifications.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Teknik Özellikler
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
{workCenter.specifications.map((spec) => (
|
||||
<div key={spec.id} className="bg-gray-50 p-3 rounded-lg">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="font-medium text-gray-900">
|
||||
{spec.specificationName}
|
||||
{workCenter.specifications && workCenter.specifications.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Teknik Özellikler</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
{workCenter.specifications.map((spec) => (
|
||||
<div key={spec.id} className="bg-gray-50 p-3 rounded-lg">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="font-medium text-gray-900">{spec.specificationName}</span>
|
||||
{spec.isRequired && (
|
||||
<span className="px-2 py-0.5 bg-blue-100 text-blue-800 text-xs rounded-full">
|
||||
Zorunlu
|
||||
</span>
|
||||
{spec.isRequired && (
|
||||
<span className="px-2 py-0.5 bg-blue-100 text-blue-800 text-xs rounded-full">
|
||||
Zorunlu
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-gray-700">
|
||||
{spec.specificationValue} {spec.unit}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-gray-700">
|
||||
{spec.specificationValue} {spec.unit}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Maintenance Plans Summary */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
Bakım Planları
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">Bakım Planları</h3>
|
||||
<div className="bg-gray-50 p-3 rounded-lg">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
|
@ -310,9 +275,7 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
|
||||
{/* Work Orders Summary */}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
||||
İş Emirleri
|
||||
</h3>
|
||||
<h3 className="text-base font-medium text-gray-900 mb-2">İş Emirleri</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||
<div className="bg-gray-50 p-3 rounded-lg">
|
||||
<div className="flex items-center justify-between">
|
||||
|
|
@ -331,7 +294,7 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
<div>
|
||||
<p className="text-lg font-semibold text-gray-900">
|
||||
{workCenter.workOrders?.filter(
|
||||
(wo) => wo.status === WorkOrderStatusEnum.InProgress
|
||||
(wo) => wo.status === WorkOrderStatusEnum.InProgress,
|
||||
).length || 0}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">Devam Eden</p>
|
||||
|
|
@ -367,7 +330,7 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default ViewWorkCenterModal;
|
||||
export default ViewWorkCenterModal
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React from "react";
|
||||
import React from 'react'
|
||||
import {
|
||||
FaTimes,
|
||||
FaEye,
|
||||
|
|
@ -11,21 +11,21 @@ import {
|
|||
FaDollarSign,
|
||||
FaCheck,
|
||||
FaExclamationTriangle,
|
||||
} from "react-icons/fa";
|
||||
import { PmMaintenanceWorkOrder, WorkOrderStatusEnum } from "../../../types/pm";
|
||||
} from 'react-icons/fa'
|
||||
import { PmMaintenanceWorkOrder, WorkOrderStatusEnum } from '../../../types/pm'
|
||||
import {
|
||||
getPriorityColor,
|
||||
getPriorityText,
|
||||
getWorkOrderStatusColor,
|
||||
getWorkOrderStatusText,
|
||||
getWorkOrderTypeText,
|
||||
} from "../../../utils/erp";
|
||||
} from '../../../utils/erp'
|
||||
|
||||
interface ViewWorkOrderModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onEdit: (workOrder: PmMaintenanceWorkOrder) => void;
|
||||
workOrder: PmMaintenanceWorkOrder;
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onEdit: (workOrder: PmMaintenanceWorkOrder) => void
|
||||
workOrder: PmMaintenanceWorkOrder
|
||||
}
|
||||
|
||||
const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||
|
|
@ -36,36 +36,25 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
}) => {
|
||||
const getCompletionPercentage = () => {
|
||||
const completedActivities = workOrder.activities.filter(
|
||||
(activity) => activity.completedAt
|
||||
).length;
|
||||
const totalActivities = workOrder.activities.length;
|
||||
return totalActivities > 0
|
||||
? Math.round((completedActivities / totalActivities) * 100)
|
||||
: 0;
|
||||
};
|
||||
(activity) => activity.completedAt,
|
||||
).length
|
||||
const totalActivities = workOrder.activities.length
|
||||
return totalActivities > 0 ? Math.round((completedActivities / totalActivities) * 100) : 0
|
||||
}
|
||||
|
||||
const getTotalMaterialCost = () => {
|
||||
return workOrder.materials.reduce(
|
||||
(total, material) => total + material.totalCost,
|
||||
0
|
||||
);
|
||||
};
|
||||
return workOrder.materials.reduce((total, material) => total + material.totalCost, 0)
|
||||
}
|
||||
|
||||
const getTotalPlannedDuration = () => {
|
||||
return workOrder.activities.reduce(
|
||||
(total, activity) => total + activity.plannedDuration,
|
||||
0
|
||||
);
|
||||
};
|
||||
return workOrder.activities.reduce((total, activity) => total + activity.plannedDuration, 0)
|
||||
}
|
||||
|
||||
const getTotalActualDuration = () => {
|
||||
return workOrder.activities.reduce(
|
||||
(total, activity) => total + activity.actualDuration,
|
||||
0
|
||||
);
|
||||
};
|
||||
return workOrder.activities.reduce((total, activity) => total + activity.actualDuration, 0)
|
||||
}
|
||||
|
||||
if (!isOpen) return null;
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
|
|
@ -74,13 +63,11 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="flex items-center space-x-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<FaEye className="w-5 h-5 text-blue-600" />
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
İş Emri Detayları
|
||||
</h3>
|
||||
<h3 className="text-lg font-semibold text-gray-900">İş Emri Detayları</h3>
|
||||
</div>
|
||||
<span
|
||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
||||
workOrder.status
|
||||
workOrder.status,
|
||||
)}`}
|
||||
>
|
||||
{getWorkOrderStatusText(workOrder.status)}
|
||||
|
|
@ -94,10 +81,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<FaEdit className="w-4 h-4" />
|
||||
<span>Düzenle</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -113,14 +97,14 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="space-y-1">
|
||||
<span
|
||||
className={`inline-block px-2 py-0.5 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
||||
workOrder.status
|
||||
workOrder.status,
|
||||
)}`}
|
||||
>
|
||||
{getWorkOrderTypeText(workOrder.orderType)}
|
||||
</span>
|
||||
<span
|
||||
className={`inline-block px-2 py-0.5 rounded-full text-xs font-medium ml-2 ${getPriorityColor(
|
||||
workOrder.priority
|
||||
workOrder.priority,
|
||||
)}`}
|
||||
>
|
||||
{getPriorityText(workOrder.priority)}
|
||||
|
|
@ -133,7 +117,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
{workOrder.workCenter?.code || workOrder.workCenterId}
|
||||
</p>
|
||||
<p className="text-xs text-gray-600">
|
||||
{workOrder.workCenter?.name || "İş merkezi bilgisi bulunamadı"}
|
||||
{workOrder.workCenter?.name || 'İş merkezi bilgisi bulunamadı'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -147,7 +131,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<label className="text-xs text-gray-600">Atanan</label>
|
||||
<p className="font-medium text-sm text-gray-900 flex items-center">
|
||||
<FaWrench className="w-4 h-4 mr-2 text-gray-500" />
|
||||
{workOrder.assignedTo || "Atanmadı"}
|
||||
{workOrder.assignedTo || 'Atanmadı'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -155,9 +139,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
|
||||
{/* Description */}
|
||||
<div className="mb-4">
|
||||
<h4 className="text-base font-semibold text-gray-900 mb-2">
|
||||
Açıklama
|
||||
</h4>
|
||||
<h4 className="text-base font-semibold text-gray-900 mb-2">Açıklama</h4>
|
||||
<div className="bg-gray-50 rounded-lg p-3">
|
||||
<p className="text-sm text-gray-800">{workOrder.description}</p>
|
||||
</div>
|
||||
|
|
@ -175,14 +157,14 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="flex justify-between items-center p-2 bg-gray-50 rounded-lg text-sm">
|
||||
<span className="text-gray-600">Oluşturulma</span>
|
||||
<span className="font-medium">
|
||||
{workOrder.creationTime.toLocaleDateString("tr-TR")}
|
||||
{workOrder.creationTime.toLocaleDateString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
{workOrder.scheduledStart && (
|
||||
<div className="flex justify-between items-center p-2 bg-blue-50 rounded-lg text-sm">
|
||||
<span className="text-gray-600">Planlanan Başlangıç</span>
|
||||
<span className="font-medium">
|
||||
{workOrder.scheduledStart.toLocaleDateString("tr-TR")}
|
||||
{workOrder.scheduledStart.toLocaleDateString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -190,7 +172,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="flex justify-between items-center p-2 bg-blue-50 rounded-lg text-sm">
|
||||
<span className="text-gray-600">Planlanan Bitiş</span>
|
||||
<span className="font-medium">
|
||||
{workOrder.scheduledEnd.toLocaleDateString("tr-TR")}
|
||||
{workOrder.scheduledEnd.toLocaleDateString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -198,7 +180,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="flex justify-between items-center p-2 bg-green-50 rounded-lg text-sm">
|
||||
<span className="text-gray-600">Gerçek Başlangıç</span>
|
||||
<span className="font-medium">
|
||||
{workOrder.actualStart.toLocaleDateString("tr-TR")}
|
||||
{workOrder.actualStart.toLocaleDateString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -206,7 +188,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="flex justify-between items-center p-2 bg-green-50 rounded-lg text-sm">
|
||||
<span className="text-gray-600">Gerçek Bitiş</span>
|
||||
<span className="font-medium">
|
||||
{workOrder.actualEnd.toLocaleDateString("tr-TR")}
|
||||
{workOrder.actualEnd.toLocaleDateString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -224,13 +206,13 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-gray-600">Tahmini Maliyet</span>
|
||||
<span className="font-medium">
|
||||
₺{workOrder.estimatedCost.toLocaleString("tr-TR")}
|
||||
₺{workOrder.estimatedCost.toLocaleString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-600">Gerçek Maliyet</span>
|
||||
<span className="font-medium">
|
||||
₺{workOrder.actualCost.toLocaleString("tr-TR")}
|
||||
₺{workOrder.actualCost.toLocaleString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -239,9 +221,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="p-2 bg-orange-50 rounded-lg text-sm">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-gray-600">İlerleme</span>
|
||||
<span className="font-medium">
|
||||
{getCompletionPercentage()}%
|
||||
</span>
|
||||
<span className="font-medium">{getCompletionPercentage()}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||
<div
|
||||
|
|
@ -255,15 +235,11 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="p-2 bg-blue-50 rounded-lg text-sm">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-gray-600">Planlanan Süre</span>
|
||||
<span className="font-medium">
|
||||
{getTotalPlannedDuration()} dk
|
||||
</span>
|
||||
<span className="font-medium">{getTotalPlannedDuration()} dk</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-600">Gerçek Süre</span>
|
||||
<span className="font-medium">
|
||||
{getTotalActualDuration()} dk
|
||||
</span>
|
||||
<span className="font-medium">{getTotalActualDuration()} dk</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -275,8 +251,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="mb-4">
|
||||
<h4 className="text-base font-semibold text-gray-900 mb-2 flex items-center">
|
||||
<FaTools className="w-5 h-5 mr-2" />
|
||||
Aktiviteler (
|
||||
{workOrder.activities.filter((a) => a.completedAt).length}/
|
||||
Aktiviteler ({workOrder.activities.filter((a) => a.completedAt).length}/
|
||||
{workOrder.activities.length})
|
||||
</h4>
|
||||
<div className="space-y-2">
|
||||
|
|
@ -285,8 +260,8 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
key={activity.id}
|
||||
className={`p-3 rounded-lg border ${
|
||||
activity.completedAt
|
||||
? "bg-green-50 border-green-200"
|
||||
: "bg-gray-50 border-gray-200"
|
||||
? 'bg-green-50 border-green-200'
|
||||
: 'bg-gray-50 border-gray-200'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
|
|
@ -304,27 +279,21 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 text-xs">
|
||||
<div>
|
||||
<span className="text-gray-600">Planlanan Süre:</span>
|
||||
<p className="font-medium">
|
||||
{activity.plannedDuration} dk
|
||||
</p>
|
||||
<p className="font-medium">{activity.plannedDuration} dk</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-600">Gerçek Süre:</span>
|
||||
<p className="font-medium">
|
||||
{activity.actualDuration} dk
|
||||
</p>
|
||||
<p className="font-medium">{activity.actualDuration} dk</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-600">Yapan:</span>
|
||||
<p className="font-medium">
|
||||
{activity.performedBy || "Atanmadı"}
|
||||
</p>
|
||||
<p className="font-medium">{activity.performedBy || 'Atanmadı'}</p>
|
||||
</div>
|
||||
{activity.completedAt && (
|
||||
<div>
|
||||
<span className="text-gray-600">Tamamlanma:</span>
|
||||
<p className="font-medium">
|
||||
{activity.completedAt.toLocaleDateString("tr-TR")}
|
||||
{activity.completedAt.toLocaleDateString('tr-TR')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -376,25 +345,19 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<tr key={material.id}>
|
||||
<td className="px-3 py-2 text-sm">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900">
|
||||
{material.materialCode}
|
||||
</p>
|
||||
<p className="text-gray-600">
|
||||
{material.materialName}
|
||||
</p>
|
||||
<p className="font-medium text-gray-900">{material.materialCode}</p>
|
||||
<p className="text-gray-600">{material.materialName}</p>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-sm text-gray-900">
|
||||
{material.plannedQuantity}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-sm text-gray-900">{material.actualQuantity}</td>
|
||||
<td className="px-3 py-2 text-sm text-gray-900">
|
||||
{material.actualQuantity}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-sm text-gray-900">
|
||||
₺{material.unitCost.toLocaleString("tr-TR")}
|
||||
₺{material.unitCost.toLocaleString('tr-TR')}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-sm font-medium text-gray-900">
|
||||
₺{material.totalCost.toLocaleString("tr-TR")}
|
||||
₺{material.totalCost.toLocaleString('tr-TR')}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
|
@ -408,7 +371,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
Toplam Malzeme Maliyeti
|
||||
</td>
|
||||
<td className="px-3 py-2 text-sm font-bold text-gray-900">
|
||||
₺{getTotalMaterialCost().toLocaleString("tr-TR")}
|
||||
₺{getTotalMaterialCost().toLocaleString('tr-TR')}
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
|
@ -420,18 +383,14 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
{/* Notes */}
|
||||
{(workOrder.notes || workOrder.completionNotes) && (
|
||||
<div className="mb-4">
|
||||
<h4 className="text-base font-semibold text-gray-900 mb-2">
|
||||
Notlar
|
||||
</h4>
|
||||
<h4 className="text-base font-semibold text-gray-900 mb-2">Notlar</h4>
|
||||
<div className="space-y-2">
|
||||
{workOrder.notes && (
|
||||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3">
|
||||
<div className="flex items-start space-x-2">
|
||||
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
||||
<div>
|
||||
<h5 className="font-medium text-yellow-800 mb-1">
|
||||
İş Emri Notları
|
||||
</h5>
|
||||
<h5 className="font-medium text-yellow-800 mb-1">İş Emri Notları</h5>
|
||||
<p className="text-yellow-700">{workOrder.notes}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -442,12 +401,8 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div className="flex items-start space-x-2">
|
||||
<FaCheck className="w-5 h-5 text-green-600 mt-0.5" />
|
||||
<div>
|
||||
<h5 className="font-medium text-green-800 mb-1">
|
||||
Tamamlanma Notları
|
||||
</h5>
|
||||
<p className="text-green-700">
|
||||
{workOrder.completionNotes}
|
||||
</p>
|
||||
<h5 className="font-medium text-green-800 mb-1">Tamamlanma Notları</h5>
|
||||
<p className="text-green-700">{workOrder.completionNotes}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -462,22 +417,22 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
|||
<div>
|
||||
<span>Oluşturulma:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{workOrder.creationTime.toLocaleDateString("tr-TR")}{" "}
|
||||
{workOrder.creationTime.toLocaleTimeString("tr-TR")}
|
||||
{workOrder.creationTime.toLocaleDateString('tr-TR')}{' '}
|
||||
{workOrder.creationTime.toLocaleTimeString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>Son Güncelleme:</span>
|
||||
<span className="ml-2 font-medium">
|
||||
{workOrder.lastModificationTime.toLocaleDateString("tr-TR")}{" "}
|
||||
{workOrder.lastModificationTime.toLocaleTimeString("tr-TR")}
|
||||
{workOrder.lastModificationTime.toLocaleDateString('tr-TR')}{' '}
|
||||
{workOrder.lastModificationTime.toLocaleTimeString('tr-TR')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default ViewWorkOrderModal;
|
||||
export default ViewWorkOrderModal
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue