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,
|
"RequiredPermissionName": null,
|
||||||
"IsDisabled": false
|
"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",
|
"ParentCode": "App.Maintenance",
|
||||||
"Code": "App.Maintenance.Workcenters",
|
"Code": "App.Maintenance.Workcenters",
|
||||||
|
|
@ -3373,7 +3363,7 @@
|
||||||
{
|
{
|
||||||
"ParentCode": "App.Maintenance",
|
"ParentCode": "App.Maintenance",
|
||||||
"Code": "App.Maintenance.Teams",
|
"Code": "App.Maintenance.Teams",
|
||||||
"DisplayName": "Bakım Takımları",
|
"DisplayName": "Bakım Ekipleri",
|
||||||
"Order": 5,
|
"Order": 5,
|
||||||
"Url": "/admin/maintenance/teams",
|
"Url": "/admin/maintenance/teams",
|
||||||
"Icon": "FcConferenceCall",
|
"Icon": "FcConferenceCall",
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,14 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaUser, FaUsers } from "react-icons/fa";
|
import { FaTimes, FaSave, FaUser, FaUsers } from 'react-icons/fa'
|
||||||
import { PmFaultNotification } from "../../../types/pm";
|
import { PmFaultNotification } from '../../../types/pm'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||||
|
|
||||||
interface AssignNotificationModalProps {
|
interface AssignNotificationModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (assignments: {
|
onSave: (assignments: { notificationIds: string[]; assignedTo?: string; teamId?: string }) => void
|
||||||
notificationIds: string[];
|
notifications: PmFaultNotification[]
|
||||||
assignedTo?: string;
|
|
||||||
teamId?: string;
|
|
||||||
}) => void;
|
|
||||||
notifications: PmFaultNotification[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||||
|
|
@ -21,46 +17,44 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||||
onSave,
|
onSave,
|
||||||
notifications,
|
notifications,
|
||||||
}) => {
|
}) => {
|
||||||
const [assignmentType, setAssignmentType] = useState<"person" | "team">(
|
const [assignmentType, setAssignmentType] = useState<'person' | 'team'>('person')
|
||||||
"person"
|
const [assignedTo, setAssignedTo] = useState('')
|
||||||
);
|
const [teamId, setTeamId] = useState('')
|
||||||
const [assignedTo, setAssignedTo] = useState("");
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
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 validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (assignmentType === "person" && !assignedTo) {
|
if (assignmentType === 'person' && !assignedTo) {
|
||||||
newErrors.assignedTo = "Kişi seçimi gerekli";
|
newErrors.assignedTo = 'Kişi seçimi gerekli'
|
||||||
}
|
}
|
||||||
if (assignmentType === "team" && !teamId) {
|
if (assignmentType === 'team' && !teamId) {
|
||||||
newErrors.teamId = "Ekip seçimi gerekli";
|
newErrors.teamId = 'Ekip seçimi gerekli'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
const assignmentData = {
|
const assignmentData = {
|
||||||
notificationIds: notifications.map((n) => n.id),
|
notificationIds: notifications.map((n) => n.id),
|
||||||
...(assignmentType === "person" ? { assignedTo } : { teamId }),
|
...(assignmentType === 'person' ? { assignedTo } : { teamId }),
|
||||||
};
|
}
|
||||||
|
|
||||||
onSave(assignmentData);
|
onSave(assignmentData)
|
||||||
onClose();
|
onClose()
|
||||||
|
|
||||||
// Reset form
|
// Reset form
|
||||||
setAssignmentType("person");
|
setAssignmentType('person')
|
||||||
setAssignedTo("");
|
setAssignedTo('')
|
||||||
setTeamId("");
|
setTeamId('')
|
||||||
setErrors({});
|
setErrors({})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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 */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-3 border-b border-gray-200">
|
<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>
|
<h2 className="text-xl font-bold text-gray-900">Atama Yap</h2>
|
||||||
<button
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5" />
|
<FaTimes className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -86,14 +77,12 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{notifications.map((notification) => (
|
{notifications.map((notification) => (
|
||||||
<div key={notification.id} className="text-sm text-blue-700">
|
<div key={notification.id} className="text-sm text-blue-700">
|
||||||
<span className="font-medium">
|
<span className="font-medium">{notification.notificationCode}</span>
|
||||||
{notification.notificationCode}
|
{' - '}
|
||||||
</span>
|
|
||||||
{" - "}
|
|
||||||
<span>{notification.title}</span>
|
<span>{notification.title}</span>
|
||||||
{" ("}
|
{' ('}
|
||||||
<span>{notification.workCenter.code}</span>
|
<span>{notification.workCenter.code}</span>
|
||||||
{")"}
|
{')'}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -101,18 +90,14 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||||
|
|
||||||
{/* Assignment Type */}
|
{/* Assignment Type */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Atama Türü</label>
|
||||||
Atama Türü
|
|
||||||
</label>
|
|
||||||
<div className="flex space-x-3">
|
<div className="flex space-x-3">
|
||||||
<label className="flex items-center">
|
<label className="flex items-center">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
value="person"
|
value="person"
|
||||||
checked={assignmentType === "person"}
|
checked={assignmentType === 'person'}
|
||||||
onChange={(e) =>
|
onChange={(e) => setAssignmentType(e.target.value as 'person' | 'team')}
|
||||||
setAssignmentType(e.target.value as "person" | "team")
|
|
||||||
}
|
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
/>
|
/>
|
||||||
<FaUser className="w-4 h-4 mr-2 text-gray-500" />
|
<FaUser className="w-4 h-4 mr-2 text-gray-500" />
|
||||||
|
|
@ -122,10 +107,8 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
value="team"
|
value="team"
|
||||||
checked={assignmentType === "team"}
|
checked={assignmentType === 'team'}
|
||||||
onChange={(e) =>
|
onChange={(e) => setAssignmentType(e.target.value as 'person' | 'team')}
|
||||||
setAssignmentType(e.target.value as "person" | "team")
|
|
||||||
}
|
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
/>
|
/>
|
||||||
<FaUsers className="w-4 h-4 mr-2 text-gray-500" />
|
<FaUsers className="w-4 h-4 mr-2 text-gray-500" />
|
||||||
|
|
@ -135,7 +118,7 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Person Assignment */}
|
{/* Person Assignment */}
|
||||||
{assignmentType === "person" && (
|
{assignmentType === 'person' && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
Atanacak Kişi *
|
Atanacak Kişi *
|
||||||
|
|
@ -144,7 +127,7 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||||
value={assignedTo}
|
value={assignedTo}
|
||||||
onChange={(e) => setAssignedTo(e.target.value)}
|
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 ${
|
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>
|
<option value="">Kişi seçin</option>
|
||||||
|
|
@ -161,7 +144,7 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Team Assignment */}
|
{/* Team Assignment */}
|
||||||
{assignmentType === "team" && (
|
{assignmentType === 'team' && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
Atanacak Ekip *
|
Atanacak Ekip *
|
||||||
|
|
@ -170,7 +153,7 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||||
value={teamId}
|
value={teamId}
|
||||||
onChange={(e) => setTeamId(e.target.value)}
|
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 ${
|
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>
|
<option value="">Ekip seçin</option>
|
||||||
|
|
@ -182,40 +165,28 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.teamId && (
|
{errors.teamId && <p className="text-red-500 text-xs mt-1">{errors.teamId}</p>}
|
||||||
<p className="text-red-500 text-xs mt-1">{errors.teamId}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Assignment Preview */}
|
{/* Assignment Preview */}
|
||||||
{(assignedTo || teamId) && (
|
{(assignedTo || teamId) && (
|
||||||
<div className="bg-green-50 border border-green-200 rounded-lg p-2">
|
<div className="bg-green-50 border border-green-200 rounded-lg p-2">
|
||||||
<h4 className="text-sm font-medium text-green-800 mb-1">
|
<h4 className="text-sm font-medium text-green-800 mb-1">Atama Özeti</h4>
|
||||||
Atama Özeti
|
|
||||||
</h4>
|
|
||||||
<div className="text-sm text-green-700">
|
<div className="text-sm text-green-700">
|
||||||
<p>
|
<p>
|
||||||
<strong>{notifications.length}</strong> arıza bildirimi{" "}
|
<strong>{notifications.length}</strong> arıza bildirimi{' '}
|
||||||
{assignmentType === "person" ? (
|
{assignmentType === 'person' ? (
|
||||||
<>
|
<>
|
||||||
<strong>
|
<strong>
|
||||||
{
|
{mockEmployees.find((emp) => emp.fullName === assignedTo)?.fullName}
|
||||||
mockEmployees.find(
|
|
||||||
(emp) => emp.fullName === assignedTo
|
|
||||||
)?.fullName
|
|
||||||
}
|
|
||||||
</strong>
|
</strong>
|
||||||
kişisine atanacak
|
kişisine atanacak
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<strong>
|
<strong>
|
||||||
{
|
{mockMaintenanceTeams.find((team) => team.id === teamId)?.name}
|
||||||
mockMaintenanceTeams.find(
|
|
||||||
(team) => team.id === teamId
|
|
||||||
)?.name
|
|
||||||
}
|
|
||||||
</strong>
|
</strong>
|
||||||
ekibine atanacak
|
ekibine atanacak
|
||||||
</>
|
</>
|
||||||
|
|
@ -244,7 +215,7 @@ const AssignNotificationModal: React.FC<AssignNotificationModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default AssignNotificationModal;
|
export default AssignNotificationModal
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaWrench } from "react-icons/fa";
|
import { FaTimes, FaSave, FaWrench } from 'react-icons/fa'
|
||||||
import { mockMaintenancePlans } from "../../../mocks/mockMaintenancePlans";
|
import { mockMaintenancePlans } from '../../../mocks/mockMaintenancePlans'
|
||||||
import { Team } from "../../../types/common";
|
import { Team } from '../../../types/common'
|
||||||
|
|
||||||
interface AssignWorkOrderModalProps {
|
interface AssignWorkOrderModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (assignments: { teamId: string; planIds: string[] }[]) => void;
|
onSave: (assignments: { teamId: string; planIds: string[] }[]) => void
|
||||||
selectedTeams: Team[];
|
selectedTeams: Team[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
|
|
@ -16,22 +16,20 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
onSave,
|
onSave,
|
||||||
selectedTeams,
|
selectedTeams,
|
||||||
}) => {
|
}) => {
|
||||||
const [assignments, setAssignments] = useState<Record<string, string[]>>({});
|
const [assignments, setAssignments] = useState<Record<string, string[]>>({})
|
||||||
const [bulkPlanIds, setBulkPlanIds] = useState<string[]>([]);
|
const [bulkPlanIds, setBulkPlanIds] = useState<string[]>([])
|
||||||
const [useBulkAssignment, setUseBulkAssignment] = useState(true);
|
const [useBulkAssignment, setUseBulkAssignment] = useState(true)
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
// Get available maintenance plans
|
// Get available maintenance plans
|
||||||
const availablePlans = mockMaintenancePlans.filter((plan) => plan.isActive);
|
const availablePlans = mockMaintenancePlans.filter((plan) => plan.isActive)
|
||||||
|
|
||||||
const handleBulkPlanChange = (planId: string) => {
|
const handleBulkPlanChange = (planId: string) => {
|
||||||
setBulkPlanIds((prev) =>
|
setBulkPlanIds((prev) =>
|
||||||
prev.includes(planId)
|
prev.includes(planId) ? prev.filter((id) => id !== planId) : [...prev, planId],
|
||||||
? prev.filter((id) => id !== planId)
|
)
|
||||||
: [...prev, planId]
|
}
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleIndividualPlanChange = (teamId: string, planId: string) => {
|
const handleIndividualPlanChange = (teamId: string, planId: string) => {
|
||||||
setAssignments((prev) => ({
|
setAssignments((prev) => ({
|
||||||
|
|
@ -39,40 +37,40 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
[teamId]: prev[teamId]?.includes(planId)
|
[teamId]: prev[teamId]?.includes(planId)
|
||||||
? prev[teamId].filter((id) => id !== planId)
|
? prev[teamId].filter((id) => id !== planId)
|
||||||
: [...(prev[teamId] || []), planId],
|
: [...(prev[teamId] || []), planId],
|
||||||
}));
|
}))
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (useBulkAssignment) {
|
if (useBulkAssignment) {
|
||||||
if (bulkPlanIds.length === 0) {
|
if (bulkPlanIds.length === 0) {
|
||||||
alert("Lütfen en az bir bakım planı seçin");
|
alert('Lütfen en az bir bakım planı seçin')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
const bulkAssignments = selectedTeams.map((team) => ({
|
const bulkAssignments = selectedTeams.map((team) => ({
|
||||||
teamId: team.id,
|
teamId: team.id,
|
||||||
planIds: bulkPlanIds,
|
planIds: bulkPlanIds,
|
||||||
}));
|
}))
|
||||||
onSave(bulkAssignments);
|
onSave(bulkAssignments)
|
||||||
} else {
|
} else {
|
||||||
const individualAssignments = selectedTeams
|
const individualAssignments = selectedTeams
|
||||||
.map((team) => ({
|
.map((team) => ({
|
||||||
teamId: team.id,
|
teamId: team.id,
|
||||||
planIds: assignments[team.id] || [],
|
planIds: assignments[team.id] || [],
|
||||||
}))
|
}))
|
||||||
.filter((assignment) => assignment.planIds.length > 0);
|
.filter((assignment) => assignment.planIds.length > 0)
|
||||||
|
|
||||||
if (individualAssignments.length === 0) {
|
if (individualAssignments.length === 0) {
|
||||||
alert("Lütfen en az bir ekip için bakım planı seçin");
|
alert('Lütfen en az bir ekip için bakım planı seçin')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
onSave(individualAssignments);
|
onSave(individualAssignments)
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose();
|
onClose()
|
||||||
// Reset state
|
// Reset state
|
||||||
setAssignments({});
|
setAssignments({})
|
||||||
setBulkPlanIds([]);
|
setBulkPlanIds([])
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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
|
Seçili ekiplere ({selectedTeams.length}) bakım planları atayın
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5" />
|
<FaTimes className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -97,9 +92,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
<div className="p-3 space-y-3">
|
<div className="p-3 space-y-3">
|
||||||
{/* Assignment Mode Selection */}
|
{/* Assignment Mode Selection */}
|
||||||
<div className="border border-gray-200 rounded-lg p-3">
|
<div className="border border-gray-200 rounded-lg p-3">
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Atama Türü</h3>
|
||||||
Atama Türü
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="flex items-center">
|
<label className="flex items-center">
|
||||||
<input
|
<input
|
||||||
|
|
@ -128,9 +121,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
|
|
||||||
{/* Selected Teams */}
|
{/* Selected Teams */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Seçili Ekipler</h3>
|
||||||
Seçili Ekipler
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
{selectedTeams.map((team) => (
|
{selectedTeams.map((team) => (
|
||||||
<div
|
<div
|
||||||
|
|
@ -139,9 +130,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
>
|
>
|
||||||
<FaWrench className="w-5 h-5 text-blue-600" />
|
<FaWrench className="w-5 h-5 text-blue-600" />
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-medium text-sm text-gray-900">
|
<h4 className="font-medium text-sm text-gray-900">{team.name}</h4>
|
||||||
{team.name}
|
|
||||||
</h4>
|
|
||||||
<p className="text-gray-600">{team.code}</p>
|
<p className="text-gray-600">{team.code}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -169,12 +158,8 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
className="mt-1 text-blue-600 focus:ring-blue-500"
|
className="mt-1 text-blue-600 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h4 className="font-medium text-gray-900">
|
<h4 className="font-medium text-gray-900">{plan.planCode}</h4>
|
||||||
{plan.planCode}
|
<p className="text-gray-600">{plan.description}</p>
|
||||||
</h4>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
{plan.description}
|
|
||||||
</p>
|
|
||||||
<div className="flex items-center space-x-4 mt-1 text-xs text-gray-500">
|
<div className="flex items-center space-x-4 mt-1 text-xs text-gray-500">
|
||||||
<span>
|
<span>
|
||||||
Sıklık: {plan.frequency} {plan.frequencyUnit}
|
Sıklık: {plan.frequency} {plan.frequencyUnit}
|
||||||
|
|
@ -192,15 +177,10 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
{/* Individual Assignment */}
|
{/* Individual Assignment */}
|
||||||
{!useBulkAssignment && (
|
{!useBulkAssignment && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Bireysel Plan Atamaları</h3>
|
||||||
Bireysel Plan Atamaları
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{selectedTeams.map((team) => (
|
{selectedTeams.map((team) => (
|
||||||
<div
|
<div key={team.id} className="border border-gray-200 rounded-lg p-2">
|
||||||
key={team.id}
|
|
||||||
className="border border-gray-200 rounded-lg p-2"
|
|
||||||
>
|
|
||||||
<h4 className="font-medium text-sm text-gray-900 mb-2">
|
<h4 className="font-medium text-sm text-gray-900 mb-2">
|
||||||
{team.name} ({team.code})
|
{team.name} ({team.code})
|
||||||
</h4>
|
</h4>
|
||||||
|
|
@ -212,21 +192,13 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={
|
checked={assignments[team.id]?.includes(plan.id) || false}
|
||||||
assignments[team.id]?.includes(plan.id) || false
|
onChange={() => handleIndividualPlanChange(team.id, plan.id)}
|
||||||
}
|
|
||||||
onChange={() =>
|
|
||||||
handleIndividualPlanChange(team.id, plan.id)
|
|
||||||
}
|
|
||||||
className="mt-1 text-blue-600 focus:ring-blue-500"
|
className="mt-1 text-blue-600 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h5 className="text-sm font-medium text-gray-900">
|
<h5 className="text-sm font-medium text-gray-900">{plan.planCode}</h5>
|
||||||
{plan.planCode}
|
<p className="text-xs text-gray-600">{plan.description}</p>
|
||||||
</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">
|
<div className="flex items-center space-x-3 mt-1 text-xs text-gray-500">
|
||||||
<span>
|
<span>
|
||||||
Sıklık: {plan.frequency} {plan.frequencyUnit}
|
Sıklık: {plan.frequency} {plan.frequencyUnit}
|
||||||
|
|
@ -262,7 +234,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default AssignWorkOrderModal;
|
export default AssignWorkOrderModal
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,21 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaUserCog, FaUser, FaUsers } from "react-icons/fa";
|
import { FaTimes, FaUserCog, FaUser, FaUsers } from 'react-icons/fa'
|
||||||
import { PmMaintenanceWorkOrder } from "../../../types/pm";
|
import { PmMaintenanceWorkOrder } from '../../../types/pm'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||||
|
|
||||||
interface AssignWorkOrderModalProps {
|
interface AssignWorkOrderModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onAssign: (
|
onAssign: (workOrders: PmMaintenanceWorkOrder[], assignmentData: AssignmentData) => void
|
||||||
workOrders: PmMaintenanceWorkOrder[],
|
workOrders: PmMaintenanceWorkOrder[]
|
||||||
assignmentData: AssignmentData
|
|
||||||
) => void;
|
|
||||||
workOrders: PmMaintenanceWorkOrder[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AssignmentData {
|
interface AssignmentData {
|
||||||
assignmentType: "person" | "team";
|
assignmentType: 'person' | 'team'
|
||||||
assignedTo?: string;
|
assignedTo?: string
|
||||||
maintenanceTeamId?: string;
|
maintenanceTeamId?: string
|
||||||
notes?: string;
|
notes?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
|
|
@ -27,65 +24,60 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
onAssign,
|
onAssign,
|
||||||
workOrders,
|
workOrders,
|
||||||
}) => {
|
}) => {
|
||||||
const [assignmentType, setAssignmentType] = useState<"person" | "team">(
|
const [assignmentType, setAssignmentType] = useState<'person' | 'team'>('person')
|
||||||
"person"
|
|
||||||
);
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
assignedTo: "",
|
assignedTo: '',
|
||||||
maintenanceTeamId: "",
|
maintenanceTeamId: '',
|
||||||
notes: "",
|
notes: '',
|
||||||
});
|
})
|
||||||
|
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (assignmentType === "person" && !formData.assignedTo) {
|
if (assignmentType === 'person' && !formData.assignedTo) {
|
||||||
newErrors.assignedTo = "Atanacak kişi seçilmelidir";
|
newErrors.assignedTo = 'Atanacak kişi seçilmelidir'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (assignmentType === "team" && !formData.maintenanceTeamId) {
|
if (assignmentType === 'team' && !formData.maintenanceTeamId) {
|
||||||
newErrors.maintenanceTeamId = "Atanacak ekip seçilmelidir";
|
newErrors.maintenanceTeamId = 'Atanacak ekip seçilmelidir'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
if (!validateForm()) return;
|
if (!validateForm()) return
|
||||||
|
|
||||||
const assignmentData: AssignmentData = {
|
const assignmentData: AssignmentData = {
|
||||||
assignmentType,
|
assignmentType,
|
||||||
assignedTo: assignmentType === "person" ? formData.assignedTo : undefined,
|
assignedTo: assignmentType === 'person' ? formData.assignedTo : undefined,
|
||||||
maintenanceTeamId:
|
maintenanceTeamId: assignmentType === 'team' ? formData.maintenanceTeamId : undefined,
|
||||||
assignmentType === "team" ? formData.maintenanceTeamId : undefined,
|
|
||||||
notes: formData.notes || undefined,
|
notes: formData.notes || undefined,
|
||||||
};
|
}
|
||||||
|
|
||||||
onAssign(workOrders, assignmentData);
|
onAssign(workOrders, assignmentData)
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const getSelectedTeam = () => {
|
const getSelectedTeam = () => {
|
||||||
if (assignmentType === "team" && formData.maintenanceTeamId) {
|
if (assignmentType === 'team' && formData.maintenanceTeamId) {
|
||||||
return mockMaintenanceTeams.find(
|
return mockMaintenanceTeams.find((team) => team.id === formData.maintenanceTeamId)
|
||||||
(team) => team.id === formData.maintenanceTeamId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
};
|
}
|
||||||
|
|
||||||
const getSelectedEmployee = () => {
|
const getSelectedEmployee = () => {
|
||||||
if (assignmentType === "person" && formData.assignedTo) {
|
if (assignmentType === 'person' && formData.assignedTo) {
|
||||||
return mockEmployees.find((emp) => emp.fullName === formData.assignedTo);
|
return mockEmployees.find((emp) => emp.fullName === formData.assignedTo)
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
};
|
}
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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" />
|
<FaUserCog className="w-5 h-5 mr-2 text-blue-600" />
|
||||||
İş Emirlerini Ata
|
İş Emirlerini Ata
|
||||||
</h3>
|
</h3>
|
||||||
<button
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5" />
|
<FaTimes className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -110,22 +99,15 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
</h4>
|
</h4>
|
||||||
<div className="max-h-32 overflow-y-auto border border-gray-200 rounded-lg">
|
<div className="max-h-32 overflow-y-auto border border-gray-200 rounded-lg">
|
||||||
{workOrders.map((workOrder) => (
|
{workOrders.map((workOrder) => (
|
||||||
<div
|
<div key={workOrder.id} className="p-2 border-b border-gray-100 last:border-b-0">
|
||||||
key={workOrder.id}
|
|
||||||
className="p-2 border-b border-gray-100 last:border-b-0"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-gray-900">
|
<p className="font-medium text-gray-900">{workOrder.workOrderNumber}</p>
|
||||||
{workOrder.workOrderNumber}
|
<p className="text-sm text-gray-600 truncate">{workOrder.description}</p>
|
||||||
</p>
|
|
||||||
<p className="text-sm text-gray-600 truncate">
|
|
||||||
{workOrder.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right text-sm">
|
<div className="text-right text-sm">
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">
|
||||||
Mevcut Atama: {workOrder.assignedTo || "Atanmadı"}
|
Mevcut Atama: {workOrder.assignedTo || 'Atanmadı'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -137,43 +119,33 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
<form onSubmit={handleSubmit} className="space-y-3">
|
<form onSubmit={handleSubmit} className="space-y-3">
|
||||||
{/* Assignment Type Selection */}
|
{/* Assignment Type Selection */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Atama Türü</label>
|
||||||
Atama Türü
|
|
||||||
</label>
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<label className="relative">
|
<label className="relative">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="assignmentType"
|
name="assignmentType"
|
||||||
value="person"
|
value="person"
|
||||||
checked={assignmentType === "person"}
|
checked={assignmentType === 'person'}
|
||||||
onChange={(e) =>
|
onChange={(e) => setAssignmentType(e.target.value as 'person' | 'team')}
|
||||||
setAssignmentType(e.target.value as "person" | "team")
|
|
||||||
}
|
|
||||||
className="sr-only"
|
className="sr-only"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={`p-2 border-2 rounded-lg cursor-pointer transition-colors ${
|
className={`p-2 border-2 rounded-lg cursor-pointer transition-colors ${
|
||||||
assignmentType === "person"
|
assignmentType === 'person'
|
||||||
? "border-blue-500 bg-blue-50"
|
? 'border-blue-500 bg-blue-50'
|
||||||
: "border-gray-300 bg-white hover:bg-gray-50"
|
: 'border-gray-300 bg-white hover:bg-gray-50'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<FaUser
|
<FaUser
|
||||||
className={`w-5 h-5 ${
|
className={`w-5 h-5 ${
|
||||||
assignmentType === "person"
|
assignmentType === 'person' ? 'text-blue-600' : 'text-gray-400'
|
||||||
? "text-blue-600"
|
|
||||||
: "text-gray-400"
|
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-medium text-sm text-gray-900">
|
<h4 className="font-medium text-sm text-gray-900">Kişiye Ata</h4>
|
||||||
Kişiye Ata
|
<p className="text-xs text-gray-600">Belirli bir çalışana atama yap</p>
|
||||||
</h4>
|
|
||||||
<p className="text-xs text-gray-600">
|
|
||||||
Belirli bir çalışana atama yap
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -184,34 +156,26 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
type="radio"
|
type="radio"
|
||||||
name="assignmentType"
|
name="assignmentType"
|
||||||
value="team"
|
value="team"
|
||||||
checked={assignmentType === "team"}
|
checked={assignmentType === 'team'}
|
||||||
onChange={(e) =>
|
onChange={(e) => setAssignmentType(e.target.value as 'person' | 'team')}
|
||||||
setAssignmentType(e.target.value as "person" | "team")
|
|
||||||
}
|
|
||||||
className="sr-only"
|
className="sr-only"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={`p-2 border-2 rounded-lg cursor-pointer transition-colors ${
|
className={`p-2 border-2 rounded-lg cursor-pointer transition-colors ${
|
||||||
assignmentType === "team"
|
assignmentType === 'team'
|
||||||
? "border-blue-500 bg-blue-50"
|
? 'border-blue-500 bg-blue-50'
|
||||||
: "border-gray-300 bg-white hover:bg-gray-50"
|
: 'border-gray-300 bg-white hover:bg-gray-50'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<FaUsers
|
<FaUsers
|
||||||
className={`w-5 h-5 ${
|
className={`w-5 h-5 ${
|
||||||
assignmentType === "team"
|
assignmentType === 'team' ? 'text-blue-600' : 'text-gray-400'
|
||||||
? "text-blue-600"
|
|
||||||
: "text-gray-400"
|
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-medium text-sm text-gray-900">
|
<h4 className="font-medium text-sm text-gray-900">Ekibe Ata</h4>
|
||||||
Ekibe Ata
|
<p className="text-xs text-gray-600">Bakım ekibine atama yap</p>
|
||||||
</h4>
|
|
||||||
<p className="text-xs text-gray-600">
|
|
||||||
Bakım ekibine atama yap
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -220,7 +184,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Person Assignment */}
|
{/* Person Assignment */}
|
||||||
{assignmentType === "person" && (
|
{assignmentType === 'person' && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
<FaUser className="w-4 h-4 inline mr-2" />
|
<FaUser className="w-4 h-4 inline mr-2" />
|
||||||
|
|
@ -228,11 +192,9 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={formData.assignedTo}
|
value={formData.assignedTo}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, assignedTo: e.target.value })}
|
||||||
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 ${
|
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>
|
<option value="">Kişi Seçin</option>
|
||||||
|
|
@ -249,7 +211,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Team Assignment */}
|
{/* Team Assignment */}
|
||||||
{assignmentType === "team" && (
|
{assignmentType === 'team' && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
<FaUsers className="w-4 h-4 inline mr-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 ${
|
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
|
errors.maintenanceTeamId ? 'border-red-500' : 'border-gray-300'
|
||||||
? "border-red-500"
|
|
||||||
: "border-gray-300"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<option value="">Ekip Seçin</option>
|
<option value="">Ekip Seçin</option>
|
||||||
|
|
@ -277,31 +237,23 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.maintenanceTeamId && (
|
{errors.maintenanceTeamId && (
|
||||||
<p className="mt-1 text-xs text-red-600">
|
<p className="mt-1 text-xs text-red-600">{errors.maintenanceTeamId}</p>
|
||||||
{errors.maintenanceTeamId}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Team Details */}
|
{/* Team Details */}
|
||||||
{assignmentType === "team" && getSelectedTeam() && (
|
{assignmentType === 'team' && getSelectedTeam() && (
|
||||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-2">
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-2">
|
||||||
<h5 className="font-medium text-sm text-blue-900 mb-2">
|
<h5 className="font-medium text-sm text-blue-900 mb-2">Ekip Detayları</h5>
|
||||||
Ekip Detayları
|
|
||||||
</h5>
|
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<span className="text-blue-700">Ekip Adı:</span>
|
<span className="text-blue-700">Ekip Adı:</span>
|
||||||
<span className="ml-2 font-medium">
|
<span className="ml-2 font-medium">{getSelectedTeam()?.name}</span>
|
||||||
{getSelectedTeam()?.name}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-blue-700">Üye Sayısı:</span>
|
<span className="text-blue-700">Üye Sayısı:</span>
|
||||||
<span className="ml-2 font-medium">
|
<span className="ml-2 font-medium">{getSelectedTeam()?.members.length}</span>
|
||||||
{getSelectedTeam()?.members.length}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-blue-700">Uzmanlık Alanları:</span>
|
<span className="text-blue-700">Uzmanlık Alanları:</span>
|
||||||
|
|
@ -321,17 +273,13 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Employee Details */}
|
{/* Employee Details */}
|
||||||
{assignmentType === "person" && getSelectedEmployee() && (
|
{assignmentType === 'person' && getSelectedEmployee() && (
|
||||||
<div className="bg-green-50 border border-green-200 rounded-lg p-2">
|
<div className="bg-green-50 border border-green-200 rounded-lg p-2">
|
||||||
<h5 className="font-medium text-sm text-green-900 mb-2">
|
<h5 className="font-medium text-sm text-green-900 mb-2">Çalışan Detayları</h5>
|
||||||
Çalışan Detayları
|
|
||||||
</h5>
|
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<span className="text-green-700">Ad Soyad:</span>
|
<span className="text-green-700">Ad Soyad:</span>
|
||||||
<span className="ml-2 font-medium">
|
<span className="ml-2 font-medium">{getSelectedEmployee()?.fullName}</span>
|
||||||
{getSelectedEmployee()?.fullName}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-green-700">Pozisyon:</span>
|
<span className="text-green-700">Pozisyon:</span>
|
||||||
|
|
@ -351,14 +299,10 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
|
|
||||||
{/* Notes */}
|
{/* Notes */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Atama Notları</label>
|
||||||
Atama Notları
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={formData.notes}
|
value={formData.notes}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
|
||||||
setFormData({ ...formData, notes: e.target.value })
|
|
||||||
}
|
|
||||||
rows={2}
|
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"
|
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..."
|
placeholder="Atama ile ilgili notlar..."
|
||||||
|
|
@ -367,9 +311,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
|
|
||||||
{/* Assignment Preview */}
|
{/* Assignment Preview */}
|
||||||
<div className="bg-gray-50 rounded-lg p-2">
|
<div className="bg-gray-50 rounded-lg p-2">
|
||||||
<h5 className="font-medium text-sm text-gray-900 mb-2">
|
<h5 className="font-medium text-sm text-gray-900 mb-2">Atama Önizlemesi</h5>
|
||||||
Atama Önizlemesi
|
|
||||||
</h5>
|
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">İş Emri Sayısı:</span>
|
<span className="text-gray-600">İş Emri Sayısı:</span>
|
||||||
|
|
@ -378,19 +320,17 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">Atama Türü:</span>
|
<span className="text-gray-600">Atama Türü:</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{assignmentType === "person" ? "Kişiye Atama" : "Ekip Atama"}
|
{assignmentType === 'person' ? 'Kişiye Atama' : 'Ekip Atama'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">
|
<span className="text-gray-600">
|
||||||
{assignmentType === "person"
|
{assignmentType === 'person' ? 'Atanan Kişi:' : 'Atanan Ekip:'}
|
||||||
? "Atanan Kişi:"
|
|
||||||
: "Atanan Ekip:"}
|
|
||||||
</span>
|
</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{assignmentType === "person"
|
{assignmentType === 'person'
|
||||||
? formData.assignedTo || "Seçilmedi"
|
? formData.assignedTo || 'Seçilmedi'
|
||||||
: getSelectedTeam()?.name || "Seçilmedi"}
|
: getSelectedTeam()?.name || 'Seçilmedi'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -416,7 +356,7 @@ const AssignWorkOrderModal: React.FC<AssignWorkOrderModalProps> = ({
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default AssignWorkOrderModal;
|
export default AssignWorkOrderModal
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,43 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaExclamationTriangle } from "react-icons/fa";
|
import { FaTimes, FaSave, FaExclamationTriangle } from 'react-icons/fa'
|
||||||
import { PmFaultNotification, NotificationStatusEnum } from "../../../types/pm";
|
import { PmFaultNotification, NotificationStatusEnum } from '../../../types/pm'
|
||||||
import {
|
import { getNotificationStatusColor, getNotificationStatusText } from '../../../utils/erp'
|
||||||
getNotificationStatusColor,
|
|
||||||
getNotificationStatusText,
|
|
||||||
} from "../../../utils/erp";
|
|
||||||
|
|
||||||
interface ChangeNotificationStatusModalProps {
|
interface ChangeNotificationStatusModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (statusChange: {
|
onSave: (statusChange: {
|
||||||
notificationIds: string[];
|
notificationIds: string[]
|
||||||
status: NotificationStatusEnum;
|
status: NotificationStatusEnum
|
||||||
notes?: string;
|
notes?: string
|
||||||
}) => void;
|
}) => void
|
||||||
notifications: PmFaultNotification[];
|
notifications: PmFaultNotification[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChangeNotificationStatusModal: React.FC<
|
const ChangeNotificationStatusModal: React.FC<ChangeNotificationStatusModalProps> = ({
|
||||||
ChangeNotificationStatusModalProps
|
isOpen,
|
||||||
> = ({ isOpen, onClose, onSave, notifications }) => {
|
onClose,
|
||||||
|
onSave,
|
||||||
|
notifications,
|
||||||
|
}) => {
|
||||||
const [newStatus, setNewStatus] = useState<NotificationStatusEnum>(
|
const [newStatus, setNewStatus] = useState<NotificationStatusEnum>(
|
||||||
NotificationStatusEnum.InProgress
|
NotificationStatusEnum.InProgress,
|
||||||
);
|
)
|
||||||
const [notes, setNotes] = useState("");
|
const [notes, setNotes] = useState('')
|
||||||
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 validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (!newStatus) {
|
if (!newStatus) {
|
||||||
newErrors.status = "Durum seçimi gerekli";
|
newErrors.status = 'Durum seçimi gerekli'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
|
|
@ -45,42 +45,40 @@ const ChangeNotificationStatusModal: React.FC<
|
||||||
notificationIds: notifications.map((n) => n.id),
|
notificationIds: notifications.map((n) => n.id),
|
||||||
status: newStatus,
|
status: newStatus,
|
||||||
notes: notes.trim() || undefined,
|
notes: notes.trim() || undefined,
|
||||||
};
|
}
|
||||||
|
|
||||||
onSave(statusChangeData);
|
onSave(statusChangeData)
|
||||||
onClose();
|
onClose()
|
||||||
|
|
||||||
// Reset form
|
// Reset form
|
||||||
setNewStatus(NotificationStatusEnum.InProgress);
|
setNewStatus(NotificationStatusEnum.InProgress)
|
||||||
setNotes("");
|
setNotes('')
|
||||||
setErrors({});
|
setErrors({})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const getCurrentStatuses = () => {
|
const getCurrentStatuses = () => {
|
||||||
const statusCounts = notifications.reduce((acc, notification) => {
|
const statusCounts = notifications.reduce(
|
||||||
acc[notification.status] = (acc[notification.status] || 0) + 1;
|
(acc, notification) => {
|
||||||
return acc;
|
acc[notification.status] = (acc[notification.status] || 0) + 1
|
||||||
}, {} as Record<NotificationStatusEnum, number>);
|
return acc
|
||||||
|
},
|
||||||
|
{} as Record<NotificationStatusEnum, number>,
|
||||||
|
)
|
||||||
|
|
||||||
return Object.entries(statusCounts).map(([status, count]) => ({
|
return Object.entries(statusCounts).map(([status, count]) => ({
|
||||||
status: status as NotificationStatusEnum,
|
status: status as NotificationStatusEnum,
|
||||||
count,
|
count,
|
||||||
}));
|
}))
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<div className="bg-white rounded-lg w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">
|
<h2 className="text-lg font-semibold text-gray-900">Durum Değiştir</h2>
|
||||||
Durum Değiştir
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||||
</h2>
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-6 h-6" />
|
<FaTimes className="w-6 h-6" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -95,20 +93,18 @@ const ChangeNotificationStatusModal: React.FC<
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{notifications.map((notification) => (
|
{notifications.map((notification) => (
|
||||||
<div key={notification.id} className="text-sm text-blue-700">
|
<div key={notification.id} className="text-sm text-blue-700">
|
||||||
<span className="font-medium">
|
<span className="font-medium">{notification.notificationCode}</span>
|
||||||
{notification.notificationCode}
|
{' - '}
|
||||||
</span>
|
|
||||||
{" - "}
|
|
||||||
<span>{notification.title}</span>
|
<span>{notification.title}</span>
|
||||||
{" ("}
|
{' ('}
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-full text-xs ${getNotificationStatusColor(
|
className={`px-2 py-1 rounded-full text-xs ${getNotificationStatusColor(
|
||||||
notification.status
|
notification.status,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getNotificationStatusText(notification.status)}
|
{getNotificationStatusText(notification.status)}
|
||||||
</span>
|
</span>
|
||||||
{")"}
|
{')'}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -116,15 +112,13 @@ const ChangeNotificationStatusModal: React.FC<
|
||||||
|
|
||||||
{/* Current Status Summary */}
|
{/* Current Status Summary */}
|
||||||
<div className="bg-gray-50 border border-gray-200 rounded-lg p-3">
|
<div className="bg-gray-50 border border-gray-200 rounded-lg p-3">
|
||||||
<h4 className="text-xs font-medium text-gray-800 mb-2">
|
<h4 className="text-xs font-medium text-gray-800 mb-2">Mevcut Durumlar</h4>
|
||||||
Mevcut Durumlar
|
|
||||||
</h4>
|
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{getCurrentStatuses().map(({ status, count }) => (
|
{getCurrentStatuses().map(({ status, count }) => (
|
||||||
<span
|
<span
|
||||||
key={status}
|
key={status}
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getNotificationStatusColor(
|
className={`px-2 py-1 rounded-full text-xs font-medium ${getNotificationStatusColor(
|
||||||
status
|
status,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getNotificationStatusText(status)}: {count}
|
{getNotificationStatusText(status)}: {count}
|
||||||
|
|
@ -135,32 +129,22 @@ const ChangeNotificationStatusModal: React.FC<
|
||||||
|
|
||||||
{/* New Status Selection */}
|
{/* New Status Selection */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Yeni Durum *</label>
|
||||||
Yeni Durum *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={newStatus}
|
value={newStatus}
|
||||||
onChange={(e) =>
|
onChange={(e) => setNewStatus(e.target.value as NotificationStatusEnum)}
|
||||||
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 ${
|
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.Open}>Açık</option>
|
||||||
<option value={NotificationStatusEnum.Assigned}>Atandı</option>
|
<option value={NotificationStatusEnum.Assigned}>Atandı</option>
|
||||||
<option value={NotificationStatusEnum.InProgress}>
|
<option value={NotificationStatusEnum.InProgress}>Devam Ediyor</option>
|
||||||
Devam Ediyor
|
|
||||||
</option>
|
|
||||||
<option value={NotificationStatusEnum.Resolved}>Çözüldü</option>
|
<option value={NotificationStatusEnum.Resolved}>Çözüldü</option>
|
||||||
<option value={NotificationStatusEnum.Closed}>Kapatıldı</option>
|
<option value={NotificationStatusEnum.Closed}>Kapatıldı</option>
|
||||||
<option value={NotificationStatusEnum.Rejected}>
|
<option value={NotificationStatusEnum.Rejected}>Reddedildi</option>
|
||||||
Reddedildi
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
{errors.status && (
|
{errors.status && <p className="text-red-500 text-sm mt-1">{errors.status}</p>}
|
||||||
<p className="text-red-500 text-sm mt-1">{errors.status}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Status Change Preview */}
|
{/* Status Change Preview */}
|
||||||
|
|
@ -168,18 +152,16 @@ const ChangeNotificationStatusModal: React.FC<
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
<FaExclamationTriangle className="w-5 h-5 text-green-600 mt-0.5" />
|
<FaExclamationTriangle className="w-5 h-5 text-green-600 mt-0.5" />
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h4 className="text-sm font-medium text-green-800 mb-1">
|
<h4 className="text-sm font-medium text-green-800 mb-1">Durum Değişikliği</h4>
|
||||||
Durum Değişikliği
|
|
||||||
</h4>
|
|
||||||
<p className="text-sm text-green-700">
|
<p className="text-sm text-green-700">
|
||||||
<strong>{notifications.length}</strong> arıza bildirimi{" "}
|
<strong>{notifications.length}</strong> arıza bildirimi{' '}
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-full text-xs ${getNotificationStatusColor(
|
className={`px-2 py-1 rounded-full text-xs ${getNotificationStatusColor(
|
||||||
newStatus
|
newStatus,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getNotificationStatusText(newStatus)}
|
{getNotificationStatusText(newStatus)}
|
||||||
</span>{" "}
|
</span>{' '}
|
||||||
durumuna geçirilecek.
|
durumuna geçirilecek.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -207,12 +189,10 @@ const ChangeNotificationStatusModal: React.FC<
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h4 className="text-sm font-medium text-yellow-800 mb-1">
|
<h4 className="text-sm font-medium text-yellow-800 mb-1">Dikkat!</h4>
|
||||||
Dikkat!
|
|
||||||
</h4>
|
|
||||||
<p className="text-sm text-yellow-700">
|
<p className="text-sm text-yellow-700">
|
||||||
Bu durum değişikliği kalıcıdır ve bildirimlerin aktif
|
Bu durum değişikliği kalıcıdır ve bildirimlerin aktif süreçlerden çıkarılmasına
|
||||||
süreçlerden çıkarılmasına neden olabilir.
|
neden olabilir.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -238,7 +218,7 @@ const ChangeNotificationStatusModal: React.FC<
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default ChangeNotificationStatusModal;
|
export default ChangeNotificationStatusModal
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,23 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import {
|
import { FaTimes, FaExchangeAlt, FaCheck, FaPause, FaTimesCircle } from 'react-icons/fa'
|
||||||
FaTimes,
|
import { PmMaintenanceWorkOrder, WorkOrderStatusEnum } from '../../../types/pm'
|
||||||
FaExchangeAlt,
|
|
||||||
FaCheck,
|
|
||||||
FaPause,
|
|
||||||
FaTimesCircle,
|
|
||||||
} from "react-icons/fa";
|
|
||||||
import { PmMaintenanceWorkOrder, WorkOrderStatusEnum } from "../../../types/pm";
|
|
||||||
import {
|
import {
|
||||||
getWorkOrderStatusColor,
|
getWorkOrderStatusColor,
|
||||||
getWorkOrderStatusText,
|
getWorkOrderStatusText,
|
||||||
getWorkOrderStatusIcon,
|
getWorkOrderStatusIcon,
|
||||||
} from "../../../utils/erp";
|
} from '../../../utils/erp'
|
||||||
|
|
||||||
interface ChangeWorkOrderStatusModalProps {
|
interface ChangeWorkOrderStatusModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onStatusChange: (
|
onStatusChange: (workOrders: PmMaintenanceWorkOrder[], statusData: StatusChangeData) => void
|
||||||
workOrders: PmMaintenanceWorkOrder[],
|
workOrders: PmMaintenanceWorkOrder[]
|
||||||
statusData: StatusChangeData
|
|
||||||
) => void;
|
|
||||||
workOrders: PmMaintenanceWorkOrder[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StatusChangeData {
|
interface StatusChangeData {
|
||||||
newStatus: WorkOrderStatusEnum;
|
newStatus: WorkOrderStatusEnum
|
||||||
notes?: string;
|
notes?: string
|
||||||
completionNotes?: string;
|
completionNotes?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
|
|
@ -37,47 +28,40 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
newStatus: WorkOrderStatusEnum.InProgress,
|
newStatus: WorkOrderStatusEnum.InProgress,
|
||||||
notes: "",
|
notes: '',
|
||||||
completionNotes: "",
|
completionNotes: '',
|
||||||
});
|
})
|
||||||
|
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (
|
if (formData.newStatus === WorkOrderStatusEnum.Completed && !formData.completionNotes.trim()) {
|
||||||
formData.newStatus === WorkOrderStatusEnum.Completed &&
|
newErrors.completionNotes = 'Tamamlanan iş emirleri için tamamlanma notları zorunludur'
|
||||||
!formData.completionNotes.trim()
|
|
||||||
) {
|
|
||||||
newErrors.completionNotes =
|
|
||||||
"Tamamlanan iş emirleri için tamamlanma notları zorunludur";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (formData.newStatus === WorkOrderStatusEnum.Cancelled && !formData.notes.trim()) {
|
||||||
formData.newStatus === WorkOrderStatusEnum.Cancelled &&
|
newErrors.notes = 'İptal edilen iş emirleri için iptal nedeni zorunludur'
|
||||||
!formData.notes.trim()
|
|
||||||
) {
|
|
||||||
newErrors.notes = "İptal edilen iş emirleri için iptal nedeni zorunludur";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
if (!validateForm()) return;
|
if (!validateForm()) return
|
||||||
|
|
||||||
const statusData: StatusChangeData = {
|
const statusData: StatusChangeData = {
|
||||||
newStatus: formData.newStatus,
|
newStatus: formData.newStatus,
|
||||||
notes: formData.notes || undefined,
|
notes: formData.notes || undefined,
|
||||||
completionNotes: formData.completionNotes || undefined,
|
completionNotes: formData.completionNotes || undefined,
|
||||||
};
|
}
|
||||||
|
|
||||||
onStatusChange(workOrders, statusData);
|
onStatusChange(workOrders, statusData)
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const getStatusCounts = () => {
|
const getStatusCounts = () => {
|
||||||
const counts: Record<WorkOrderStatusEnum, number> = {
|
const counts: Record<WorkOrderStatusEnum, number> = {
|
||||||
|
|
@ -88,43 +72,42 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
[WorkOrderStatusEnum.OnHold]: 0,
|
[WorkOrderStatusEnum.OnHold]: 0,
|
||||||
[WorkOrderStatusEnum.Completed]: 0,
|
[WorkOrderStatusEnum.Completed]: 0,
|
||||||
[WorkOrderStatusEnum.Cancelled]: 0,
|
[WorkOrderStatusEnum.Cancelled]: 0,
|
||||||
};
|
}
|
||||||
|
|
||||||
workOrders.forEach((wo) => {
|
workOrders.forEach((wo) => {
|
||||||
counts[wo.status]++;
|
counts[wo.status]++
|
||||||
});
|
})
|
||||||
|
|
||||||
return counts;
|
return counts
|
||||||
};
|
}
|
||||||
|
|
||||||
const canChangeToStatus = (status: WorkOrderStatusEnum) => {
|
const canChangeToStatus = (status: WorkOrderStatusEnum) => {
|
||||||
// İş emirlerinin mevcut durumlarına göre hangi durumlara geçebileceğini kontrol et
|
// İş emirlerinin mevcut durumlarına göre hangi durumlara geçebileceğini kontrol et
|
||||||
return workOrders.some((wo) => {
|
return workOrders.some((wo) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case WorkOrderStatusEnum.Planned:
|
case WorkOrderStatusEnum.Planned:
|
||||||
return wo.status === WorkOrderStatusEnum.Created;
|
return wo.status === WorkOrderStatusEnum.Created
|
||||||
case WorkOrderStatusEnum.Released:
|
case WorkOrderStatusEnum.Released:
|
||||||
return wo.status === WorkOrderStatusEnum.Planned;
|
return wo.status === WorkOrderStatusEnum.Planned
|
||||||
case WorkOrderStatusEnum.InProgress:
|
case WorkOrderStatusEnum.InProgress:
|
||||||
return (
|
return (
|
||||||
wo.status === WorkOrderStatusEnum.Released ||
|
wo.status === WorkOrderStatusEnum.Released || wo.status === WorkOrderStatusEnum.OnHold
|
||||||
wo.status === WorkOrderStatusEnum.OnHold
|
)
|
||||||
);
|
|
||||||
case WorkOrderStatusEnum.OnHold:
|
case WorkOrderStatusEnum.OnHold:
|
||||||
return wo.status === WorkOrderStatusEnum.InProgress;
|
return wo.status === WorkOrderStatusEnum.InProgress
|
||||||
case WorkOrderStatusEnum.Completed:
|
case WorkOrderStatusEnum.Completed:
|
||||||
return wo.status === WorkOrderStatusEnum.InProgress;
|
return wo.status === WorkOrderStatusEnum.InProgress
|
||||||
case WorkOrderStatusEnum.Cancelled:
|
case WorkOrderStatusEnum.Cancelled:
|
||||||
return wo.status !== WorkOrderStatusEnum.Completed;
|
return wo.status !== WorkOrderStatusEnum.Completed
|
||||||
default:
|
default:
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
const statusCounts = getStatusCounts();
|
const statusCounts = getStatusCounts()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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" />
|
<FaExchangeAlt className="w-5 h-5 mr-2 text-orange-600" />
|
||||||
İş Emri Durumunu Değiştir
|
İş Emri Durumunu Değiştir
|
||||||
</h3>
|
</h3>
|
||||||
<button
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-6 h-6" />
|
<FaTimes className="w-6 h-6" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -150,46 +130,36 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
|
|
||||||
{/* Current Status Distribution */}
|
{/* Current Status Distribution */}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h5 className="text-sm font-medium text-gray-700 mb-2">
|
<h5 className="text-sm font-medium text-gray-700 mb-2">Mevcut Durum Dağılımı</h5>
|
||||||
Mevcut Durum Dağılımı
|
|
||||||
</h5>
|
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{Object.entries(statusCounts).map(([status, count]) => {
|
{Object.entries(statusCounts).map(([status, count]) => {
|
||||||
if (count === 0) return null;
|
if (count === 0) return null
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
key={status}
|
key={status}
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
className={`px-2 py-1 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
||||||
status as WorkOrderStatusEnum
|
status as WorkOrderStatusEnum,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getWorkOrderStatusText(status as WorkOrderStatusEnum)}:{" "}
|
{getWorkOrderStatusText(status as WorkOrderStatusEnum)}: {count}
|
||||||
{count}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="max-h-40 overflow-y-auto border border-gray-200 rounded-lg">
|
<div className="max-h-40 overflow-y-auto border border-gray-200 rounded-lg">
|
||||||
{workOrders.map((workOrder) => (
|
{workOrders.map((workOrder) => (
|
||||||
<div
|
<div key={workOrder.id} className="p-2 border-b border-gray-100 last:border-b-0">
|
||||||
key={workOrder.id}
|
|
||||||
className="p-2 border-b border-gray-100 last:border-b-0"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-gray-900">
|
<p className="font-medium text-gray-900">{workOrder.workOrderNumber}</p>
|
||||||
{workOrder.workOrderNumber}
|
<p className="text-sm text-gray-600 truncate">{workOrder.description}</p>
|
||||||
</p>
|
|
||||||
<p className="text-sm text-gray-600 truncate">
|
|
||||||
{workOrder.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
className={`px-2 py-1 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
||||||
workOrder.status
|
workOrder.status,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getWorkOrderStatusText(workOrder.status)}
|
{getWorkOrderStatusText(workOrder.status)}
|
||||||
|
|
@ -204,9 +174,7 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
{/* New Status Selection */}
|
{/* New Status Selection */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-3">
|
<label className="block text-sm font-medium text-gray-700 mb-3">Yeni Durum *</label>
|
||||||
Yeni Durum *
|
|
||||||
</label>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||||
{[
|
{[
|
||||||
WorkOrderStatusEnum.Planned,
|
WorkOrderStatusEnum.Planned,
|
||||||
|
|
@ -234,19 +202,17 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
<div
|
<div
|
||||||
className={`p-2 border-2 rounded-lg cursor-pointer transition-colors ${
|
className={`p-2 border-2 rounded-lg cursor-pointer transition-colors ${
|
||||||
formData.newStatus === status
|
formData.newStatus === status
|
||||||
? "border-blue-500 bg-blue-50"
|
? 'border-blue-500 bg-blue-50'
|
||||||
: canChangeToStatus(status)
|
: canChangeToStatus(status)
|
||||||
? "border-gray-300 bg-white hover:bg-gray-50"
|
? 'border-gray-300 bg-white hover:bg-gray-50'
|
||||||
: "border-gray-200 bg-gray-50 cursor-not-allowed"
|
: 'border-gray-200 bg-gray-50 cursor-not-allowed'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
{getWorkOrderStatusIcon(status)}
|
{getWorkOrderStatusIcon(status)}
|
||||||
<span
|
<span
|
||||||
className={`text-sm font-medium ${
|
className={`text-sm font-medium ${
|
||||||
canChangeToStatus(status)
|
canChangeToStatus(status) ? 'text-gray-900' : 'text-gray-400'
|
||||||
? "text-gray-900"
|
|
||||||
: "text-gray-400"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{getWorkOrderStatusText(status)}
|
{getWorkOrderStatusText(status)}
|
||||||
|
|
@ -266,19 +232,15 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={formData.completionNotes}
|
value={formData.completionNotes}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, completionNotes: e.target.value })}
|
||||||
setFormData({ ...formData, completionNotes: e.target.value })
|
|
||||||
}
|
|
||||||
rows={3}
|
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 ${
|
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..."
|
placeholder="İş emirlerinin nasıl tamamlandığına dair detaylar..."
|
||||||
/>
|
/>
|
||||||
{errors.completionNotes && (
|
{errors.completionNotes && (
|
||||||
<p className="mt-1 text-sm text-red-600">
|
<p className="mt-1 text-sm text-red-600">{errors.completionNotes}</p>
|
||||||
{errors.completionNotes}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -287,27 +249,23 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
{formData.newStatus === WorkOrderStatusEnum.Cancelled
|
{formData.newStatus === WorkOrderStatusEnum.Cancelled
|
||||||
? "İptal Nedeni *"
|
? 'İptal Nedeni *'
|
||||||
: "Durum Değişikliği Notları"}
|
: 'Durum Değişikliği Notları'}
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={formData.notes}
|
value={formData.notes}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
|
||||||
setFormData({ ...formData, notes: e.target.value })
|
|
||||||
}
|
|
||||||
rows={3}
|
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 ${
|
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={
|
placeholder={
|
||||||
formData.newStatus === WorkOrderStatusEnum.Cancelled
|
formData.newStatus === WorkOrderStatusEnum.Cancelled
|
||||||
? "İptal nedenini açıklayın..."
|
? 'İptal nedenini açıklayın...'
|
||||||
: "Durum değişikliği ile ilgili notlar..."
|
: 'Durum değişikliği ile ilgili notlar...'
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{errors.notes && (
|
{errors.notes && <p className="mt-1 text-sm text-red-600">{errors.notes}</p>}
|
||||||
<p className="mt-1 text-sm text-red-600">{errors.notes}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Status Change Warnings */}
|
{/* Status Change Warnings */}
|
||||||
|
|
@ -318,8 +276,8 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<h5 className="font-medium text-red-800 mb-1">Uyarı</h5>
|
<h5 className="font-medium text-red-800 mb-1">Uyarı</h5>
|
||||||
<p className="text-red-700 text-sm">
|
<p className="text-red-700 text-sm">
|
||||||
İş emirleri iptal edilecek. Bu işlem geri alınamaz ve tüm
|
İş emirleri iptal edilecek. Bu işlem geri alınamaz ve tüm aktiviteler
|
||||||
aktiviteler durdurulacak.
|
durdurulacak.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -333,8 +291,8 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<h5 className="font-medium text-green-800 mb-1">Bilgi</h5>
|
<h5 className="font-medium text-green-800 mb-1">Bilgi</h5>
|
||||||
<p className="text-green-700 text-sm">
|
<p className="text-green-700 text-sm">
|
||||||
İş emirleri tamamlandı olarak işaretlenecek. Gerçek bitiş
|
İş emirleri tamamlandı olarak işaretlenecek. Gerçek bitiş tarihi otomatik olarak
|
||||||
tarihi otomatik olarak şu an olarak ayarlanacak.
|
şu an olarak ayarlanacak.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -348,8 +306,8 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<h5 className="font-medium text-yellow-800 mb-1">Bilgi</h5>
|
<h5 className="font-medium text-yellow-800 mb-1">Bilgi</h5>
|
||||||
<p className="text-yellow-700 text-sm">
|
<p className="text-yellow-700 text-sm">
|
||||||
İş emirleri beklemede durumuna alınacak. Aktiviteler
|
İş emirleri beklemede durumuna alınacak. Aktiviteler durdurulacak ancak daha
|
||||||
durdurulacak ancak daha sonra devam ettirilebilir.
|
sonra devam ettirilebilir.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -358,9 +316,7 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
|
|
||||||
{/* Change Preview */}
|
{/* Change Preview */}
|
||||||
<div className="bg-gray-50 rounded-lg p-3">
|
<div className="bg-gray-50 rounded-lg p-3">
|
||||||
<h5 className="font-medium text-sm text-gray-900 mb-2">
|
<h5 className="font-medium text-sm text-gray-900 mb-2">Durum Değişikliği Önizlemesi</h5>
|
||||||
Durum Değişikliği Önizlemesi
|
|
||||||
</h5>
|
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">Etkilenen İş Emri Sayısı:</span>
|
<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="text-gray-600">Yeni Durum:</span>
|
||||||
<span
|
<span
|
||||||
className={`font-medium px-2 py-1 rounded text-xs ${getWorkOrderStatusColor(
|
className={`font-medium px-2 py-1 rounded text-xs ${getWorkOrderStatusColor(
|
||||||
formData.newStatus
|
formData.newStatus,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getWorkOrderStatusText(formData.newStatus)}
|
{getWorkOrderStatusText(formData.newStatus)}
|
||||||
|
|
@ -379,10 +335,10 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">Değişiklik Tarihi:</span>
|
<span className="text-gray-600">Değişiklik Tarihi:</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{new Date().toLocaleDateString("tr-TR")}{" "}
|
{new Date().toLocaleDateString('tr-TR')}{' '}
|
||||||
{new Date().toLocaleTimeString("tr-TR", {
|
{new Date().toLocaleTimeString('tr-TR', {
|
||||||
hour: "2-digit",
|
hour: '2-digit',
|
||||||
minute: "2-digit",
|
minute: '2-digit',
|
||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -402,10 +358,10 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
type="submit"
|
type="submit"
|
||||||
className={`px-4 py-1.5 text-sm text-white rounded-lg flex items-center space-x-2 ${
|
className={`px-4 py-1.5 text-sm text-white rounded-lg flex items-center space-x-2 ${
|
||||||
formData.newStatus === WorkOrderStatusEnum.Cancelled
|
formData.newStatus === WorkOrderStatusEnum.Cancelled
|
||||||
? "bg-red-600 hover:bg-red-700"
|
? 'bg-red-600 hover:bg-red-700'
|
||||||
: formData.newStatus === WorkOrderStatusEnum.Completed
|
: formData.newStatus === WorkOrderStatusEnum.Completed
|
||||||
? "bg-green-600 hover:bg-green-700"
|
? 'bg-green-600 hover:bg-green-700'
|
||||||
: "bg-orange-600 hover:bg-orange-700"
|
: 'bg-orange-600 hover:bg-orange-700'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<FaExchangeAlt className="w-4 h-4" />
|
<FaExchangeAlt className="w-4 h-4" />
|
||||||
|
|
@ -415,7 +371,7 @@ const ChangeWorkOrderStatusModal: React.FC<ChangeWorkOrderStatusModalProps> = ({
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default ChangeWorkOrderStatusModal;
|
export default ChangeWorkOrderStatusModal
|
||||||
|
|
|
||||||
|
|
@ -1,73 +1,74 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaExclamationTriangle } from "react-icons/fa";
|
import { FaTimes, FaSave, FaExclamationTriangle } from 'react-icons/fa'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||||
import { PmFaultNotification, WorkOrderTypeEnum } from "../../../types/pm";
|
import { PmFaultNotification, WorkOrderTypeEnum } from '../../../types/pm'
|
||||||
import { MrpWorkOrder } from "../../../types/mrp";
|
import { MrpWorkOrder } from '../../../types/mrp'
|
||||||
import { PriorityEnum } from "../../../types/common";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
|
|
||||||
interface CreateWorkOrderFromNotificationModalProps {
|
interface CreateWorkOrderFromNotificationModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (workOrderData: MrpWorkOrder) => void;
|
onSave: (workOrderData: MrpWorkOrder) => void
|
||||||
notifications: PmFaultNotification[];
|
notifications: PmFaultNotification[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const CreateWorkOrderFromNotificationModal: React.FC<
|
const CreateWorkOrderFromNotificationModal: React.FC<CreateWorkOrderFromNotificationModalProps> = ({
|
||||||
CreateWorkOrderFromNotificationModalProps
|
isOpen,
|
||||||
> = ({ isOpen, onClose, onSave, notifications }) => {
|
onClose,
|
||||||
|
onSave,
|
||||||
|
notifications,
|
||||||
|
}) => {
|
||||||
const [workOrderData, setWorkOrderData] = useState({
|
const [workOrderData, setWorkOrderData] = useState({
|
||||||
workOrderNumber: `WO-${new Date().getFullYear()}-${String(Date.now()).slice(
|
workOrderNumber: `WO-${new Date().getFullYear()}-${String(Date.now()).slice(-3)}`,
|
||||||
-3
|
|
||||||
)}`,
|
|
||||||
orderType: WorkOrderTypeEnum.Corrective,
|
orderType: WorkOrderTypeEnum.Corrective,
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
description: "",
|
description: '',
|
||||||
assignedTo: "",
|
assignedTo: '',
|
||||||
maintenanceTeamId: "",
|
maintenanceTeamId: '',
|
||||||
scheduledStart: "",
|
scheduledStart: '',
|
||||||
scheduledEnd: "",
|
scheduledEnd: '',
|
||||||
estimatedCost: 0,
|
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 validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (!workOrderData.description.trim()) {
|
if (!workOrderData.description.trim()) {
|
||||||
newErrors.description = "Açıklama gerekli";
|
newErrors.description = 'Açıklama gerekli'
|
||||||
}
|
}
|
||||||
if (!workOrderData.assignedTo && !workOrderData.maintenanceTeamId) {
|
if (!workOrderData.assignedTo && !workOrderData.maintenanceTeamId) {
|
||||||
newErrors.assignment = "Kişi veya ekip ataması gerekli";
|
newErrors.assignment = 'Kişi veya ekip ataması gerekli'
|
||||||
}
|
}
|
||||||
if (!workOrderData.scheduledStart) {
|
if (!workOrderData.scheduledStart) {
|
||||||
newErrors.scheduledStart = "Başlangıç tarihi gerekli";
|
newErrors.scheduledStart = 'Başlangıç tarihi gerekli'
|
||||||
}
|
}
|
||||||
if (!workOrderData.scheduledEnd) {
|
if (!workOrderData.scheduledEnd) {
|
||||||
newErrors.scheduledEnd = "Bitiş tarihi gerekli";
|
newErrors.scheduledEnd = 'Bitiş tarihi gerekli'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleInputChange = (field: string, value: string | number) => {
|
const handleInputChange = (field: string, value: string | number) => {
|
||||||
setWorkOrderData((prev) => ({
|
setWorkOrderData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
}));
|
}))
|
||||||
// Clear error when user starts typing
|
// Clear error when user starts typing
|
||||||
if (errors[field]) {
|
if (errors[field]) {
|
||||||
setErrors((prev) => ({
|
setErrors((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: "",
|
[field]: '',
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
|
|
@ -79,42 +80,35 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
||||||
reportedBy: notifications[0].reportedBy,
|
reportedBy: notifications[0].reportedBy,
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
|
|
||||||
onSave(workOrderToSave);
|
onSave(workOrderToSave)
|
||||||
onClose();
|
onClose()
|
||||||
|
|
||||||
// Reset form
|
// Reset form
|
||||||
setWorkOrderData({
|
setWorkOrderData({
|
||||||
workOrderNumber: `WO-${new Date().getFullYear()}-${String(
|
workOrderNumber: `WO-${new Date().getFullYear()}-${String(Date.now()).slice(-3)}`,
|
||||||
Date.now()
|
|
||||||
).slice(-3)}`,
|
|
||||||
orderType: WorkOrderTypeEnum.Corrective,
|
orderType: WorkOrderTypeEnum.Corrective,
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
description: "",
|
description: '',
|
||||||
assignedTo: "",
|
assignedTo: '',
|
||||||
maintenanceTeamId: "",
|
maintenanceTeamId: '',
|
||||||
scheduledStart: "",
|
scheduledStart: '',
|
||||||
scheduledEnd: "",
|
scheduledEnd: '',
|
||||||
estimatedCost: 0,
|
estimatedCost: 0,
|
||||||
notes: "",
|
notes: '',
|
||||||
});
|
})
|
||||||
setErrors({});
|
setErrors({})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<div className="bg-white rounded-lg w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">
|
<h2 className="text-lg font-semibold text-gray-900">İş Emri Oluştur</h2>
|
||||||
İş Emri Oluştur
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||||
</h2>
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-6 h-6" />
|
<FaTimes className="w-6 h-6" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -131,18 +125,13 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
||||||
</h4>
|
</h4>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{notifications.map((notification) => (
|
{notifications.map((notification) => (
|
||||||
<div
|
<div key={notification.id} className="text-sm text-blue-700">
|
||||||
key={notification.id}
|
<span className="font-medium">{notification.notificationCode}</span>
|
||||||
className="text-sm text-blue-700"
|
{' - '}
|
||||||
>
|
|
||||||
<span className="font-medium">
|
|
||||||
{notification.notificationCode}
|
|
||||||
</span>
|
|
||||||
{" - "}
|
|
||||||
<span>{notification.title}</span>
|
<span>{notification.title}</span>
|
||||||
{" ("}
|
{' ('}
|
||||||
<span>{notification.workCenter.code}</span>
|
<span>{notification.workCenter.code}</span>
|
||||||
{")"}
|
{')'}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -159,55 +148,36 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={workOrderData.workOrderNumber}
|
value={workOrderData.workOrderNumber}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('workOrderNumber', e.target.value)}
|
||||||
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"
|
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"
|
placeholder="WO-2024-001"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">İş Emri Türü</label>
|
||||||
İş Emri Türü
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={workOrderData.orderType}
|
value={workOrderData.orderType}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleInputChange(
|
handleInputChange('orderType', e.target.value as WorkOrderTypeEnum)
|
||||||
"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"
|
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}>
|
<option value={WorkOrderTypeEnum.Preventive}>Önleyici Bakım</option>
|
||||||
Önleyici Bakım
|
<option value={WorkOrderTypeEnum.Corrective}>Düzeltici Bakım</option>
|
||||||
</option>
|
<option value={WorkOrderTypeEnum.Emergency}>Acil Müdahale</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.Inspection}>İnceleme</option>
|
||||||
<option value={WorkOrderTypeEnum.Calibration}>
|
<option value={WorkOrderTypeEnum.Calibration}>Kalibrasyon</option>
|
||||||
Kalibrasyon
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Priority */}
|
{/* Priority */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Öncelik</label>
|
||||||
Öncelik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={workOrderData.priority}
|
value={workOrderData.priority}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('priority', e.target.value as PriorityEnum)}
|
||||||
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"
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
>
|
>
|
||||||
<option value={PriorityEnum.Low}>Düşük</option>
|
<option value={PriorityEnum.Low}>Düşük</option>
|
||||||
|
|
@ -219,15 +189,13 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Açıklama *</label>
|
||||||
Açıklama *
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={workOrderData.description}
|
value={workOrderData.description}
|
||||||
onChange={(e) => handleInputChange("description", e.target.value)}
|
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||||
rows={4}
|
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 ${
|
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"
|
placeholder="İş emri detayları ve yapılacak işlemler"
|
||||||
/>
|
/>
|
||||||
|
|
@ -239,15 +207,13 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
||||||
{/* Assignment */}
|
{/* Assignment */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Atanan Kişi</label>
|
||||||
Atanan Kişi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={workOrderData.assignedTo}
|
value={workOrderData.assignedTo}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
handleInputChange("assignedTo", e.target.value);
|
handleInputChange('assignedTo', e.target.value)
|
||||||
if (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"
|
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>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Atanan Ekip</label>
|
||||||
Atanan Ekip
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={workOrderData.maintenanceTeamId}
|
value={workOrderData.maintenanceTeamId}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
handleInputChange("maintenanceTeamId", e.target.value);
|
handleInputChange('maintenanceTeamId', e.target.value)
|
||||||
if (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"
|
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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{errors.assignment && (
|
{errors.assignment && <p className="text-red-500 text-sm mt-1">{errors.assignment}</p>}
|
||||||
<p className="text-red-500 text-sm mt-1">{errors.assignment}</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Schedule */}
|
{/* Schedule */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
|
@ -297,17 +259,13 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={workOrderData.scheduledStart}
|
value={workOrderData.scheduledStart}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('scheduledStart', e.target.value)}
|
||||||
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 ${
|
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 && (
|
{errors.scheduledStart && (
|
||||||
<p className="text-red-500 text-sm mt-1">
|
<p className="text-red-500 text-sm mt-1">{errors.scheduledStart}</p>
|
||||||
{errors.scheduledStart}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -318,17 +276,13 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={workOrderData.scheduledEnd}
|
value={workOrderData.scheduledEnd}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('scheduledEnd', e.target.value)}
|
||||||
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 ${
|
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 && (
|
{errors.scheduledEnd && (
|
||||||
<p className="text-red-500 text-sm mt-1">
|
<p className="text-red-500 text-sm mt-1">{errors.scheduledEnd}</p>
|
||||||
{errors.scheduledEnd}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -341,12 +295,7 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={workOrderData.estimatedCost}
|
value={workOrderData.estimatedCost}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('estimatedCost', parseFloat(e.target.value) || 0)}
|
||||||
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"
|
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"
|
placeholder="0.00"
|
||||||
min="0"
|
min="0"
|
||||||
|
|
@ -356,12 +305,10 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
||||||
|
|
||||||
{/* Notes */}
|
{/* Notes */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Notlar</label>
|
||||||
Notlar
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={workOrderData.notes}
|
value={workOrderData.notes}
|
||||||
onChange={(e) => handleInputChange("notes", e.target.value)}
|
onChange={(e) => handleInputChange('notes', e.target.value)}
|
||||||
rows={3}
|
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"
|
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"
|
placeholder="Ek notlar ve özel talimatlar"
|
||||||
|
|
@ -387,7 +334,7 @@ const CreateWorkOrderFromNotificationModal: React.FC<
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default CreateWorkOrderFromNotificationModal;
|
export default CreateWorkOrderFromNotificationModal
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaExclamationTriangle } from "react-icons/fa";
|
import { FaTimes, FaSave, FaExclamationTriangle } from 'react-icons/fa'
|
||||||
import {
|
import {
|
||||||
PmMaintenancePlan,
|
PmMaintenancePlan,
|
||||||
PmMaintenanceWorkOrder,
|
PmMaintenanceWorkOrder,
|
||||||
WorkOrderStatusEnum,
|
WorkOrderStatusEnum,
|
||||||
WorkOrderTypeEnum,
|
WorkOrderTypeEnum,
|
||||||
} from "../../../types/pm";
|
} from '../../../types/pm'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { PriorityEnum } from "../../../types/common";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
import { getPriorityColor } from "../../../utils/erp";
|
import { getPriorityColor } from '../../../utils/erp'
|
||||||
|
|
||||||
interface CreateWorkOrderModalProps {
|
interface CreateWorkOrderModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (workOrders: Partial<PmMaintenanceWorkOrder>[]) => void;
|
onSave: (workOrders: Partial<PmMaintenanceWorkOrder>[]) => void
|
||||||
selectedPlans: PmMaintenancePlan[];
|
selectedPlans: PmMaintenancePlan[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
|
|
@ -24,64 +24,54 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
selectedPlans,
|
selectedPlans,
|
||||||
}) => {
|
}) => {
|
||||||
const [commonSettings, setCommonSettings] = useState({
|
const [commonSettings, setCommonSettings] = useState({
|
||||||
scheduledStartDate: new Date().toISOString().split("T")[0],
|
scheduledStartDate: new Date().toISOString().split('T')[0],
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
assignedTo: "",
|
assignedTo: '',
|
||||||
notes: "",
|
notes: '',
|
||||||
createSeparateOrders: true,
|
createSeparateOrders: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
const [individualSettings, setIndividualSettings] = useState<{
|
const [individualSettings, setIndividualSettings] = useState<{
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
scheduledStartDate?: string;
|
scheduledStartDate?: string
|
||||||
priority?: PriorityEnum;
|
priority?: PriorityEnum
|
||||||
assignedTo?: string;
|
assignedTo?: string
|
||||||
notes?: string;
|
notes?: string
|
||||||
};
|
}
|
||||||
}>({});
|
}>({})
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
const handleCommonChange = (
|
const handleCommonChange = (field: string, value: string | PriorityEnum | boolean) => {
|
||||||
field: string,
|
|
||||||
value: string | PriorityEnum | boolean
|
|
||||||
) => {
|
|
||||||
setCommonSettings((prev) => ({
|
setCommonSettings((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
}));
|
}))
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleIndividualChange = (
|
const handleIndividualChange = (planId: string, field: string, value: string | PriorityEnum) => {
|
||||||
planId: string,
|
|
||||||
field: string,
|
|
||||||
value: string | PriorityEnum
|
|
||||||
) => {
|
|
||||||
setIndividualSettings((prev) => ({
|
setIndividualSettings((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[planId]: {
|
[planId]: {
|
||||||
...prev[planId],
|
...prev[planId],
|
||||||
[field]: value,
|
[field]: value,
|
||||||
},
|
},
|
||||||
}));
|
}))
|
||||||
};
|
}
|
||||||
|
|
||||||
const generateWorkOrderCode = (plan: PmMaintenancePlan, index: number) => {
|
const generateWorkOrderCode = (plan: PmMaintenancePlan, index: number) => {
|
||||||
const date = new Date();
|
const date = new Date()
|
||||||
const dateStr = date.toISOString().slice(0, 10).replace(/-/g, "");
|
const dateStr = date.toISOString().slice(0, 10).replace(/-/g, '')
|
||||||
return `WO-${plan.planCode}-${dateStr}-${String(index + 1).padStart(
|
return `WO-${plan.planCode}-${dateStr}-${String(index + 1).padStart(3, '0')}`
|
||||||
3,
|
}
|
||||||
"0"
|
|
||||||
)}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
const workOrders: Partial<PmMaintenanceWorkOrder>[] = [];
|
const workOrders: Partial<PmMaintenanceWorkOrder>[] = []
|
||||||
|
|
||||||
if (commonSettings.createSeparateOrders) {
|
if (commonSettings.createSeparateOrders) {
|
||||||
// Her plan için ayrı iş emri oluştur
|
// Her plan için ayrı iş emri oluştur
|
||||||
selectedPlans.forEach((plan, index) => {
|
selectedPlans.forEach((plan, index) => {
|
||||||
const individual = individualSettings[plan.id] || {};
|
const individual = individualSettings[plan.id] || {}
|
||||||
const workOrder: Partial<PmMaintenanceWorkOrder> = {
|
const workOrder: Partial<PmMaintenanceWorkOrder> = {
|
||||||
workOrderNumber: generateWorkOrderCode(plan, index),
|
workOrderNumber: generateWorkOrderCode(plan, index),
|
||||||
planId: plan.id,
|
planId: plan.id,
|
||||||
|
|
@ -91,16 +81,14 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
status: WorkOrderStatusEnum.Planned,
|
status: WorkOrderStatusEnum.Planned,
|
||||||
description: `${plan.description} - Bakım Planı: ${plan.planCode}`,
|
description: `${plan.description} - Bakım Planı: ${plan.planCode}`,
|
||||||
scheduledStart: new Date(
|
scheduledStart: new Date(
|
||||||
individual.scheduledStartDate || commonSettings.scheduledStartDate
|
individual.scheduledStartDate || commonSettings.scheduledStartDate,
|
||||||
),
|
),
|
||||||
scheduledEnd: new Date(
|
scheduledEnd: new Date(
|
||||||
new Date(
|
new Date(individual.scheduledStartDate || commonSettings.scheduledStartDate).getTime() +
|
||||||
individual.scheduledStartDate || commonSettings.scheduledStartDate
|
plan.estimatedDuration * 60000,
|
||||||
).getTime() +
|
|
||||||
plan.estimatedDuration * 60000
|
|
||||||
),
|
),
|
||||||
assignedTo: individual.assignedTo || commonSettings.assignedTo,
|
assignedTo: individual.assignedTo || commonSettings.assignedTo,
|
||||||
reportedBy: "System",
|
reportedBy: 'System',
|
||||||
estimatedCost: 0,
|
estimatedCost: 0,
|
||||||
actualCost: 0,
|
actualCost: 0,
|
||||||
materials: [],
|
materials: [],
|
||||||
|
|
@ -108,25 +96,20 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
notes: individual.notes || commonSettings.notes,
|
notes: individual.notes || commonSettings.notes,
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
workOrders.push(workOrder);
|
workOrders.push(workOrder)
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
// Tek bir toplu iş emri oluştur
|
// Tek bir toplu iş emri oluştur
|
||||||
const combinedPlanCodes = selectedPlans.map((p) => p.planCode).join(", ");
|
const combinedPlanCodes = selectedPlans.map((p) => p.planCode).join(', ')
|
||||||
const combinedWorkCenterIds = [
|
const combinedWorkCenterIds = [...new Set(selectedPlans.map((p) => p.workCenterId))]
|
||||||
...new Set(selectedPlans.map((p) => p.workCenterId)),
|
const totalDuration = selectedPlans.reduce((sum, plan) => sum + plan.estimatedDuration, 0)
|
||||||
];
|
|
||||||
const totalDuration = selectedPlans.reduce(
|
|
||||||
(sum, plan) => sum + plan.estimatedDuration,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
const workOrder: Partial<PmMaintenanceWorkOrder> = {
|
const workOrder: Partial<PmMaintenanceWorkOrder> = {
|
||||||
workOrderNumber: `WO-BULK-${new Date()
|
workOrderNumber: `WO-BULK-${new Date()
|
||||||
.toISOString()
|
.toISOString()
|
||||||
.slice(0, 10)
|
.slice(0, 10)
|
||||||
.replace(/-/g, "")}-${String(Date.now()).slice(-3)}`,
|
.replace(/-/g, '')}-${String(Date.now()).slice(-3)}`,
|
||||||
workCenterId: combinedWorkCenterIds[0], // Ana İş merkezleri
|
workCenterId: combinedWorkCenterIds[0], // Ana İş merkezleri
|
||||||
orderType: WorkOrderTypeEnum.Preventive,
|
orderType: WorkOrderTypeEnum.Preventive,
|
||||||
priority: commonSettings.priority,
|
priority: commonSettings.priority,
|
||||||
|
|
@ -134,34 +117,33 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
description: `Toplu Bakım İş Emri - Planlar: ${combinedPlanCodes}`,
|
description: `Toplu Bakım İş Emri - Planlar: ${combinedPlanCodes}`,
|
||||||
scheduledStart: new Date(commonSettings.scheduledStartDate),
|
scheduledStart: new Date(commonSettings.scheduledStartDate),
|
||||||
scheduledEnd: new Date(
|
scheduledEnd: new Date(
|
||||||
new Date(commonSettings.scheduledStartDate).getTime() +
|
new Date(commonSettings.scheduledStartDate).getTime() + totalDuration * 60000,
|
||||||
totalDuration * 60000
|
|
||||||
),
|
),
|
||||||
reportedBy: "System",
|
reportedBy: 'System',
|
||||||
estimatedCost: 0,
|
estimatedCost: 0,
|
||||||
actualCost: 0,
|
actualCost: 0,
|
||||||
materials: [],
|
materials: [],
|
||||||
activities: [],
|
activities: [],
|
||||||
notes: `${commonSettings.notes}\n\nPlan Talimatları:\n${selectedPlans
|
notes: `${commonSettings.notes}\n\nPlan Talimatları:\n${selectedPlans
|
||||||
.map((p) => `${p.planCode}: ${p.instructions || "Talimat yok"}`)
|
.map((p) => `${p.planCode}: ${p.instructions || 'Talimat yok'}`)
|
||||||
.join("\n")}`,
|
.join('\n')}`,
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
workOrders.push(workOrder);
|
workOrders.push(workOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
onSave(workOrders);
|
onSave(workOrders)
|
||||||
setCommonSettings({
|
setCommonSettings({
|
||||||
scheduledStartDate: new Date().toISOString().split("T")[0],
|
scheduledStartDate: new Date().toISOString().split('T')[0],
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
assignedTo: "",
|
assignedTo: '',
|
||||||
notes: "",
|
notes: '',
|
||||||
createSeparateOrders: true,
|
createSeparateOrders: true,
|
||||||
});
|
})
|
||||||
setIndividualSettings({});
|
setIndividualSettings({})
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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 */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold text-gray-900">
|
<h2 className="text-lg font-semibold text-gray-900">İş Emri Oluştur</h2>
|
||||||
İş Emri Oluştur
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-sm text-gray-600">
|
||||||
{selectedPlans.length} plan için iş emri oluşturuluyor
|
{selectedPlans.length} plan için iş emri oluşturuluyor
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button onClick={onClose} className="p-2 hover:bg-gray-100 rounded-lg transition-colors">
|
||||||
onClick={onClose}
|
|
||||||
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5 text-gray-500" />
|
<FaTimes className="w-5 h-5 text-gray-500" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -188,24 +165,16 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-4 space-y-4">
|
||||||
{/* Selected Plans Summary */}
|
{/* Selected Plans Summary */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Seçili Bakım Planları</h3>
|
||||||
Seçili Bakım Planları
|
|
||||||
</h3>
|
|
||||||
<div className="bg-gray-50 rounded-lg p-3">
|
<div className="bg-gray-50 rounded-lg p-3">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
{selectedPlans.map((plan) => (
|
{selectedPlans.map((plan) => (
|
||||||
<div key={plan.id} className="flex items-center space-x-3">
|
<div key={plan.id} className="flex items-center space-x-3">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<span className="text-sm font-medium text-gray-900">
|
<span className="text-sm font-medium text-gray-900">{plan.planCode}</span>
|
||||||
{plan.planCode}
|
<span className="text-xs text-gray-500 ml-2">({plan.workCenterId})</span>
|
||||||
</span>
|
|
||||||
<span className="text-xs text-gray-500 ml-2">
|
|
||||||
({plan.workCenterId})
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span className={`text-xs ${getPriorityColor(plan.priority)}`}>
|
||||||
className={`text-xs ${getPriorityColor(plan.priority)}`}
|
|
||||||
>
|
|
||||||
{plan.priority}
|
{plan.priority}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -216,24 +185,18 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
|
|
||||||
{/* Work Order Type Selection */}
|
{/* Work Order Type Selection */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">İş Emri Türü</h3>
|
||||||
İş Emri Türü
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<label className="flex items-center space-x-3">
|
<label className="flex items-center space-x-3">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="orderType"
|
name="orderType"
|
||||||
checked={commonSettings.createSeparateOrders}
|
checked={commonSettings.createSeparateOrders}
|
||||||
onChange={() =>
|
onChange={() => handleCommonChange('createSeparateOrders', true)}
|
||||||
handleCommonChange("createSeparateOrders", true)
|
|
||||||
}
|
|
||||||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"
|
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-sm font-medium text-gray-900">
|
<span className="text-sm font-medium text-gray-900">Ayrı İş Emirleri</span>
|
||||||
Ayrı İş Emirleri
|
|
||||||
</span>
|
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-sm text-gray-600">
|
||||||
Her bakım planı için ayrı bir iş emri oluşturulur
|
Her bakım planı için ayrı bir iş emri oluşturulur
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -244,15 +207,11 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
type="radio"
|
type="radio"
|
||||||
name="orderType"
|
name="orderType"
|
||||||
checked={!commonSettings.createSeparateOrders}
|
checked={!commonSettings.createSeparateOrders}
|
||||||
onChange={() =>
|
onChange={() => handleCommonChange('createSeparateOrders', false)}
|
||||||
handleCommonChange("createSeparateOrders", false)
|
|
||||||
}
|
|
||||||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"
|
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-sm font-medium text-gray-900">
|
<span className="text-sm font-medium text-gray-900">Toplu İş Emri</span>
|
||||||
Toplu İş Emri
|
|
||||||
</span>
|
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-sm text-gray-600">
|
||||||
Tüm seçili planlar için tek bir iş emri oluşturulur
|
Tüm seçili planlar için tek bir iş emri oluşturulur
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -263,9 +222,7 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
|
|
||||||
{/* Common Settings */}
|
{/* Common Settings */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Ortak Ayarlar</h3>
|
||||||
Ortak Ayarlar
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
|
@ -274,25 +231,16 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={commonSettings.scheduledStartDate}
|
value={commonSettings.scheduledStartDate}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleCommonChange('scheduledStartDate', e.target.value)}
|
||||||
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"
|
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
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Öncelik</label>
|
||||||
Öncelik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={commonSettings.priority}
|
value={commonSettings.priority}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleCommonChange('priority', e.target.value as PriorityEnum)}
|
||||||
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"
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
>
|
>
|
||||||
<option value={PriorityEnum.Low}>Düşük</option>
|
<option value={PriorityEnum.Low}>Düşük</option>
|
||||||
|
|
@ -302,14 +250,10 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Atanan Kişi</label>
|
||||||
Atanan Kişi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={commonSettings.assignedTo}
|
value={commonSettings.assignedTo}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleCommonChange('assignedTo', e.target.value)}
|
||||||
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"
|
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>
|
<option value="">Teknisyen seçin</option>
|
||||||
|
|
@ -321,12 +265,10 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Notlar</label>
|
||||||
Notlar
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={commonSettings.notes}
|
value={commonSettings.notes}
|
||||||
onChange={(e) => handleCommonChange("notes", e.target.value)}
|
onChange={(e) => handleCommonChange('notes', e.target.value)}
|
||||||
rows={1}
|
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"
|
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..."
|
placeholder="Ek notlar ve talimatlar..."
|
||||||
|
|
@ -346,12 +288,9 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{selectedPlans.map((plan) => {
|
{selectedPlans.map((plan) => {
|
||||||
const individual = individualSettings[plan.id] || {};
|
const individual = individualSettings[plan.id] || {}
|
||||||
return (
|
return (
|
||||||
<div
|
<div key={plan.id} className="border border-gray-200 rounded-lg p-3">
|
||||||
key={plan.id}
|
|
||||||
className="border border-gray-200 rounded-lg p-3"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<h4 className="text-sm font-medium text-gray-900">
|
<h4 className="text-sm font-medium text-gray-900">
|
||||||
{plan.planCode} - {plan.description}
|
{plan.planCode} - {plan.description}
|
||||||
|
|
@ -367,13 +306,9 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={individual.scheduledStartDate || ""}
|
value={individual.scheduledStartDate || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleIndividualChange(
|
handleIndividualChange(plan.id, 'scheduledStartDate', e.target.value)
|
||||||
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"
|
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
|
Öncelik
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={individual.priority || ""}
|
value={individual.priority || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleIndividualChange(
|
handleIndividualChange(
|
||||||
plan.id,
|
plan.id,
|
||||||
"priority",
|
'priority',
|
||||||
e.target.value as PriorityEnum
|
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"
|
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
|
Atanan Kişi
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={individual.assignedTo || ""}
|
value={individual.assignedTo || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleIndividualChange(
|
handleIndividualChange(plan.id, 'assignedTo', e.target.value)
|
||||||
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"
|
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>
|
<option value="">Teknisyen seçin</option>
|
||||||
{mockEmployees.map((employee) => (
|
{mockEmployees.map((employee) => (
|
||||||
<option
|
<option key={employee.id} value={employee.fullName}>
|
||||||
key={employee.id}
|
|
||||||
value={employee.fullName}
|
|
||||||
>
|
|
||||||
{employee.fullName} ({employee.code})
|
{employee.fullName} ({employee.code})
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
|
|
@ -428,7 +356,7 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -440,13 +368,11 @@ const CreateWorkOrderModal: React.FC<CreateWorkOrderModalProps> = ({
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm font-medium text-yellow-800">
|
<h4 className="text-sm font-medium text-yellow-800">Toplu İş Emri Uyarısı</h4>
|
||||||
Toplu İş Emri Uyarısı
|
|
||||||
</h4>
|
|
||||||
<p className="text-sm text-yellow-700 mt-1">
|
<p className="text-sm text-yellow-700 mt-1">
|
||||||
Toplu iş emri oluşturulduğunda, farklı iş merkezleri ve
|
Toplu iş emri oluşturulduğunda, farklı iş merkezleri ve planlar tek bir iş emri
|
||||||
planlar tek bir iş emri altında birleştirilecektir. Bu durum
|
altında birleştirilecektir. Bu durum takip ve raporlama açısından karmaşıklığa
|
||||||
takip ve raporlama açısından karmaşıklığa neden olabilir.
|
neden olabilir.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</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"
|
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" />
|
<FaSave className="w-4 h-4" />
|
||||||
<span>
|
<span>İş {commonSettings.createSeparateOrders ? 'Emirlerini' : 'Emrini'} Oluştur</span>
|
||||||
İş {commonSettings.createSeparateOrders ? "Emirlerini" : "Emrini"}{" "}
|
|
||||||
Oluştur
|
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default CreateWorkOrderModal;
|
export default CreateWorkOrderModal
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from 'react'
|
||||||
import { FaTimes, FaSave, FaUpload, FaMinus } from "react-icons/fa";
|
import { FaTimes, FaSave, FaUpload, FaMinus } from 'react-icons/fa'
|
||||||
import {
|
import {
|
||||||
PmFaultNotification,
|
PmFaultNotification,
|
||||||
FaultTypeEnum,
|
FaultTypeEnum,
|
||||||
CriticalityLevelEnum,
|
CriticalityLevelEnum,
|
||||||
NotificationStatusEnum,
|
NotificationStatusEnum,
|
||||||
} from "../../../types/pm";
|
} from '../../../types/pm'
|
||||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { PriorityEnum } from "../../../types/common";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
|
|
||||||
interface EditFaultNotificationModalProps {
|
interface EditFaultNotificationModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (notification: PmFaultNotification) => void;
|
onSave: (notification: PmFaultNotification) => void
|
||||||
notification: PmFaultNotification | null;
|
notification: PmFaultNotification | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
|
|
@ -23,65 +23,61 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
onSave,
|
onSave,
|
||||||
notification,
|
notification,
|
||||||
}) => {
|
}) => {
|
||||||
const [notificationData, setNotificationData] = useState<
|
const [notificationData, setNotificationData] = useState<Partial<PmFaultNotification>>({})
|
||||||
Partial<PmFaultNotification>
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
>({});
|
const [uploadedImages, setUploadedImages] = useState<string[]>([])
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
||||||
const [uploadedImages, setUploadedImages] = useState<string[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (notification && isOpen) {
|
if (notification && isOpen) {
|
||||||
setNotificationData(notification);
|
setNotificationData(notification)
|
||||||
setUploadedImages(notification.images || []);
|
setUploadedImages(notification.images || [])
|
||||||
}
|
}
|
||||||
}, [notification, isOpen]);
|
}, [notification, isOpen])
|
||||||
|
|
||||||
if (!isOpen || !notification) return null;
|
if (!isOpen || !notification) return null
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (!notificationData.workCenterId) {
|
if (!notificationData.workCenterId) {
|
||||||
newErrors.workCenterId = "İş Merkezi seçimi gerekli";
|
newErrors.workCenterId = 'İş Merkezi seçimi gerekli'
|
||||||
}
|
}
|
||||||
if (!notificationData.title?.trim()) {
|
if (!notificationData.title?.trim()) {
|
||||||
newErrors.title = "Başlık gerekli";
|
newErrors.title = 'Başlık gerekli'
|
||||||
}
|
}
|
||||||
if (!notificationData.description?.trim()) {
|
if (!notificationData.description?.trim()) {
|
||||||
newErrors.description = "Açıklama gerekli";
|
newErrors.description = 'Açıklama gerekli'
|
||||||
}
|
}
|
||||||
if (!notificationData.location?.trim()) {
|
if (!notificationData.location?.trim()) {
|
||||||
newErrors.location = "Konum gerekli";
|
newErrors.location = 'Konum gerekli'
|
||||||
}
|
}
|
||||||
if (!notificationData.reportedBy?.trim()) {
|
if (!notificationData.reportedBy?.trim()) {
|
||||||
newErrors.reportedBy = "Bildiren kişi gerekli";
|
newErrors.reportedBy = 'Bildiren kişi gerekli'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (
|
||||||
field: keyof PmFaultNotification,
|
field: keyof PmFaultNotification,
|
||||||
value: string | number | boolean | undefined
|
value: string | number | boolean | undefined,
|
||||||
) => {
|
) => {
|
||||||
setNotificationData((prev) => ({
|
setNotificationData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
}));
|
}))
|
||||||
// Clear error when user starts typing
|
// Clear error when user starts typing
|
||||||
if (errors[field]) {
|
if (errors[field]) {
|
||||||
setErrors((prev) => ({
|
setErrors((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: "",
|
[field]: '',
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleWorkCenterChange = (workCenterId: string) => {
|
const handleWorkCenterChange = (workCenterId: string) => {
|
||||||
const selectedWorkCenter = mockWorkCenters.find(
|
const selectedWorkCenter = mockWorkCenters.find((eq) => eq.id === workCenterId)
|
||||||
(eq) => eq.id === workCenterId
|
|
||||||
);
|
|
||||||
if (selectedWorkCenter) {
|
if (selectedWorkCenter) {
|
||||||
setNotificationData((prev) => ({
|
setNotificationData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
|
|
@ -89,29 +85,29 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
workCenterCode: selectedWorkCenter.code,
|
workCenterCode: selectedWorkCenter.code,
|
||||||
workCenterName: selectedWorkCenter.name,
|
workCenterName: selectedWorkCenter.name,
|
||||||
location: selectedWorkCenter.location,
|
location: selectedWorkCenter.location,
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleImageUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleImageUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const files = event.target.files;
|
const files = event.target.files
|
||||||
if (files) {
|
if (files) {
|
||||||
const imageNames = Array.from(files).map((file) => file.name);
|
const imageNames = Array.from(files).map((file) => file.name)
|
||||||
setUploadedImages((prev) => [...prev, ...imageNames]);
|
setUploadedImages((prev) => [...prev, ...imageNames])
|
||||||
setNotificationData((prev) => ({
|
setNotificationData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
images: [...(prev.images || []), ...imageNames],
|
images: [...(prev.images || []), ...imageNames],
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeImage = (index: number) => {
|
const removeImage = (index: number) => {
|
||||||
setUploadedImages((prev) => prev.filter((_, i) => i !== index));
|
setUploadedImages((prev) => prev.filter((_, i) => i !== index))
|
||||||
setNotificationData((prev) => ({
|
setNotificationData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
images: prev.images?.filter((_, i) => i !== index) || [],
|
images: prev.images?.filter((_, i) => i !== index) || [],
|
||||||
}));
|
}))
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
|
|
@ -119,25 +115,20 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
...notification,
|
...notification,
|
||||||
...notificationData,
|
...notificationData,
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
|
|
||||||
onSave(updatedNotification);
|
onSave(updatedNotification)
|
||||||
onClose();
|
onClose()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<div className="bg-white rounded-lg w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">
|
<h2 className="text-lg font-semibold text-gray-900">Arıza Bildirimini Düzenle</h2>
|
||||||
Arıza Bildirimini Düzenle
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||||
</h2>
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5" />
|
<FaTimes className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -152,24 +143,20 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={notificationData.notificationCode || ""}
|
value={notificationData.notificationCode || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('notificationCode', e.target.value)}
|
||||||
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"
|
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"
|
placeholder="ARZ-2024-001"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||||
İş Merkezi *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={notificationData.workCenterId || ""}
|
value={notificationData.workCenterId || ''}
|
||||||
onChange={(e) => handleWorkCenterChange(e.target.value)}
|
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 ${
|
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>
|
<option value="">İş merkezi seçin</option>
|
||||||
|
|
@ -180,9 +167,7 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.workCenterId && (
|
{errors.workCenterId && (
|
||||||
<p className="text-red-500 text-sm mt-1">
|
<p className="text-red-500 text-sm mt-1">{errors.workCenterId}</p>
|
||||||
{errors.workCenterId}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -195,16 +180,14 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={notificationData.title || ""}
|
value={notificationData.title || ''}
|
||||||
onChange={(e) => handleInputChange("title", e.target.value)}
|
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 ${
|
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ığı"
|
placeholder="Arıza başlığı"
|
||||||
/>
|
/>
|
||||||
{errors.title && (
|
{errors.title && <p className="text-red-500 text-sm mt-1">{errors.title}</p>}
|
||||||
<p className="text-red-500 text-sm mt-1">{errors.title}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -212,20 +195,16 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
Detaylı Açıklama *
|
Detaylı Açıklama *
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={notificationData.description || ""}
|
value={notificationData.description || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||||
handleInputChange("description", e.target.value)
|
|
||||||
}
|
|
||||||
rows={3}
|
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 ${
|
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"
|
placeholder="Arızanın detaylı açıklaması, belirtiler ve gözlemler"
|
||||||
/>
|
/>
|
||||||
{errors.description && (
|
{errors.description && (
|
||||||
<p className="text-red-500 text-sm mt-1">
|
<p className="text-red-500 text-sm mt-1">{errors.description}</p>
|
||||||
{errors.description}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -233,21 +212,17 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
{/* Location and Reporter */}
|
{/* Location and Reporter */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Konum *</label>
|
||||||
Konum *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={notificationData.location || ""}
|
value={notificationData.location || ''}
|
||||||
onChange={(e) => handleInputChange("location", e.target.value)}
|
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 ${
|
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"
|
placeholder="Atölye A - Hat 1"
|
||||||
/>
|
/>
|
||||||
{errors.location && (
|
{errors.location && <p className="text-red-500 text-sm mt-1">{errors.location}</p>}
|
||||||
<p className="text-red-500 text-sm mt-1">{errors.location}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -255,12 +230,10 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
Bildiren Kişi *
|
Bildiren Kişi *
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={notificationData.reportedBy || ""}
|
value={notificationData.reportedBy || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('reportedBy', e.target.value)}
|
||||||
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 ${
|
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>
|
<option value="">Bildiren kişi seçin</option>
|
||||||
|
|
@ -279,41 +252,28 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
{/* Status and Assignment */}
|
{/* Status and Assignment */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
||||||
Durum
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={notificationData.status || NotificationStatusEnum.Open}
|
value={notificationData.status || NotificationStatusEnum.Open}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleInputChange(
|
handleInputChange('status', e.target.value as NotificationStatusEnum)
|
||||||
"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"
|
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.Open}>Açık</option>
|
||||||
<option value={NotificationStatusEnum.Assigned}>Atandı</option>
|
<option value={NotificationStatusEnum.Assigned}>Atandı</option>
|
||||||
<option value={NotificationStatusEnum.InProgress}>
|
<option value={NotificationStatusEnum.InProgress}>Devam Ediyor</option>
|
||||||
Devam Ediyor
|
|
||||||
</option>
|
|
||||||
<option value={NotificationStatusEnum.Resolved}>Çözüldü</option>
|
<option value={NotificationStatusEnum.Resolved}>Çözüldü</option>
|
||||||
<option value={NotificationStatusEnum.Closed}>Kapatıldı</option>
|
<option value={NotificationStatusEnum.Closed}>Kapatıldı</option>
|
||||||
<option value={NotificationStatusEnum.Rejected}>
|
<option value={NotificationStatusEnum.Rejected}>Reddedildi</option>
|
||||||
Reddedildi
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Atanan Kişi</label>
|
||||||
Atanan Kişi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={notificationData.assignedTo || ""}
|
value={notificationData.assignedTo || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('assignedTo', e.target.value)}
|
||||||
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"
|
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>
|
<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 className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Arıza Türü</label>
|
||||||
Arıza Türü
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={notificationData.faultType || FaultTypeEnum.Mechanical}
|
value={notificationData.faultType || FaultTypeEnum.Mechanical}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('faultType', e.target.value as FaultTypeEnum)}
|
||||||
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"
|
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>
|
<option value={FaultTypeEnum.Mechanical}>Mekanik</option>
|
||||||
|
|
@ -353,14 +306,10 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Öncelik</label>
|
||||||
Öncelik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={notificationData.priority || PriorityEnum.Normal}
|
value={notificationData.priority || PriorityEnum.Normal}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('priority', e.target.value as PriorityEnum)}
|
||||||
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"
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
>
|
>
|
||||||
<option value={PriorityEnum.Low}>Düşük</option>
|
<option value={PriorityEnum.Low}>Düşük</option>
|
||||||
|
|
@ -371,16 +320,11 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Kritiklik</label>
|
||||||
Kritiklik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={notificationData.severity || CriticalityLevelEnum.Medium}
|
value={notificationData.severity || CriticalityLevelEnum.Medium}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleInputChange(
|
handleInputChange('severity', e.target.value as CriticalityLevelEnum)
|
||||||
"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"
|
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>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={notificationData.estimatedRepairTime || ""}
|
value={notificationData.estimatedRepairTime || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleInputChange(
|
handleInputChange('estimatedRepairTime', parseInt(e.target.value) || undefined)
|
||||||
"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"
|
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"
|
placeholder="120"
|
||||||
|
|
@ -419,12 +360,9 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={notificationData.actualRepairTime || ""}
|
value={notificationData.actualRepairTime || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleInputChange(
|
handleInputChange('actualRepairTime', parseInt(e.target.value) || undefined)
|
||||||
"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"
|
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"
|
placeholder="150"
|
||||||
|
|
@ -435,13 +373,11 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
|
|
||||||
{/* Work Order ID */}
|
{/* Work Order ID */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">İş Emri ID</label>
|
||||||
İş Emri ID
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={notificationData.workOrderId || ""}
|
value={notificationData.workOrderId || ''}
|
||||||
onChange={(e) => handleInputChange("workOrderId", e.target.value)}
|
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"
|
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"
|
placeholder="WO-2024-001"
|
||||||
/>
|
/>
|
||||||
|
|
@ -449,14 +385,10 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
|
|
||||||
{/* Resolution Notes */}
|
{/* Resolution Notes */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Çözüm Notları</label>
|
||||||
Çözüm Notları
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={notificationData.resolutionNotes || ""}
|
value={notificationData.resolutionNotes || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('resolutionNotes', e.target.value)}
|
||||||
handleInputChange("resolutionNotes", e.target.value)
|
|
||||||
}
|
|
||||||
rows={3}
|
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"
|
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"
|
placeholder="Çözüm detayları ve yapılan işlemler"
|
||||||
|
|
@ -469,9 +401,7 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={notificationData.followUpRequired || false}
|
checked={notificationData.followUpRequired || false}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('followUpRequired', e.target.checked)}
|
||||||
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"
|
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>
|
||||||
|
|
@ -480,9 +410,7 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
|
|
||||||
{/* Image Upload */}
|
{/* Image Upload */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Fotoğraflar</label>
|
||||||
Fotoğraflar
|
|
||||||
</label>
|
|
||||||
<div className="border-2 border-dashed border-gray-300 rounded-lg p-4">
|
<div className="border-2 border-dashed border-gray-300 rounded-lg p-4">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<FaUpload className="mx-auto h-10 w-10 text-gray-400" />
|
<FaUpload className="mx-auto h-10 w-10 text-gray-400" />
|
||||||
|
|
@ -510,9 +438,7 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
|
|
||||||
{uploadedImages.length > 0 && (
|
{uploadedImages.length > 0 && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<h4 className="text-sm font-medium text-gray-700 mb-2">
|
<h4 className="text-sm font-medium text-gray-700 mb-2">Mevcut Fotoğraflar:</h4>
|
||||||
Mevcut Fotoğraflar:
|
|
||||||
</h4>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{uploadedImages.map((image, index) => (
|
{uploadedImages.map((image, index) => (
|
||||||
<div
|
<div
|
||||||
|
|
@ -553,7 +479,7 @@ const EditFaultNotificationModal: React.FC<EditFaultNotificationModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default EditFaultNotificationModal;
|
export default EditFaultNotificationModal
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from 'react'
|
||||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||||
import {
|
import {
|
||||||
PmMaintenancePlan,
|
PmMaintenancePlan,
|
||||||
MaintenancePlanTypeEnum,
|
MaintenancePlanTypeEnum,
|
||||||
FrequencyUnitEnum,
|
FrequencyUnitEnum,
|
||||||
PmPlanMaterial,
|
PmPlanMaterial,
|
||||||
} from "../../../types/pm";
|
} from '../../../types/pm'
|
||||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||||
import { mockMaterials } from "../../../mocks/mockMaterials";
|
import { mockMaterials } from '../../../mocks/mockMaterials'
|
||||||
import { mockUnits } from "../../../mocks/mockUnits";
|
import { mockUnits } from '../../../mocks/mockUnits'
|
||||||
import { PriorityEnum } from "../../../types/common";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
|
|
||||||
interface EditMaintenancePlanModalProps {
|
interface EditMaintenancePlanModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (plan: PmMaintenancePlan) => void;
|
onSave: (plan: PmMaintenancePlan) => void
|
||||||
plan: PmMaintenancePlan | null;
|
plan: PmMaintenancePlan | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
|
|
@ -24,82 +24,67 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
onSave,
|
onSave,
|
||||||
plan,
|
plan,
|
||||||
}) => {
|
}) => {
|
||||||
const [planData, setPlanData] = useState<Partial<PmMaintenancePlan>>({});
|
const [planData, setPlanData] = useState<Partial<PmMaintenancePlan>>({})
|
||||||
const [requiredSkills, setRequiredSkills] = useState<string[]>([]);
|
const [requiredSkills, setRequiredSkills] = useState<string[]>([])
|
||||||
const [requiredMaterials, setRequiredMaterials] = useState<PmPlanMaterial[]>(
|
const [requiredMaterials, setRequiredMaterials] = useState<PmPlanMaterial[]>([])
|
||||||
[]
|
const [newSkill, setNewSkill] = useState('')
|
||||||
);
|
|
||||||
const [newSkill, setNewSkill] = useState("");
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (plan) {
|
if (plan) {
|
||||||
setPlanData({
|
setPlanData({
|
||||||
...plan,
|
...plan,
|
||||||
nextDue: plan.nextDue ? new Date(plan.nextDue) : new Date(),
|
nextDue: plan.nextDue ? new Date(plan.nextDue) : new Date(),
|
||||||
});
|
})
|
||||||
setRequiredSkills(plan.requiredSkills || []);
|
setRequiredSkills(plan.requiredSkills || [])
|
||||||
setRequiredMaterials(plan.requiredMaterials || []);
|
setRequiredMaterials(plan.requiredMaterials || [])
|
||||||
}
|
}
|
||||||
}, [plan]);
|
}, [plan])
|
||||||
|
|
||||||
if (!isOpen || !plan) return null;
|
if (!isOpen || !plan) return null
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (
|
||||||
e: React.ChangeEvent<
|
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||||||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
|
||||||
>
|
|
||||||
) => {
|
) => {
|
||||||
const { name, value, type } = e.target;
|
const { name, value, type } = e.target
|
||||||
setPlanData((prev) => ({
|
setPlanData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[name]:
|
[name]: type === 'date' ? new Date(value) : type === 'number' ? Number(value) : value,
|
||||||
type === "date"
|
}))
|
||||||
? new Date(value)
|
}
|
||||||
: type === "number"
|
|
||||||
? Number(value)
|
|
||||||
: value,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const addSkill = () => {
|
const addSkill = () => {
|
||||||
if (newSkill.trim() && !requiredSkills.includes(newSkill.trim())) {
|
if (newSkill.trim() && !requiredSkills.includes(newSkill.trim())) {
|
||||||
setRequiredSkills([...requiredSkills, newSkill.trim()]);
|
setRequiredSkills([...requiredSkills, newSkill.trim()])
|
||||||
setNewSkill("");
|
setNewSkill('')
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeSkill = (skillToRemove: string) => {
|
const removeSkill = (skillToRemove: string) => {
|
||||||
setRequiredSkills(
|
setRequiredSkills(requiredSkills.filter((skill) => skill !== skillToRemove))
|
||||||
requiredSkills.filter((skill) => skill !== skillToRemove)
|
}
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const addMaterial = () => {
|
const addMaterial = () => {
|
||||||
const newMaterial: PmPlanMaterial = {
|
const newMaterial: PmPlanMaterial = {
|
||||||
id: `MAT${Date.now()}`,
|
id: `MAT${Date.now()}`,
|
||||||
planId: plan.id,
|
planId: plan.id,
|
||||||
materialId: "",
|
materialId: '',
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
unitId: "",
|
unitId: '',
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
};
|
}
|
||||||
setRequiredMaterials([...requiredMaterials, newMaterial]);
|
setRequiredMaterials([...requiredMaterials, newMaterial])
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeMaterial = (index: number) => {
|
const removeMaterial = (index: number) => {
|
||||||
setRequiredMaterials(requiredMaterials.filter((_, i) => i !== index));
|
setRequiredMaterials(requiredMaterials.filter((_, i) => i !== index))
|
||||||
};
|
}
|
||||||
|
|
||||||
const updateMaterial = (
|
const updateMaterial = (index: number, field: string, value: string | number | boolean) => {
|
||||||
index: number,
|
|
||||||
field: string,
|
|
||||||
value: string | number | boolean
|
|
||||||
) => {
|
|
||||||
const updated = requiredMaterials.map((material, i) =>
|
const updated = requiredMaterials.map((material, i) =>
|
||||||
i === index ? { ...material, [field]: value } : material
|
i === index ? { ...material, [field]: value } : material,
|
||||||
);
|
)
|
||||||
setRequiredMaterials(updated);
|
setRequiredMaterials(updated)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
const updatedPlan: PmMaintenancePlan = {
|
const updatedPlan: PmMaintenancePlan = {
|
||||||
|
|
@ -108,11 +93,11 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
requiredSkills,
|
requiredSkills,
|
||||||
requiredMaterials,
|
requiredMaterials,
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
} as PmMaintenancePlan;
|
} as PmMaintenancePlan
|
||||||
|
|
||||||
onSave(updatedPlan);
|
onSave(updatedPlan)
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<h2 className="text-lg font-semibold text-gray-900">
|
||||||
Bakım Planı Düzenle - {plan.planCode}
|
Bakım Planı Düzenle - {plan.planCode}
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||||
onClick={onClose}
|
|
||||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -134,30 +116,24 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-4 space-y-4">
|
||||||
{/* Basic Information */}
|
{/* Basic Information */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
<h3 className="text-lg font-medium text-gray-900 mb-4">Temel Bilgiler</h3>
|
||||||
Temel Bilgiler
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Plan Kodu *</label>
|
||||||
Plan Kodu *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="planCode"
|
name="planCode"
|
||||||
value={planData.planCode || ""}
|
value={planData.planCode || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||||
İş Merkezi *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="workCenterId"
|
name="workCenterId"
|
||||||
value={planData.workCenterId || ""}
|
value={planData.workCenterId || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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
|
required
|
||||||
|
|
@ -171,35 +147,21 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Plan Tipi *</label>
|
||||||
Plan Tipi *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="planType"
|
name="planType"
|
||||||
value={
|
value={planData.planType || MaintenancePlanTypeEnum.Preventive}
|
||||||
planData.planType || MaintenancePlanTypeEnum.Preventive
|
|
||||||
}
|
|
||||||
onChange={handleInputChange}
|
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"
|
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}>
|
<option value={MaintenancePlanTypeEnum.Preventive}>Önleyici Bakım</option>
|
||||||
Önleyici Bakım
|
<option value={MaintenancePlanTypeEnum.Predictive}>Kestirimci Bakım</option>
|
||||||
</option>
|
<option value={MaintenancePlanTypeEnum.Corrective}>Düzeltici Bakım</option>
|
||||||
<option value={MaintenancePlanTypeEnum.Predictive}>
|
<option value={MaintenancePlanTypeEnum.Condition}>Durum Bazlı Bakım</option>
|
||||||
Kestirimci Bakım
|
|
||||||
</option>
|
|
||||||
<option value={MaintenancePlanTypeEnum.Corrective}>
|
|
||||||
Düzeltici Bakım
|
|
||||||
</option>
|
|
||||||
<option value={MaintenancePlanTypeEnum.Condition}>
|
|
||||||
Durum Bazlı Bakım
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Öncelik</label>
|
||||||
Öncelik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="priority"
|
name="priority"
|
||||||
value={planData.priority || PriorityEnum.Normal}
|
value={planData.priority || PriorityEnum.Normal}
|
||||||
|
|
@ -213,12 +175,10 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama *</label>
|
||||||
Açıklama *
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
name="description"
|
name="description"
|
||||||
value={planData.description || ""}
|
value={planData.description || ''}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
rows={2}
|
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"
|
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 */}
|
{/* Frequency Settings */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
<h3 className="text-lg font-medium text-gray-900 mb-4">Sıklık Ayarları</h3>
|
||||||
Sıklık Ayarları
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Sıklık *</label>
|
||||||
Sıklık *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
name="frequency"
|
name="frequency"
|
||||||
|
|
@ -287,11 +243,7 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
name="nextDue"
|
name="nextDue"
|
||||||
value={
|
value={planData.nextDue ? planData.nextDue.toISOString().split('T')[0] : ''}
|
||||||
planData.nextDue
|
|
||||||
? planData.nextDue.toISOString().split("T")[0]
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm font-medium text-gray-700">
|
<span className="text-sm font-medium text-gray-700">Plan Aktif</span>
|
||||||
Plan Aktif
|
|
||||||
</span>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -320,12 +270,10 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
|
|
||||||
{/* Instructions */}
|
{/* Instructions */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
<h3 className="text-lg font-medium text-gray-900 mb-4">Bakım Talimatları</h3>
|
||||||
Bakım Talimatları
|
|
||||||
</h3>
|
|
||||||
<textarea
|
<textarea
|
||||||
name="instructions"
|
name="instructions"
|
||||||
value={planData.instructions || ""}
|
value={planData.instructions || ''}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
rows={3}
|
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"
|
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 */}
|
{/* Required Skills */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h3 className="text-lg font-medium text-gray-900">
|
<h3 className="text-lg font-medium text-gray-900">Gerekli Yetenekler</h3>
|
||||||
Gerekli Yetenekler
|
|
||||||
</h3>
|
|
||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -346,7 +292,7 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
onChange={(e) => setNewSkill(e.target.value)}
|
onChange={(e) => setNewSkill(e.target.value)}
|
||||||
placeholder="Yetenek ekle"
|
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"
|
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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -379,9 +325,7 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
{/* Required Materials */}
|
{/* Required Materials */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h3 className="text-lg font-medium text-gray-900">
|
<h3 className="text-lg font-medium text-gray-900">Gerekli Malzemeler</h3>
|
||||||
Gerekli Malzemeler
|
|
||||||
</h3>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={addMaterial}
|
onClick={addMaterial}
|
||||||
|
|
@ -393,23 +337,14 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{requiredMaterials.map((material, index) => (
|
{requiredMaterials.map((material, index) => (
|
||||||
<div
|
<div key={index} className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg">
|
||||||
key={index}
|
|
||||||
className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg"
|
|
||||||
>
|
|
||||||
<select
|
<select
|
||||||
value={material.materialId}
|
value={material.materialId}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const selectedMaterial = mockMaterials.find(
|
const selectedMaterial = mockMaterials.find((m) => m.id === e.target.value)
|
||||||
(m) => m.id === e.target.value
|
updateMaterial(index, 'materialId', e.target.value)
|
||||||
);
|
|
||||||
updateMaterial(index, "materialId", e.target.value);
|
|
||||||
if (selectedMaterial) {
|
if (selectedMaterial) {
|
||||||
updateMaterial(
|
updateMaterial(index, 'unitId', selectedMaterial.baseUnitId)
|
||||||
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"
|
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
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={material.quantity}
|
value={material.quantity}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateMaterial(index, 'quantity', Number(e.target.value))}
|
||||||
updateMaterial(index, "quantity", Number(e.target.value))
|
|
||||||
}
|
|
||||||
placeholder="Miktar"
|
placeholder="Miktar"
|
||||||
min="1"
|
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"
|
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
|
<select
|
||||||
value={material.unitId}
|
value={material.unitId}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateMaterial(index, 'unitId', e.target.value)}
|
||||||
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"
|
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>
|
<option value="">Birim</option>
|
||||||
|
|
@ -448,29 +379,25 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
{material.materialId &&
|
{material.materialId &&
|
||||||
(() => {
|
(() => {
|
||||||
const selectedMaterial = mockMaterials.find(
|
const selectedMaterial = mockMaterials.find(
|
||||||
(m) => m.id === material.materialId
|
(m) => m.id === material.materialId,
|
||||||
);
|
)
|
||||||
const baseUnit = selectedMaterial?.baseUnit;
|
const baseUnit = selectedMaterial?.baseUnit
|
||||||
const existsInList = mockUnits.some(
|
const existsInList = mockUnits.some((unit) => unit.id === baseUnit?.id)
|
||||||
(unit) => unit.id === baseUnit?.id
|
|
||||||
);
|
|
||||||
if (baseUnit && !existsInList) {
|
if (baseUnit && !existsInList) {
|
||||||
return (
|
return (
|
||||||
<option key={baseUnit.id} value={baseUnit.id}>
|
<option key={baseUnit.id} value={baseUnit.id}>
|
||||||
{baseUnit.code}
|
{baseUnit.code}
|
||||||
</option>
|
</option>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
})()}
|
})()}
|
||||||
</select>
|
</select>
|
||||||
<label className="flex items-center space-x-2">
|
<label className="flex items-center space-x-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={material.isRequired}
|
checked={material.isRequired}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateMaterial(index, 'isRequired', e.target.checked)}
|
||||||
updateMaterial(index, "isRequired", e.target.checked)
|
|
||||||
}
|
|
||||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm text-gray-700">Zorunlu</span>
|
<span className="text-sm text-gray-700">Zorunlu</span>
|
||||||
|
|
@ -486,8 +413,8 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
))}
|
))}
|
||||||
{requiredMaterials.length === 0 && (
|
{requiredMaterials.length === 0 && (
|
||||||
<p className="text-gray-500 text-center py-4">
|
<p className="text-gray-500 text-center py-4">
|
||||||
Henüz malzeme eklenmedi. "Malzeme Ekle" butonunu kullanarak
|
Henüz malzeme eklenmedi. "Malzeme Ekle" butonunu kullanarak malzeme
|
||||||
malzeme ekleyebilirsiniz.
|
ekleyebilirsiniz.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -512,7 +439,7 @@ const EditMaintenancePlanModal: React.FC<EditMaintenancePlanModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default EditMaintenancePlanModal;
|
export default EditMaintenancePlanModal
|
||||||
|
|
|
||||||
|
|
@ -1,164 +1,143 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from 'react'
|
||||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||||
import MultiSelectEmployee from "../../../components/common/MultiSelectEmployee";
|
import MultiSelectEmployee from '../../../components/common/MultiSelectEmployee'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { Team, TeamMember, TeamRoleEnum } from "../../../types/common";
|
import { Team, TeamMember, TeamRoleEnum } from '../../../types/common'
|
||||||
|
|
||||||
interface EditTeamModalProps {
|
interface EditTeamModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (team: Team) => void;
|
onSave: (team: Team) => void
|
||||||
team: Team | null;
|
team: Team | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
const EditTeamModal: React.FC<EditTeamModalProps> = ({ isOpen, onClose, onSave, team }) => {
|
||||||
isOpen,
|
|
||||||
onClose,
|
|
||||||
onSave,
|
|
||||||
team,
|
|
||||||
}) => {
|
|
||||||
const [teamData, setTeamData] = useState<Partial<Team>>({
|
const [teamData, setTeamData] = useState<Partial<Team>>({
|
||||||
code: "",
|
code: '',
|
||||||
name: "",
|
name: '',
|
||||||
description: "",
|
description: '',
|
||||||
isActive: true,
|
isActive: true,
|
||||||
specializations: [],
|
specializations: [],
|
||||||
members: [],
|
members: [],
|
||||||
});
|
})
|
||||||
|
|
||||||
const [selectedEmployees, setSelectedEmployees] = useState<string[]>([]);
|
const [selectedEmployees, setSelectedEmployees] = useState<string[]>([])
|
||||||
const [newSpecialization, setNewSpecialization] = useState("");
|
const [newSpecialization, setNewSpecialization] = useState('')
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (team && isOpen) {
|
if (team && isOpen) {
|
||||||
setTeamData(team);
|
setTeamData(team)
|
||||||
const employeeNames = team.members
|
const employeeNames = team.members
|
||||||
.filter((m) => m.isActive)
|
.filter((m) => m.isActive)
|
||||||
.map((m) => m.employee?.firstName + " " + m.employee?.lastName || "");
|
.map((m) => m.employee?.firstName + ' ' + m.employee?.lastName || '')
|
||||||
setSelectedEmployees(employeeNames);
|
setSelectedEmployees(employeeNames)
|
||||||
}
|
}
|
||||||
}, [team, isOpen]);
|
}, [team, isOpen])
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (!teamData.code?.trim()) {
|
if (!teamData.code?.trim()) {
|
||||||
newErrors.teamCode = "Ekip kodu gerekli";
|
newErrors.teamCode = 'Ekip kodu gerekli'
|
||||||
}
|
}
|
||||||
if (!teamData.name?.trim()) {
|
if (!teamData.name?.trim()) {
|
||||||
newErrors.teamName = "Ekip adı gerekli";
|
newErrors.teamName = 'Ekip adı gerekli'
|
||||||
}
|
}
|
||||||
if (!teamData.description?.trim()) {
|
if (!teamData.description?.trim()) {
|
||||||
newErrors.description = "Açıklama gerekli";
|
newErrors.description = 'Açıklama gerekli'
|
||||||
}
|
}
|
||||||
if (!teamData.managerId) {
|
if (!teamData.managerId) {
|
||||||
newErrors.leaderId = "Ekip lideri seçilmeli";
|
newErrors.leaderId = 'Ekip lideri seçilmeli'
|
||||||
}
|
}
|
||||||
if (selectedEmployees.length === 0) {
|
if (selectedEmployees.length === 0) {
|
||||||
newErrors.members = "En az bir ekip üyesi seçilmeli";
|
newErrors.members = 'En az bir ekip üyesi seçilmeli'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (field: keyof Team, value: string | boolean | string[]) => {
|
||||||
field: keyof Team,
|
|
||||||
value: string | boolean | string[]
|
|
||||||
) => {
|
|
||||||
setTeamData((prev) => ({
|
setTeamData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
}));
|
}))
|
||||||
// Clear error when user starts typing
|
// Clear error when user starts typing
|
||||||
if (errors[field]) {
|
if (errors[field]) {
|
||||||
setErrors((prev) => ({
|
setErrors((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: "",
|
[field]: '',
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const addSpecialization = () => {
|
const addSpecialization = () => {
|
||||||
if (
|
if (newSpecialization.trim() && !teamData.specializations?.includes(newSpecialization.trim())) {
|
||||||
newSpecialization.trim() &&
|
|
||||||
!teamData.specializations?.includes(newSpecialization.trim())
|
|
||||||
) {
|
|
||||||
setTeamData((prev) => ({
|
setTeamData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
specializations: [
|
specializations: [...(prev.specializations || []), newSpecialization.trim()],
|
||||||
...(prev.specializations || []),
|
}))
|
||||||
newSpecialization.trim(),
|
setNewSpecialization('')
|
||||||
],
|
|
||||||
}));
|
|
||||||
setNewSpecialization("");
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeSpecialization = (index: number) => {
|
const removeSpecialization = (index: number) => {
|
||||||
setTeamData((prev) => ({
|
setTeamData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
specializations:
|
specializations: prev.specializations?.filter((_, i) => i !== index) || [],
|
||||||
prev.specializations?.filter((_, i) => i !== index) || [],
|
}))
|
||||||
}));
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const handleEmployeeSelection = (employees: string[]) => {
|
const handleEmployeeSelection = (employees: string[]) => {
|
||||||
setSelectedEmployees(employees);
|
setSelectedEmployees(employees)
|
||||||
if (errors.members) {
|
if (errors.members) {
|
||||||
setErrors((prev) => ({
|
setErrors((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
members: "",
|
members: '',
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleMemberRoleChange = (employeeName: string, role: TeamRoleEnum) => {
|
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) {
|
if (role === TeamRoleEnum.Lead && employee) {
|
||||||
handleInputChange("managerId", employee.id);
|
handleInputChange('managerId', employee.id)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
const members: TeamMember[] = selectedEmployees.map(
|
const members: TeamMember[] = selectedEmployees.map((employeeName, index) => {
|
||||||
(employeeName, index) => {
|
const employee = mockEmployees.find((emp) => emp.fullName === employeeName)
|
||||||
const employee = mockEmployees.find(
|
const existingMember = team?.members.find(
|
||||||
(emp) => emp.fullName === employeeName
|
(m) => m.employee?.firstName + ' ' + m.employee?.lastName === employeeName,
|
||||||
);
|
)
|
||||||
const existingMember = team?.members.find(
|
const isLeader = teamData.managerId === employee?.id
|
||||||
(m) =>
|
|
||||||
m.employee?.firstName + " " + m.employee?.lastName ===
|
|
||||||
employeeName
|
|
||||||
);
|
|
||||||
const isLeader = teamData.managerId === employee?.id;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: existingMember?.id || `TM${Date.now()}-${index}`,
|
id: existingMember?.id || `TM${Date.now()}-${index}`,
|
||||||
teamId: team?.id || "",
|
teamId: team?.id || '',
|
||||||
employeeId: employee?.id || "",
|
employeeId: employee?.id || '',
|
||||||
employee: mockEmployees.find((emp) => emp.id === employee?.id),
|
employee: mockEmployees.find((emp) => emp.id === employee?.id),
|
||||||
role: isLeader ? TeamRoleEnum.Lead : TeamRoleEnum.Member,
|
role: isLeader ? TeamRoleEnum.Lead : TeamRoleEnum.Member,
|
||||||
joinDate: existingMember?.joinDate || new Date(),
|
joinDate: existingMember?.joinDate || new Date(),
|
||||||
isActive: true,
|
isActive: true,
|
||||||
};
|
|
||||||
}
|
}
|
||||||
);
|
})
|
||||||
|
|
||||||
const updatedTeam: Team = {
|
const updatedTeam: Team = {
|
||||||
...team!,
|
...team!,
|
||||||
...teamData,
|
...teamData,
|
||||||
managerId: teamData.managerId || "",
|
managerId: teamData.managerId || '',
|
||||||
members,
|
members,
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
|
|
||||||
onSave(updatedTeam);
|
onSave(updatedTeam)
|
||||||
onClose();
|
onClose()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isOpen &&
|
isOpen &&
|
||||||
|
|
@ -181,91 +160,69 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
||||||
{/* Basic Info */}
|
{/* Basic Info */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Ekip Kodu *</label>
|
||||||
Ekip Kodu *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={teamData.code || ""}
|
value={teamData.code || ''}
|
||||||
onChange={(e) => handleInputChange("code", e.target.value)}
|
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 ${
|
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"
|
placeholder="MEC-001"
|
||||||
/>
|
/>
|
||||||
{errors.code && (
|
{errors.code && <p className="text-red-500 text-sm mt-1">{errors.code}</p>}
|
||||||
<p className="text-red-500 text-sm mt-1">{errors.code}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Ekip Adı *</label>
|
||||||
Ekip Adı *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={teamData.name || ""}
|
value={teamData.name || ''}
|
||||||
onChange={(e) => handleInputChange("name", e.target.value)}
|
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 ${
|
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"
|
placeholder="Mekanik Bakım Ekibi"
|
||||||
/>
|
/>
|
||||||
{errors.name && (
|
{errors.name && <p className="text-red-500 text-sm mt-1">{errors.name}</p>}
|
||||||
<p className="text-red-500 text-sm mt-1">{errors.name}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Açıklama *</label>
|
||||||
Açıklama *
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={teamData.description || ""}
|
value={teamData.description || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||||
handleInputChange("description", e.target.value)
|
|
||||||
}
|
|
||||||
rows={2}
|
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 ${
|
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ı"
|
placeholder="Ekip açıklaması ve sorumlulukları"
|
||||||
/>
|
/>
|
||||||
{errors.description && (
|
{errors.description && (
|
||||||
<p className="text-red-500 text-sm mt-1">
|
<p className="text-red-500 text-sm mt-1">{errors.description}</p>
|
||||||
{errors.description}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Team Members */}
|
{/* Team Members */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Ekip Üyeleri *</label>
|
||||||
Ekip Üyeleri *
|
|
||||||
</label>
|
|
||||||
<MultiSelectEmployee
|
<MultiSelectEmployee
|
||||||
selectedEmployees={selectedEmployees}
|
selectedEmployees={selectedEmployees}
|
||||||
onChange={handleEmployeeSelection}
|
onChange={handleEmployeeSelection}
|
||||||
placeholder="Ekip üyelerini seçin"
|
placeholder="Ekip üyelerini seçin"
|
||||||
className={errors.members ? "border-red-500" : ""}
|
className={errors.members ? 'border-red-500' : ''}
|
||||||
/>
|
/>
|
||||||
{errors.members && (
|
{errors.members && <p className="text-red-500 text-sm mt-1">{errors.members}</p>}
|
||||||
<p className="text-red-500 text-sm mt-1">{errors.members}</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Selected Members Display */}
|
{/* Selected Members Display */}
|
||||||
{selectedEmployees.length > 0 && (
|
{selectedEmployees.length > 0 && (
|
||||||
<div className="mt-3 p-3 bg-gray-50 rounded-lg">
|
<div className="mt-3 p-3 bg-gray-50 rounded-lg">
|
||||||
<h4 className="text-sm font-medium text-gray-700 mb-2">
|
<h4 className="text-sm font-medium text-gray-700 mb-2">Seçili Ekip Üyeleri:</h4>
|
||||||
Seçili Ekip Üyeleri:
|
|
||||||
</h4>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{selectedEmployees.map((employeeName, index) => {
|
{selectedEmployees.map((employeeName, index) => {
|
||||||
const employee = mockEmployees.find(
|
const employee = mockEmployees.find((emp) => emp.fullName === employeeName)
|
||||||
(emp) => emp.fullName === employeeName
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
|
|
@ -279,12 +236,8 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-900">
|
<p className="text-sm font-medium text-gray-900">{employeeName}</p>
|
||||||
{employeeName}
|
<p className="text-sm text-gray-600">{employee?.jobPosition?.name}</p>
|
||||||
</p>
|
|
||||||
<p className="text-sm text-gray-600">
|
|
||||||
{employee?.jobPosition?.name}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -295,22 +248,17 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
||||||
: TeamRoleEnum.Member
|
: TeamRoleEnum.Member
|
||||||
}
|
}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleMemberRoleChange(
|
handleMemberRoleChange(employeeName, e.target.value as TeamRoleEnum)
|
||||||
employeeName,
|
|
||||||
e.target.value as TeamRoleEnum
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
className="text-xs px-2 py-1 border border-gray-300 rounded"
|
className="text-xs px-2 py-1 border border-gray-300 rounded"
|
||||||
>
|
>
|
||||||
<option value={TeamRoleEnum.Member}>Üye</option>
|
<option value={TeamRoleEnum.Member}>Üye</option>
|
||||||
<option value={TeamRoleEnum.Lead}>Lider</option>
|
<option value={TeamRoleEnum.Lead}>Lider</option>
|
||||||
<option value={TeamRoleEnum.Specialist}>
|
<option value={TeamRoleEnum.Specialist}>Uzman</option>
|
||||||
Uzman
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</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"
|
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"
|
placeholder="Uzmanlık alanı ekle"
|
||||||
onKeyPress={(e) => {
|
onKeyPress={(e) => {
|
||||||
if (e.key === "Enter") {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
addSpecialization();
|
addSpecialization()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -345,26 +293,25 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
||||||
<FaPlus className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{teamData.specializations &&
|
{teamData.specializations && teamData.specializations.length > 0 && (
|
||||||
teamData.specializations.length > 0 && (
|
<div className="flex flex-wrap gap-2">
|
||||||
<div className="flex flex-wrap gap-2">
|
{teamData.specializations.map((spec, index) => (
|
||||||
{teamData.specializations.map((spec, index) => (
|
<span
|
||||||
<span
|
key={index}
|
||||||
key={index}
|
className="inline-flex items-center px-2 py-1 rounded-full text-xs bg-blue-100 text-blue-800"
|
||||||
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}
|
<FaMinus className="w-3 h-3" />
|
||||||
<button
|
</button>
|
||||||
type="button"
|
</span>
|
||||||
onClick={() => removeSpecialization(index)}
|
))}
|
||||||
className="ml-2 text-blue-600 hover:text-blue-800"
|
</div>
|
||||||
>
|
)}
|
||||||
<FaMinus className="w-3 h-3" />
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -374,9 +321,7 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={teamData.isActive || false}
|
checked={teamData.isActive || false}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('isActive', e.target.checked)}
|
||||||
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"
|
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>
|
<span className="ml-2 text-sm text-gray-700">Ekip aktif</span>
|
||||||
|
|
@ -403,7 +348,7 @@ const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default EditTeamModal;
|
export default EditTeamModal
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||||
import {
|
import {
|
||||||
PmWorkCenter,
|
PmWorkCenter,
|
||||||
WorkCenterStatusEnum,
|
WorkCenterStatusEnum,
|
||||||
CriticalityLevelEnum,
|
CriticalityLevelEnum,
|
||||||
PmWorkCenterSpecification,
|
PmWorkCenterSpecification,
|
||||||
} from "../../../types/pm";
|
} from '../../../types/pm'
|
||||||
import { mockDepartments } from "../../../mocks/mockDepartments";
|
import { mockDepartments } from '../../../mocks/mockDepartments'
|
||||||
|
|
||||||
interface EditWorkCenterModalProps {
|
interface EditWorkCenterModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (workCenter: PmWorkCenter) => void;
|
onSave: (workCenter: PmWorkCenter) => void
|
||||||
workCenter: PmWorkCenter;
|
workCenter: PmWorkCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
|
|
@ -21,61 +21,55 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
onSave,
|
onSave,
|
||||||
workCenter,
|
workCenter,
|
||||||
}) => {
|
}) => {
|
||||||
const [formData, setFormData] = useState<PmWorkCenter>(workCenter);
|
const [formData, setFormData] = useState<PmWorkCenter>(workCenter)
|
||||||
const [specifications, setSpecifications] = useState<
|
const [specifications, setSpecifications] = useState<PmWorkCenterSpecification[]>(
|
||||||
PmWorkCenterSpecification[]
|
workCenter.specifications || [],
|
||||||
>(workCenter.specifications || []);
|
)
|
||||||
|
|
||||||
if (!isOpen || !workCenter) return null;
|
if (!isOpen || !workCenter) return null
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (
|
||||||
e: React.ChangeEvent<
|
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||||||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
|
||||||
>
|
|
||||||
) => {
|
) => {
|
||||||
const { name, value, type } = e.target;
|
const { name, value, type } = e.target
|
||||||
setFormData((prev) => ({
|
setFormData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[name]: type === "date" ? new Date(value) : value,
|
[name]: type === 'date' ? new Date(value) : value,
|
||||||
}));
|
}))
|
||||||
};
|
}
|
||||||
|
|
||||||
const addSpecification = () => {
|
const addSpecification = () => {
|
||||||
const newSpec: PmWorkCenterSpecification = {
|
const newSpec: PmWorkCenterSpecification = {
|
||||||
id: `SPEC${Date.now()}`,
|
id: `SPEC${Date.now()}`,
|
||||||
workCenterId: workCenter.id,
|
workCenterId: workCenter.id,
|
||||||
specificationName: "",
|
specificationName: '',
|
||||||
specificationValue: "",
|
specificationValue: '',
|
||||||
unit: "",
|
unit: '',
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
};
|
}
|
||||||
setSpecifications([...specifications, newSpec]);
|
setSpecifications([...specifications, newSpec])
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeSpecification = (index: number) => {
|
const removeSpecification = (index: number) => {
|
||||||
setSpecifications(specifications.filter((_, i) => i !== index));
|
setSpecifications(specifications.filter((_, i) => i !== index))
|
||||||
};
|
}
|
||||||
|
|
||||||
const updateSpecification = (
|
const updateSpecification = (index: number, field: string, value: string | boolean) => {
|
||||||
index: number,
|
|
||||||
field: string,
|
|
||||||
value: string | boolean
|
|
||||||
) => {
|
|
||||||
const updated = specifications.map((spec, i) =>
|
const updated = specifications.map((spec, i) =>
|
||||||
i === index ? { ...spec, [field]: value } : spec
|
i === index ? { ...spec, [field]: value } : spec,
|
||||||
);
|
)
|
||||||
setSpecifications(updated);
|
setSpecifications(updated)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
const updatedWorkCenter = {
|
const updatedWorkCenter = {
|
||||||
...formData,
|
...formData,
|
||||||
specifications,
|
specifications,
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
onSave(updatedWorkCenter);
|
onSave(updatedWorkCenter)
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<h2 className="text-lg font-semibold text-gray-900">
|
||||||
İş Merkezi Düzenle - {workCenter.code}
|
İş Merkezi Düzenle - {workCenter.code}
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||||
onClick={onClose}
|
|
||||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -97,9 +88,7 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-4 space-y-4">
|
||||||
{/* Basic Information */}
|
{/* Basic Information */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
<h3 className="text-lg font-medium text-gray-900 mb-4">Temel Bilgiler</h3>
|
||||||
Temel Bilgiler
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
|
@ -130,12 +119,10 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama</label>
|
||||||
Açıklama
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
name="description"
|
name="description"
|
||||||
value={formData.description || ""}
|
value={formData.description || ''}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
rows={2}
|
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"
|
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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
<h3 className="text-lg font-medium text-gray-900 mb-4">Teknik Özellikler</h3>
|
||||||
Teknik Özellikler
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Makine Türü</label>
|
||||||
Makine Türü
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="machineType"
|
name="machineType"
|
||||||
value={formData.machineType || "Manual"}
|
value={formData.machineType || 'Manual'}
|
||||||
onChange={handleInputChange}
|
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"
|
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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Departman</label>
|
||||||
Departman
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<select
|
<select
|
||||||
value={formData.departmentId}
|
value={formData.departmentId}
|
||||||
|
|
@ -215,26 +196,22 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Üretici</label>
|
||||||
Üretici
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="manufacturer"
|
name="manufacturer"
|
||||||
value={formData.manufacturer || ""}
|
value={formData.manufacturer || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
placeholder="örn: HAAS Automation"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Model</label>
|
||||||
Model
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="model"
|
name="model"
|
||||||
value={formData.model || ""}
|
value={formData.model || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
placeholder="örn: ST-30"
|
||||||
|
|
@ -247,7 +224,7 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="serialNumber"
|
name="serialNumber"
|
||||||
value={formData.serialNumber || ""}
|
value={formData.serialNumber || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
placeholder="örn: SN123456789"
|
||||||
|
|
@ -262,8 +239,8 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
name="installationDate"
|
name="installationDate"
|
||||||
value={
|
value={
|
||||||
formData.installationDate
|
formData.installationDate
|
||||||
? formData.installationDate.toISOString().split("T")[0]
|
? formData.installationDate.toISOString().split('T')[0]
|
||||||
: ""
|
: ''
|
||||||
}
|
}
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
name="warrantyExpiry"
|
||||||
value={
|
value={
|
||||||
formData.warrantyExpiry
|
formData.warrantyExpiry
|
||||||
? formData.warrantyExpiry.toISOString().split("T")[0]
|
? formData.warrantyExpiry.toISOString().split('T')[0]
|
||||||
: ""
|
: ''
|
||||||
}
|
}
|
||||||
onChange={handleInputChange}
|
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"
|
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>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Lokasyon *</label>
|
||||||
Lokasyon *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="location"
|
name="location"
|
||||||
|
|
@ -305,29 +280,19 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
|
|
||||||
{/* Status and Priority */}
|
{/* Status and Priority */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
<h3 className="text-lg font-medium text-gray-900 mb-4">Durum ve Öncelik</h3>
|
||||||
Durum ve Öncelik
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
||||||
Durum
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="status"
|
name="status"
|
||||||
value={formData.status}
|
value={formData.status}
|
||||||
onChange={handleInputChange}
|
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"
|
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}>
|
<option value={WorkCenterStatusEnum.Operational}>Operasyonel</option>
|
||||||
Operasyonel
|
<option value={WorkCenterStatusEnum.UnderMaintenance}>Bakımda</option>
|
||||||
</option>
|
<option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option>
|
||||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>
|
|
||||||
Bakımda
|
|
||||||
</option>
|
|
||||||
<option value={WorkCenterStatusEnum.OutOfOrder}>
|
|
||||||
Arızalı
|
|
||||||
</option>
|
|
||||||
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -348,16 +313,14 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Aktif Durum</label>
|
||||||
Aktif Durum
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="isActive"
|
name="isActive"
|
||||||
value={formData.isActive ? "true" : "false"}
|
value={formData.isActive ? 'true' : 'false'}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData((prev) => ({
|
setFormData((prev) => ({
|
||||||
...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"
|
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 */}
|
{/* Specifications */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h3 className="text-lg font-medium text-gray-900">
|
<h3 className="text-lg font-medium text-gray-900">Teknik Özellikler</h3>
|
||||||
Teknik Özellikler
|
|
||||||
</h3>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={addSpecification}
|
onClick={addSpecification}
|
||||||
|
|
@ -386,19 +347,12 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{specifications.map((spec, index) => (
|
{specifications.map((spec, index) => (
|
||||||
<div
|
<div key={index} className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg">
|
||||||
key={index}
|
|
||||||
className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg"
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={spec.specificationName}
|
value={spec.specificationName}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateSpecification(
|
updateSpecification(index, 'specificationName', e.target.value)
|
||||||
index,
|
|
||||||
"specificationName",
|
|
||||||
e.target.value
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
placeholder="Özellik adı"
|
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"
|
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"
|
type="text"
|
||||||
value={spec.specificationValue}
|
value={spec.specificationValue}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateSpecification(
|
updateSpecification(index, 'specificationValue', e.target.value)
|
||||||
index,
|
|
||||||
"specificationValue",
|
|
||||||
e.target.value
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
placeholder="Değer"
|
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"
|
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
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={spec.unit || ""}
|
value={spec.unit || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateSpecification(index, 'unit', e.target.value)}
|
||||||
updateSpecification(index, "unit", e.target.value)
|
|
||||||
}
|
|
||||||
placeholder="Birim"
|
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"
|
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
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={spec.isRequired}
|
checked={spec.isRequired}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateSpecification(index, 'isRequired', e.target.checked)}
|
||||||
updateSpecification(
|
|
||||||
index,
|
|
||||||
"isRequired",
|
|
||||||
e.target.checked
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm text-gray-700">Zorunlu</span>
|
<span className="text-sm text-gray-700">Zorunlu</span>
|
||||||
|
|
@ -451,8 +393,8 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
))}
|
))}
|
||||||
{specifications.length === 0 && (
|
{specifications.length === 0 && (
|
||||||
<p className="text-gray-500 text-center py-4">
|
<p className="text-gray-500 text-center py-4">
|
||||||
Henüz teknik özellik eklenmedi. "Özellik Ekle" butonunu
|
Henüz teknik özellik eklenmedi. "Özellik Ekle" butonunu kullanarak özellik
|
||||||
kullanarak özellik ekleyebilirsiniz.
|
ekleyebilirsiniz.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -460,20 +402,18 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
|
|
||||||
{/* WorkCenter Information Summary */}
|
{/* WorkCenter Information Summary */}
|
||||||
<div className="bg-gray-50 p-3 rounded-lg">
|
<div className="bg-gray-50 p-3 rounded-lg">
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-3">
|
<h3 className="text-lg font-medium text-gray-900 mb-3">İş Merkezi Bilgi Özeti</h3>
|
||||||
İş Merkezi Bilgi Özeti
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<span className="text-gray-600">Oluşturulma Tarihi:</span>
|
<span className="text-gray-600">Oluşturulma Tarihi:</span>
|
||||||
<p className="font-medium text-gray-900">
|
<p className="font-medium text-gray-900">
|
||||||
{formData.creationTime.toLocaleDateString("tr-TR")}
|
{formData.creationTime.toLocaleDateString('tr-TR')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-gray-600">Son Güncelleme:</span>
|
<span className="text-gray-600">Son Güncelleme:</span>
|
||||||
<p className="font-medium text-gray-900">
|
<p className="font-medium text-gray-900">
|
||||||
{formData.lastModificationTime.toLocaleDateString("tr-TR")}
|
{formData.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -500,7 +440,7 @@ const EditWorkCenterModal: React.FC<EditWorkCenterModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default EditWorkCenterModal;
|
export default EditWorkCenterModal
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,23 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from 'react'
|
||||||
import {
|
import { FaTimes, FaPlus, FaTrash, FaCalendar, FaClock, FaSave } from 'react-icons/fa'
|
||||||
FaTimes,
|
|
||||||
FaPlus,
|
|
||||||
FaTrash,
|
|
||||||
FaCalendar,
|
|
||||||
FaClock,
|
|
||||||
FaSave,
|
|
||||||
} from "react-icons/fa";
|
|
||||||
import {
|
import {
|
||||||
PmMaintenanceWorkOrder,
|
PmMaintenanceWorkOrder,
|
||||||
WorkOrderTypeEnum,
|
WorkOrderTypeEnum,
|
||||||
WorkOrderStatusEnum,
|
WorkOrderStatusEnum,
|
||||||
PmWorkOrderMaterial,
|
PmWorkOrderMaterial,
|
||||||
PmWorkOrderActivity,
|
PmWorkOrderActivity,
|
||||||
} from "../../../types/pm";
|
} from '../../../types/pm'
|
||||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { mockMaterials } from "../../../mocks/mockMaterials";
|
import { mockMaterials } from '../../../mocks/mockMaterials'
|
||||||
import { PriorityEnum } from "../../../types/common";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
|
|
||||||
interface EditWorkOrderModalProps {
|
interface EditWorkOrderModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (workOrder: PmMaintenanceWorkOrder) => void;
|
onSave: (workOrder: PmMaintenanceWorkOrder) => void
|
||||||
workOrder: PmMaintenanceWorkOrder;
|
workOrder: PmMaintenanceWorkOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
|
|
@ -34,28 +27,28 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
workOrder,
|
workOrder,
|
||||||
}) => {
|
}) => {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
workOrderNumber: "",
|
workOrderNumber: '',
|
||||||
workCenterId: "",
|
workCenterId: '',
|
||||||
orderType: WorkOrderTypeEnum.Corrective,
|
orderType: WorkOrderTypeEnum.Corrective,
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
status: WorkOrderStatusEnum.Created,
|
status: WorkOrderStatusEnum.Created,
|
||||||
description: "",
|
description: '',
|
||||||
reportedBy: "",
|
reportedBy: '',
|
||||||
assignedTo: "",
|
assignedTo: '',
|
||||||
maintenanceTeamId: "",
|
maintenanceTeamId: '',
|
||||||
scheduledStart: "",
|
scheduledStart: '',
|
||||||
scheduledEnd: "",
|
scheduledEnd: '',
|
||||||
actualStart: "",
|
actualStart: '',
|
||||||
actualEnd: "",
|
actualEnd: '',
|
||||||
estimatedCost: 0,
|
estimatedCost: 0,
|
||||||
actualCost: 0,
|
actualCost: 0,
|
||||||
notes: "",
|
notes: '',
|
||||||
completionNotes: "",
|
completionNotes: '',
|
||||||
});
|
})
|
||||||
|
|
||||||
const [materials, setMaterials] = useState<PmWorkOrderMaterial[]>([]);
|
const [materials, setMaterials] = useState<PmWorkOrderMaterial[]>([])
|
||||||
const [activities, setActivities] = useState<PmWorkOrderActivity[]>([]);
|
const [activities, setActivities] = useState<PmWorkOrderActivity[]>([])
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (workOrder && isOpen) {
|
if (workOrder && isOpen) {
|
||||||
|
|
@ -67,56 +60,52 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
status: workOrder.status,
|
status: workOrder.status,
|
||||||
description: workOrder.description,
|
description: workOrder.description,
|
||||||
reportedBy: workOrder.reportedBy,
|
reportedBy: workOrder.reportedBy,
|
||||||
assignedTo: workOrder.assignedTo || "",
|
assignedTo: workOrder.assignedTo || '',
|
||||||
maintenanceTeamId: workOrder.maintenanceTeamId || "",
|
maintenanceTeamId: workOrder.maintenanceTeamId || '',
|
||||||
scheduledStart: workOrder.scheduledStart
|
scheduledStart: workOrder.scheduledStart
|
||||||
? workOrder.scheduledStart.toISOString().slice(0, 16)
|
? workOrder.scheduledStart.toISOString().slice(0, 16)
|
||||||
: "",
|
: '',
|
||||||
scheduledEnd: workOrder.scheduledEnd
|
scheduledEnd: workOrder.scheduledEnd
|
||||||
? workOrder.scheduledEnd.toISOString().slice(0, 16)
|
? workOrder.scheduledEnd.toISOString().slice(0, 16)
|
||||||
: "",
|
: '',
|
||||||
actualStart: workOrder.actualStart
|
actualStart: workOrder.actualStart ? workOrder.actualStart.toISOString().slice(0, 16) : '',
|
||||||
? workOrder.actualStart.toISOString().slice(0, 16)
|
actualEnd: workOrder.actualEnd ? workOrder.actualEnd.toISOString().slice(0, 16) : '',
|
||||||
: "",
|
|
||||||
actualEnd: workOrder.actualEnd
|
|
||||||
? workOrder.actualEnd.toISOString().slice(0, 16)
|
|
||||||
: "",
|
|
||||||
estimatedCost: workOrder.estimatedCost,
|
estimatedCost: workOrder.estimatedCost,
|
||||||
actualCost: workOrder.actualCost,
|
actualCost: workOrder.actualCost,
|
||||||
notes: workOrder.notes || "",
|
notes: workOrder.notes || '',
|
||||||
completionNotes: workOrder.completionNotes || "",
|
completionNotes: workOrder.completionNotes || '',
|
||||||
});
|
})
|
||||||
setMaterials(workOrder.materials);
|
setMaterials(workOrder.materials)
|
||||||
setActivities(workOrder.activities);
|
setActivities(workOrder.activities)
|
||||||
}
|
}
|
||||||
}, [workOrder, isOpen]);
|
}, [workOrder, isOpen])
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (!formData.description.trim()) {
|
if (!formData.description.trim()) {
|
||||||
newErrors.description = "Açıklama alanı zorunludur";
|
newErrors.description = 'Açıklama alanı zorunludur'
|
||||||
}
|
}
|
||||||
if (!formData.workCenterId) {
|
if (!formData.workCenterId) {
|
||||||
newErrors.workCenterId = "İş merkezi seçimi zorunludur";
|
newErrors.workCenterId = 'İş merkezi seçimi zorunludur'
|
||||||
}
|
}
|
||||||
if (!formData.reportedBy.trim()) {
|
if (!formData.reportedBy.trim()) {
|
||||||
newErrors.reportedBy = "Bildiren kişi zorunludur";
|
newErrors.reportedBy = 'Bildiren kişi zorunludur'
|
||||||
}
|
}
|
||||||
if (formData.estimatedCost < 0) {
|
if (formData.estimatedCost < 0) {
|
||||||
newErrors.estimatedCost = "Tahmini maliyet negatif olamaz";
|
newErrors.estimatedCost = 'Tahmini maliyet negatif olamaz'
|
||||||
}
|
}
|
||||||
if (formData.actualCost < 0) {
|
if (formData.actualCost < 0) {
|
||||||
newErrors.actualCost = "Gerçek maliyet negatif olamaz";
|
newErrors.actualCost = 'Gerçek maliyet negatif olamaz'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
if (!validateForm()) return;
|
if (!validateForm()) return
|
||||||
|
|
||||||
const updatedWorkOrder: PmMaintenanceWorkOrder = {
|
const updatedWorkOrder: PmMaintenanceWorkOrder = {
|
||||||
...workOrder,
|
...workOrder,
|
||||||
|
|
@ -129,15 +118,9 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
reportedBy: formData.reportedBy,
|
reportedBy: formData.reportedBy,
|
||||||
assignedTo: formData.assignedTo || undefined,
|
assignedTo: formData.assignedTo || undefined,
|
||||||
maintenanceTeamId: formData.maintenanceTeamId || undefined,
|
maintenanceTeamId: formData.maintenanceTeamId || undefined,
|
||||||
scheduledStart: formData.scheduledStart
|
scheduledStart: formData.scheduledStart ? new Date(formData.scheduledStart) : undefined,
|
||||||
? new Date(formData.scheduledStart)
|
scheduledEnd: formData.scheduledEnd ? new Date(formData.scheduledEnd) : undefined,
|
||||||
: undefined,
|
actualStart: formData.actualStart ? new Date(formData.actualStart) : 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,
|
actualEnd: formData.actualEnd ? new Date(formData.actualEnd) : undefined,
|
||||||
estimatedCost: formData.estimatedCost,
|
estimatedCost: formData.estimatedCost,
|
||||||
actualCost: formData.actualCost,
|
actualCost: formData.actualCost,
|
||||||
|
|
@ -146,39 +129,35 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
notes: formData.notes || undefined,
|
notes: formData.notes || undefined,
|
||||||
completionNotes: formData.completionNotes || undefined,
|
completionNotes: formData.completionNotes || undefined,
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
|
|
||||||
onSave(updatedWorkOrder);
|
onSave(updatedWorkOrder)
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const addMaterial = () => {
|
const addMaterial = () => {
|
||||||
const newMaterial: PmWorkOrderMaterial = {
|
const newMaterial: PmWorkOrderMaterial = {
|
||||||
id: `mat-${Date.now()}`,
|
id: `mat-${Date.now()}`,
|
||||||
workOrderId: workOrder.id,
|
workOrderId: workOrder.id,
|
||||||
materialId: "",
|
materialId: '',
|
||||||
materialCode: "",
|
materialCode: '',
|
||||||
materialName: "",
|
materialName: '',
|
||||||
plannedQuantity: 1,
|
plannedQuantity: 1,
|
||||||
actualQuantity: 0,
|
actualQuantity: 0,
|
||||||
unitCost: 0,
|
unitCost: 0,
|
||||||
totalCost: 0,
|
totalCost: 0,
|
||||||
};
|
}
|
||||||
setMaterials([...materials, newMaterial]);
|
setMaterials([...materials, newMaterial])
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeMaterial = (index: number) => {
|
const removeMaterial = (index: number) => {
|
||||||
setMaterials(materials.filter((_, i) => i !== index));
|
setMaterials(materials.filter((_, i) => i !== index))
|
||||||
};
|
}
|
||||||
|
|
||||||
const updateMaterial = (
|
const updateMaterial = (index: number, field: string, value: string | number) => {
|
||||||
index: number,
|
const updated = [...materials]
|
||||||
field: string,
|
if (field === 'materialId') {
|
||||||
value: string | number
|
const selectedMaterial = mockMaterials.find((m) => m.id === value)
|
||||||
) => {
|
|
||||||
const updated = [...materials];
|
|
||||||
if (field === "materialId") {
|
|
||||||
const selectedMaterial = mockMaterials.find((m) => m.id === value);
|
|
||||||
if (selectedMaterial) {
|
if (selectedMaterial) {
|
||||||
updated[index] = {
|
updated[index] = {
|
||||||
...updated[index],
|
...updated[index],
|
||||||
|
|
@ -186,75 +165,70 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
materialCode: selectedMaterial.code,
|
materialCode: selectedMaterial.code,
|
||||||
materialName: selectedMaterial.name,
|
materialName: selectedMaterial.name,
|
||||||
unitCost: selectedMaterial.costPrice,
|
unitCost: selectedMaterial.costPrice,
|
||||||
totalCost:
|
totalCost: updated[index].plannedQuantity * selectedMaterial.costPrice,
|
||||||
updated[index].plannedQuantity * selectedMaterial.costPrice,
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const material = updated[index];
|
const material = updated[index]
|
||||||
if (field === "plannedQuantity") {
|
if (field === 'plannedQuantity') {
|
||||||
material.plannedQuantity = value as number;
|
material.plannedQuantity = value as number
|
||||||
material.totalCost = material.plannedQuantity * material.unitCost;
|
material.totalCost = material.plannedQuantity * material.unitCost
|
||||||
} else if (field === "actualQuantity") {
|
} else if (field === 'actualQuantity') {
|
||||||
material.actualQuantity = value as number;
|
material.actualQuantity = value as number
|
||||||
} else if (field === "unitCost") {
|
} else if (field === 'unitCost') {
|
||||||
material.unitCost = value as number;
|
material.unitCost = value as number
|
||||||
material.totalCost = material.plannedQuantity * material.unitCost;
|
material.totalCost = material.plannedQuantity * material.unitCost
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setMaterials(updated);
|
setMaterials(updated)
|
||||||
};
|
}
|
||||||
|
|
||||||
const addActivity = () => {
|
const addActivity = () => {
|
||||||
const newActivity: PmWorkOrderActivity = {
|
const newActivity: PmWorkOrderActivity = {
|
||||||
id: `act-${Date.now()}`,
|
id: `act-${Date.now()}`,
|
||||||
workOrderId: workOrder.id,
|
workOrderId: workOrder.id,
|
||||||
activityDescription: "",
|
activityDescription: '',
|
||||||
plannedDuration: 60,
|
plannedDuration: 60,
|
||||||
actualDuration: 0,
|
actualDuration: 0,
|
||||||
performedBy: "",
|
performedBy: '',
|
||||||
notes: "",
|
notes: '',
|
||||||
};
|
}
|
||||||
setActivities([...activities, newActivity]);
|
setActivities([...activities, newActivity])
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeActivity = (index: number) => {
|
const removeActivity = (index: number) => {
|
||||||
setActivities(activities.filter((_, i) => i !== index));
|
setActivities(activities.filter((_, i) => i !== index))
|
||||||
};
|
}
|
||||||
|
|
||||||
const updateActivity = (
|
const updateActivity = (index: number, field: string, value: string | number) => {
|
||||||
index: number,
|
const updated = [...activities]
|
||||||
field: string,
|
const activity = updated[index]
|
||||||
value: string | number
|
if (field === 'activityDescription') {
|
||||||
) => {
|
activity.activityDescription = value as string
|
||||||
const updated = [...activities];
|
} else if (field === 'plannedDuration') {
|
||||||
const activity = updated[index];
|
activity.plannedDuration = value as number
|
||||||
if (field === "activityDescription") {
|
} else if (field === 'actualDuration') {
|
||||||
activity.activityDescription = value as string;
|
activity.actualDuration = value as number
|
||||||
} else if (field === "plannedDuration") {
|
} else if (field === 'performedBy') {
|
||||||
activity.plannedDuration = value as number;
|
activity.performedBy = value as string
|
||||||
} else if (field === "actualDuration") {
|
} else if (field === 'notes') {
|
||||||
activity.actualDuration = value as number;
|
activity.notes = value as string
|
||||||
} 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 toggleActivityCompletion = (index: number) => {
|
||||||
const updated = [...activities];
|
const updated = [...activities]
|
||||||
const activity = updated[index];
|
const activity = updated[index]
|
||||||
if (activity.completedAt) {
|
if (activity.completedAt) {
|
||||||
activity.completedAt = undefined;
|
activity.completedAt = undefined
|
||||||
} else {
|
} else {
|
||||||
activity.completedAt = new Date();
|
activity.completedAt = new Date()
|
||||||
}
|
}
|
||||||
setActivities(updated);
|
setActivities(updated)
|
||||||
};
|
}
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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" />
|
<FaSave className="w-5 h-5 mr-2 text-blue-600" />
|
||||||
İş Emrini Düzenle
|
İş Emrini Düzenle
|
||||||
</h3>
|
</h3>
|
||||||
<button
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5" />
|
<FaTimes className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -276,24 +247,18 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
{/* Basic Information */}
|
{/* Basic Information */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">İş Emri No</label>
|
||||||
İş Emri No
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.workOrderNumber}
|
value={formData.workOrderNumber}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, workOrderNumber: e.target.value })}
|
||||||
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"
|
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
|
readOnly
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
||||||
Durum
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.status}
|
value={formData.status}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
|
@ -306,33 +271,21 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
>
|
>
|
||||||
<option value={WorkOrderStatusEnum.Created}>Oluşturuldu</option>
|
<option value={WorkOrderStatusEnum.Created}>Oluşturuldu</option>
|
||||||
<option value={WorkOrderStatusEnum.Planned}>Planlandı</option>
|
<option value={WorkOrderStatusEnum.Planned}>Planlandı</option>
|
||||||
<option value={WorkOrderStatusEnum.Released}>
|
<option value={WorkOrderStatusEnum.Released}>Serbest Bırakıldı</option>
|
||||||
Serbest Bırakıldı
|
<option value={WorkOrderStatusEnum.InProgress}>Devam Ediyor</option>
|
||||||
</option>
|
|
||||||
<option value={WorkOrderStatusEnum.InProgress}>
|
|
||||||
Devam Ediyor
|
|
||||||
</option>
|
|
||||||
<option value={WorkOrderStatusEnum.OnHold}>Beklemede</option>
|
<option value={WorkOrderStatusEnum.OnHold}>Beklemede</option>
|
||||||
<option value={WorkOrderStatusEnum.Completed}>
|
<option value={WorkOrderStatusEnum.Completed}>Tamamlandı</option>
|
||||||
Tamamlandı
|
<option value={WorkOrderStatusEnum.Cancelled}>İptal Edildi</option>
|
||||||
</option>
|
|
||||||
<option value={WorkOrderStatusEnum.Cancelled}>
|
|
||||||
İptal Edildi
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||||
İş Merkezi *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.workCenterId}
|
value={formData.workCenterId}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, workCenterId: e.target.value })}
|
||||||
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 ${
|
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>
|
<option value="">İş Merkezi Seçin</option>
|
||||||
|
|
@ -343,16 +296,12 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.workCenterId && (
|
{errors.workCenterId && (
|
||||||
<p className="mt-1 text-sm text-red-600">
|
<p className="mt-1 text-sm text-red-600">{errors.workCenterId}</p>
|
||||||
{errors.workCenterId}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">İş Emri Tipi</label>
|
||||||
İş Emri Tipi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.orderType}
|
value={formData.orderType}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
|
@ -367,16 +316,12 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
<option value={WorkOrderTypeEnum.Corrective}>Düzeltici</option>
|
<option value={WorkOrderTypeEnum.Corrective}>Düzeltici</option>
|
||||||
<option value={WorkOrderTypeEnum.Emergency}>Acil</option>
|
<option value={WorkOrderTypeEnum.Emergency}>Acil</option>
|
||||||
<option value={WorkOrderTypeEnum.Inspection}>İnceleme</option>
|
<option value={WorkOrderTypeEnum.Inspection}>İnceleme</option>
|
||||||
<option value={WorkOrderTypeEnum.Calibration}>
|
<option value={WorkOrderTypeEnum.Calibration}>Kalibrasyon</option>
|
||||||
Kalibrasyon
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Öncelik</label>
|
||||||
Öncelik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.priority}
|
value={formData.priority}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
|
@ -396,17 +341,13 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama *</label>
|
||||||
Açıklama *
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||||
setFormData({ ...formData, description: e.target.value })
|
|
||||||
}
|
|
||||||
rows={2}
|
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 ${
|
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ı..."
|
placeholder="İş emri açıklaması..."
|
||||||
/>
|
/>
|
||||||
|
|
@ -418,17 +359,13 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
{/* Assignment */}
|
{/* Assignment */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Bildiren *</label>
|
||||||
Bildiren *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.reportedBy}
|
value={formData.reportedBy}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, reportedBy: e.target.value })}
|
||||||
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 ${
|
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ı"
|
placeholder="Bildiren kişi adı"
|
||||||
/>
|
/>
|
||||||
|
|
@ -438,14 +375,10 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Atanan Kişi</label>
|
||||||
Atanan Kişi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.assignedTo}
|
value={formData.assignedTo}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, assignedTo: e.target.value })}
|
||||||
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"
|
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>
|
<option value="">Kişi Seçin</option>
|
||||||
|
|
@ -458,9 +391,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Bakım Ekibi</label>
|
||||||
Bakım Ekibi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.maintenanceTeamId}
|
value={formData.maintenanceTeamId}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
|
@ -491,9 +422,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={formData.scheduledStart}
|
value={formData.scheduledStart}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, scheduledStart: e.target.value })}
|
||||||
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"
|
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>
|
||||||
|
|
@ -506,9 +435,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={formData.scheduledEnd}
|
value={formData.scheduledEnd}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, scheduledEnd: e.target.value })}
|
||||||
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"
|
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>
|
||||||
|
|
@ -521,9 +448,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={formData.actualStart}
|
value={formData.actualStart}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, actualStart: e.target.value })}
|
||||||
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"
|
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>
|
||||||
|
|
@ -536,9 +461,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={formData.actualEnd}
|
value={formData.actualEnd}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, actualEnd: e.target.value })}
|
||||||
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"
|
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>
|
||||||
|
|
@ -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 ${
|
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"
|
placeholder="0.00"
|
||||||
/>
|
/>
|
||||||
{errors.estimatedCost && (
|
{errors.estimatedCost && (
|
||||||
<p className="mt-1 text-sm text-red-600">
|
<p className="mt-1 text-sm text-red-600">{errors.estimatedCost}</p>
|
||||||
{errors.estimatedCost}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Gerçek Maliyet</label>
|
||||||
Gerçek Maliyet
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
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 ${
|
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"
|
placeholder="0.00"
|
||||||
/>
|
/>
|
||||||
|
|
@ -614,20 +533,13 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{materials.map((material, index) => (
|
{materials.map((material, index) => (
|
||||||
<div
|
<div key={material.id || index} className="bg-gray-50 p-3 rounded-lg mb-2">
|
||||||
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 className="grid grid-cols-1 md:grid-cols-6 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Malzeme</label>
|
||||||
Malzeme
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={material.materialId}
|
value={material.materialId}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateMaterial(index, 'materialId', e.target.value)}
|
||||||
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"
|
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>
|
<option value="">Malzeme Seçin</option>
|
||||||
|
|
@ -647,11 +559,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
min="0"
|
min="0"
|
||||||
value={material.plannedQuantity}
|
value={material.plannedQuantity}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateMaterial(
|
updateMaterial(index, 'plannedQuantity', parseInt(e.target.value) || 0)
|
||||||
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"
|
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"
|
min="0"
|
||||||
value={material.actualQuantity}
|
value={material.actualQuantity}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateMaterial(
|
updateMaterial(index, 'actualQuantity', parseInt(e.target.value) || 0)
|
||||||
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"
|
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"
|
step="0.01"
|
||||||
value={material.unitCost}
|
value={material.unitCost}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateMaterial(
|
updateMaterial(index, 'unitCost', parseFloat(e.target.value) || 0)
|
||||||
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"
|
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>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Toplam</label>
|
||||||
Toplam
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={`₺${material.totalCost.toFixed(2)}`}
|
value={`₺${material.totalCost.toFixed(2)}`}
|
||||||
|
|
@ -733,10 +631,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{activities.map((activity, index) => (
|
{activities.map((activity, index) => (
|
||||||
<div
|
<div key={activity.id || index} className="bg-gray-50 p-3 rounded-lg mb-2">
|
||||||
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="grid grid-cols-1 md:grid-cols-6 gap-4">
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
|
@ -745,13 +640,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={activity.activityDescription}
|
value={activity.activityDescription}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateActivity(index, 'activityDescription', e.target.value)}
|
||||||
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"
|
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ı"
|
placeholder="Aktivite açıklaması"
|
||||||
/>
|
/>
|
||||||
|
|
@ -766,11 +655,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
min="0"
|
min="0"
|
||||||
value={activity.plannedDuration}
|
value={activity.plannedDuration}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateActivity(
|
updateActivity(index, 'plannedDuration', parseInt(e.target.value) || 0)
|
||||||
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"
|
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"
|
min="0"
|
||||||
value={activity.actualDuration}
|
value={activity.actualDuration}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateActivity(
|
updateActivity(index, 'actualDuration', parseInt(e.target.value) || 0)
|
||||||
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"
|
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>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Yapan</label>
|
||||||
Yapan
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={activity.performedBy}
|
value={activity.performedBy}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateActivity(index, 'performedBy', e.target.value)}
|
||||||
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"
|
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"
|
placeholder="Yapan kişi"
|
||||||
/>
|
/>
|
||||||
|
|
@ -814,11 +691,11 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
onClick={() => toggleActivityCompletion(index)}
|
onClick={() => toggleActivityCompletion(index)}
|
||||||
className={`px-3 py-2 rounded text-xs ${
|
className={`px-3 py-2 rounded text-xs ${
|
||||||
activity.completedAt
|
activity.completedAt
|
||||||
? "bg-green-600 text-white"
|
? 'bg-green-600 text-white'
|
||||||
: "bg-gray-300 text-gray-700"
|
: 'bg-gray-300 text-gray-700'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{activity.completedAt ? "Tamamlandı" : "Bekliyor"}
|
{activity.completedAt ? 'Tamamlandı' : 'Bekliyor'}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -836,9 +713,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={activity.notes}
|
value={activity.notes}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateActivity(index, 'notes', e.target.value)}
|
||||||
updateActivity(index, "notes", e.target.value)
|
|
||||||
}
|
|
||||||
rows={1}
|
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"
|
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..."
|
placeholder="Aktivite hakkında notlar..."
|
||||||
|
|
@ -857,9 +732,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={formData.notes}
|
value={formData.notes}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
|
||||||
setFormData({ ...formData, notes: e.target.value })
|
|
||||||
}
|
|
||||||
rows={2}
|
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"
|
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..."
|
placeholder="Genel notlar..."
|
||||||
|
|
@ -872,9 +745,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={formData.completionNotes}
|
value={formData.completionNotes}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, completionNotes: e.target.value })}
|
||||||
setFormData({ ...formData, completionNotes: e.target.value })
|
|
||||||
}
|
|
||||||
rows={2}
|
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"
|
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..."
|
placeholder="Tamamlanma ile ilgili notlar..."
|
||||||
|
|
@ -902,7 +773,7 @@ const EditWorkOrderModal: React.FC<EditWorkOrderModalProps> = ({
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default EditWorkOrderModal;
|
export default EditWorkOrderModal
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import {
|
import {
|
||||||
FaPlus,
|
FaPlus,
|
||||||
FaSearch,
|
FaSearch,
|
||||||
|
|
@ -13,21 +13,17 @@ import {
|
||||||
FaFileAlt,
|
FaFileAlt,
|
||||||
FaCamera,
|
FaCamera,
|
||||||
FaPhone,
|
FaPhone,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import {
|
import { NotificationStatusEnum, PmFaultNotification, PmWorkCenter } from '../../../types/pm'
|
||||||
NotificationStatusEnum,
|
import { mockFaultNotifications } from '../../../mocks/mockFaultNotifications'
|
||||||
PmFaultNotification,
|
import NewFaultNotificationModal from './NewFaultNotificationModal'
|
||||||
PmWorkCenter,
|
import ViewFaultNotificationModal from './ViewFaultNotificationModal'
|
||||||
} from "../../../types/pm";
|
import EditFaultNotificationModal from './EditFaultNotificationModal'
|
||||||
import { mockFaultNotifications } from "../../../mocks/mockFaultNotifications";
|
import CreateWorkOrderFromNotificationModal from './CreateWorkOrderFromNotificationModal'
|
||||||
import NewFaultNotificationModal from "./NewFaultNotificationModal";
|
import AssignNotificationModal from './AssignNotificationModal'
|
||||||
import ViewFaultNotificationModal from "./ViewFaultNotificationModal";
|
import ChangeNotificationStatusModal from './ChangeNotificationStatusModal'
|
||||||
import EditFaultNotificationModal from "./EditFaultNotificationModal";
|
import Widget from '../../../components/common/Widget'
|
||||||
import CreateWorkOrderFromNotificationModal from "./CreateWorkOrderFromNotificationModal";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
import AssignNotificationModal from "./AssignNotificationModal";
|
|
||||||
import ChangeNotificationStatusModal from "./ChangeNotificationStatusModal";
|
|
||||||
import Widget from "../../../components/common/Widget";
|
|
||||||
import { PriorityEnum } from "../../../types/common";
|
|
||||||
import {
|
import {
|
||||||
getFaultTypeColor,
|
getFaultTypeColor,
|
||||||
getFaultTypeText,
|
getFaultTypeText,
|
||||||
|
|
@ -37,139 +33,116 @@ import {
|
||||||
getNotificationStatusColor,
|
getNotificationStatusColor,
|
||||||
getNotificationStatusIcon,
|
getNotificationStatusIcon,
|
||||||
getNotificationStatusText,
|
getNotificationStatusText,
|
||||||
} from "../../../utils/erp";
|
} from '../../../utils/erp'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
interface AssignmentData {
|
interface AssignmentData {
|
||||||
notificationIds: string[];
|
notificationIds: string[]
|
||||||
assignedTo?: string;
|
assignedTo?: string
|
||||||
teamId?: string;
|
teamId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StatusChangeData {
|
interface StatusChangeData {
|
||||||
notificationIds: string[];
|
notificationIds: string[]
|
||||||
status: NotificationStatusEnum;
|
status: NotificationStatusEnum
|
||||||
}
|
}
|
||||||
|
|
||||||
const FaultNotifications: React.FC = () => {
|
const FaultNotifications: React.FC = () => {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [statusFilter, setStatusFilter] = useState<
|
const [statusFilter, setStatusFilter] = useState<NotificationStatusEnum | 'all'>('all')
|
||||||
NotificationStatusEnum | "all"
|
const [priorityFilter, setPriorityFilter] = useState<PriorityEnum | 'all'>('all')
|
||||||
>("all");
|
const [showModal, setShowModal] = useState(false)
|
||||||
const [priorityFilter, setPriorityFilter] = useState<PriorityEnum | "all">(
|
const [editingNotification, setEditingNotification] = useState<PmFaultNotification | null>(null)
|
||||||
"all"
|
const [viewingNotification, setViewingNotification] = useState<PmFaultNotification | null>(null)
|
||||||
);
|
const [selectedNotifications, setSelectedNotifications] = useState<string[]>([])
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showCreateWorkOrderModal, setShowCreateWorkOrderModal] = useState(false)
|
||||||
const [editingNotification, setEditingNotification] =
|
const [showAssignModal, setShowAssignModal] = useState(false)
|
||||||
useState<PmFaultNotification | null>(null);
|
const [showStatusChangeModal, setShowStatusChangeModal] = useState(false)
|
||||||
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
|
// Mock data - replace with actual API calls
|
||||||
const [notifications, setNotifications] = useState<PmFaultNotification[]>(
|
const [notifications, setNotifications] = useState<PmFaultNotification[]>(mockFaultNotifications)
|
||||||
mockFaultNotifications
|
|
||||||
);
|
|
||||||
|
|
||||||
const filteredNotifications = notifications.filter((notification) => {
|
const filteredNotifications = notifications.filter((notification) => {
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
notification.notificationCode
|
notification.notificationCode.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchTerm.toLowerCase()) ||
|
|
||||||
notification.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
notification.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
notification.workCenter.code
|
notification.workCenter.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
.toLowerCase()
|
notification.reportedBy.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
.includes(searchTerm.toLowerCase()) ||
|
const matchesStatus = statusFilter === 'all' || notification.status === statusFilter
|
||||||
notification.reportedBy.toLowerCase().includes(searchTerm.toLowerCase());
|
const matchesPriority = priorityFilter === 'all' || notification.priority === priorityFilter
|
||||||
const matchesStatus =
|
return matchesSearch && matchesStatus && matchesPriority
|
||||||
statusFilter === "all" || notification.status === statusFilter;
|
})
|
||||||
const matchesPriority =
|
|
||||||
priorityFilter === "all" || notification.priority === priorityFilter;
|
|
||||||
return matchesSearch && matchesStatus && matchesPriority;
|
|
||||||
});
|
|
||||||
|
|
||||||
const getTimeAgo = (date: Date) => {
|
const getTimeAgo = (date: Date) => {
|
||||||
const now = new Date();
|
const now = new Date()
|
||||||
const diffInMs = now.getTime() - date.getTime();
|
const diffInMs = now.getTime() - date.getTime()
|
||||||
const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60));
|
const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60))
|
||||||
const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
|
const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24))
|
||||||
|
|
||||||
if (diffInHours < 1) {
|
if (diffInHours < 1) {
|
||||||
const diffInMinutes = Math.floor(diffInMs / (1000 * 60));
|
const diffInMinutes = Math.floor(diffInMs / (1000 * 60))
|
||||||
return `${diffInMinutes} dakika önce`;
|
return `${diffInMinutes} dakika önce`
|
||||||
} else if (diffInHours < 24) {
|
} else if (diffInHours < 24) {
|
||||||
return `${diffInHours} saat önce`;
|
return `${diffInHours} saat önce`
|
||||||
} else {
|
} else {
|
||||||
return `${diffInDays} gün önce`;
|
return `${diffInDays} gün önce`
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleAddNotification = () => {
|
const handleAddNotification = () => {
|
||||||
setEditingNotification(null);
|
setEditingNotification(null)
|
||||||
setShowModal(true);
|
setShowModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleEdit = (notification: PmFaultNotification) => {
|
const handleEdit = (notification: PmFaultNotification) => {
|
||||||
setEditingNotification(notification);
|
setEditingNotification(notification)
|
||||||
setShowModal(true);
|
setShowModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleView = (notification: PmFaultNotification) => {
|
const handleView = (notification: PmFaultNotification) => {
|
||||||
setViewingNotification(notification);
|
setViewingNotification(notification)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSelectNotification = (notificationId: string) => {
|
const handleSelectNotification = (notificationId: string) => {
|
||||||
setSelectedNotifications((prev) =>
|
setSelectedNotifications((prev) =>
|
||||||
prev.includes(notificationId)
|
prev.includes(notificationId)
|
||||||
? prev.filter((id) => id !== notificationId)
|
? prev.filter((id) => id !== notificationId)
|
||||||
: [...prev, notificationId]
|
: [...prev, notificationId],
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSaveNotification = (
|
const handleSaveNotification = (notificationData: Partial<PmFaultNotification>) => {
|
||||||
notificationData: Partial<PmFaultNotification>
|
|
||||||
) => {
|
|
||||||
if (editingNotification) {
|
if (editingNotification) {
|
||||||
// Update existing notification
|
// Update existing notification
|
||||||
setNotifications((prev) =>
|
setNotifications((prev) =>
|
||||||
prev.map((n) =>
|
prev.map((n) => (n.id === editingNotification.id ? { ...n, ...notificationData } : n)),
|
||||||
n.id === editingNotification.id ? { ...n, ...notificationData } : n
|
)
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// Add new notification
|
// Add new notification
|
||||||
setNotifications((prev) => [
|
setNotifications((prev) => [...prev, notificationData as PmFaultNotification])
|
||||||
...prev,
|
|
||||||
notificationData as PmFaultNotification,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCreateWorkOrder = () => {
|
const handleCreateWorkOrder = () => {
|
||||||
setShowCreateWorkOrderModal(true);
|
setShowCreateWorkOrderModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleAssignNotifications = () => {
|
const handleAssignNotifications = () => {
|
||||||
setShowAssignModal(true);
|
setShowAssignModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleChangeStatus = () => {
|
const handleChangeStatus = () => {
|
||||||
setShowStatusChangeModal(true);
|
setShowStatusChangeModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleWorkOrderSave = (workOrderData: PmWorkCenter) => {
|
const handleWorkOrderSave = (workOrderData: PmWorkCenter) => {
|
||||||
console.log("İş emri oluşturuldu:", workOrderData);
|
console.log('İş emri oluşturuldu:', workOrderData)
|
||||||
// Here you would typically save to backend
|
// Here you would typically save to backend
|
||||||
setSelectedNotifications([]);
|
setSelectedNotifications([])
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleAssignmentSave = (assignmentData: AssignmentData) => {
|
const handleAssignmentSave = (assignmentData: AssignmentData) => {
|
||||||
console.log("Atama yapıldı:", assignmentData);
|
console.log('Atama yapıldı:', assignmentData)
|
||||||
// Here you would typically save to backend
|
// Here you would typically save to backend
|
||||||
// Update notifications with assignment
|
// Update notifications with assignment
|
||||||
setNotifications((prev) =>
|
setNotifications((prev) =>
|
||||||
|
|
@ -180,14 +153,14 @@ const FaultNotifications: React.FC = () => {
|
||||||
assignedTo: assignmentData.assignedTo,
|
assignedTo: assignmentData.assignedTo,
|
||||||
status: NotificationStatusEnum.Assigned,
|
status: NotificationStatusEnum.Assigned,
|
||||||
}
|
}
|
||||||
: n
|
: n,
|
||||||
)
|
),
|
||||||
);
|
)
|
||||||
setSelectedNotifications([]);
|
setSelectedNotifications([])
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleStatusChangeSave = (statusChangeData: StatusChangeData) => {
|
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
|
// Here you would typically save to backend
|
||||||
// Update notifications with new status
|
// Update notifications with new status
|
||||||
setNotifications((prev) =>
|
setNotifications((prev) =>
|
||||||
|
|
@ -198,359 +171,322 @@ const FaultNotifications: React.FC = () => {
|
||||||
status: statusChangeData.status,
|
status: statusChangeData.status,
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
}
|
}
|
||||||
: n
|
: n,
|
||||||
)
|
),
|
||||||
);
|
)
|
||||||
setSelectedNotifications([]);
|
setSelectedNotifications([])
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 pt-2">
|
<Container>
|
||||||
{/* Header */}
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
{/* Header */}
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-2xl font-bold text-gray-900">
|
<div>
|
||||||
Arıza Bildirimleri
|
<h2 className="text-2xl font-bold text-gray-900">Arıza Bildirimleri</h2>
|
||||||
</h2>
|
<p className="text-gray-600">İş merkezi arızalarını takip edin ve yönetin</p>
|
||||||
<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>
|
|
||||||
</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
|
<button
|
||||||
onClick={handleAddNotification}
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Bulk Actions */}
|
{/* Summary Cards */}
|
||||||
{selectedNotifications.length > 0 && (
|
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
|
||||||
<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">
|
<Widget
|
||||||
<div className="flex items-center space-x-4">
|
title="Toplam"
|
||||||
<span className="text-sm text-gray-600">
|
value={notifications.length}
|
||||||
{selectedNotifications.length} bildirim seçildi
|
color="gray"
|
||||||
</span>
|
icon="FaExclamationTriangle"
|
||||||
<div className="flex space-x-2">
|
/>
|
||||||
<button
|
|
||||||
onClick={handleCreateWorkOrder}
|
<Widget
|
||||||
className="bg-green-600 text-white px-3 py-2 rounded text-sm hover:bg-green-700"
|
title="Açık"
|
||||||
>
|
value={notifications.filter((n) => n.status === NotificationStatusEnum.Open).length}
|
||||||
İş Emri Oluştur
|
color="red"
|
||||||
</button>
|
icon="FaExclamationTriangle"
|
||||||
<button
|
/>
|
||||||
onClick={handleAssignNotifications}
|
|
||||||
className="bg-blue-600 text-white px-3 py-2 rounded text-sm hover:bg-blue-700"
|
<Widget
|
||||||
>
|
title="Devam Ediyor"
|
||||||
Atama Yap
|
value={
|
||||||
</button>
|
notifications.filter((n) => n.status === NotificationStatusEnum.InProgress).length
|
||||||
<button
|
}
|
||||||
onClick={handleChangeStatus}
|
color="orange"
|
||||||
className="bg-orange-600 text-white px-3 py-2 rounded text-sm hover:bg-orange-700"
|
icon="FaClock"
|
||||||
>
|
/>
|
||||||
Durum Değiştir
|
|
||||||
</button>
|
<Widget
|
||||||
<button
|
title="Çözüldü"
|
||||||
onClick={() => setSelectedNotifications([])}
|
value={notifications.filter((n) => n.status === NotificationStatusEnum.Resolved).length}
|
||||||
className="bg-gray-600 text-white px-3 py-2 rounded text-sm hover:bg-gray-700"
|
color="green"
|
||||||
>
|
icon="FaCheckCircle"
|
||||||
Temizle
|
/>
|
||||||
</button>
|
|
||||||
</div>
|
<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>
|
||||||
</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 */}
|
{/* Modals */}
|
||||||
{showModal && !editingNotification && (
|
{showModal && !editingNotification && (
|
||||||
|
|
@ -565,8 +501,8 @@ const FaultNotifications: React.FC = () => {
|
||||||
<EditFaultNotificationModal
|
<EditFaultNotificationModal
|
||||||
isOpen={showModal}
|
isOpen={showModal}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setShowModal(false);
|
setShowModal(false)
|
||||||
setEditingNotification(null);
|
setEditingNotification(null)
|
||||||
}}
|
}}
|
||||||
onSave={handleSaveNotification}
|
onSave={handleSaveNotification}
|
||||||
notification={editingNotification}
|
notification={editingNotification}
|
||||||
|
|
@ -578,9 +514,9 @@ const FaultNotifications: React.FC = () => {
|
||||||
isOpen={!!viewingNotification}
|
isOpen={!!viewingNotification}
|
||||||
onClose={() => setViewingNotification(null)}
|
onClose={() => setViewingNotification(null)}
|
||||||
onEdit={(notification) => {
|
onEdit={(notification) => {
|
||||||
setViewingNotification(null);
|
setViewingNotification(null)
|
||||||
setEditingNotification(notification);
|
setEditingNotification(notification)
|
||||||
setShowModal(true);
|
setShowModal(true)
|
||||||
}}
|
}}
|
||||||
notification={viewingNotification}
|
notification={viewingNotification}
|
||||||
/>
|
/>
|
||||||
|
|
@ -591,9 +527,7 @@ const FaultNotifications: React.FC = () => {
|
||||||
isOpen={showCreateWorkOrderModal}
|
isOpen={showCreateWorkOrderModal}
|
||||||
onClose={() => setShowCreateWorkOrderModal(false)}
|
onClose={() => setShowCreateWorkOrderModal(false)}
|
||||||
onSave={handleWorkOrderSave}
|
onSave={handleWorkOrderSave}
|
||||||
notifications={notifications.filter((n) =>
|
notifications={notifications.filter((n) => selectedNotifications.includes(n.id))}
|
||||||
selectedNotifications.includes(n.id)
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -602,9 +536,7 @@ const FaultNotifications: React.FC = () => {
|
||||||
isOpen={showAssignModal}
|
isOpen={showAssignModal}
|
||||||
onClose={() => setShowAssignModal(false)}
|
onClose={() => setShowAssignModal(false)}
|
||||||
onSave={handleAssignmentSave}
|
onSave={handleAssignmentSave}
|
||||||
notifications={notifications.filter((n) =>
|
notifications={notifications.filter((n) => selectedNotifications.includes(n.id))}
|
||||||
selectedNotifications.includes(n.id)
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -613,13 +545,11 @@ const FaultNotifications: React.FC = () => {
|
||||||
isOpen={showStatusChangeModal}
|
isOpen={showStatusChangeModal}
|
||||||
onClose={() => setShowStatusChangeModal(false)}
|
onClose={() => setShowStatusChangeModal(false)}
|
||||||
onSave={handleStatusChangeSave}
|
onSave={handleStatusChangeSave}
|
||||||
notifications={notifications.filter((n) =>
|
notifications={notifications.filter((n) => selectedNotifications.includes(n.id))}
|
||||||
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 {
|
import {
|
||||||
FaCalendar,
|
FaCalendar,
|
||||||
FaClock,
|
FaClock,
|
||||||
|
|
@ -8,124 +8,109 @@ import {
|
||||||
FaChevronRight,
|
FaChevronRight,
|
||||||
FaEdit,
|
FaEdit,
|
||||||
FaUser,
|
FaUser,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import {
|
import { WorkOrderStatusEnum, CalendarView, PmCalendarEvent } from '../../../types/pm'
|
||||||
WorkOrderStatusEnum,
|
import { mockCalendarEvents } from '../../../mocks/mockMaintenanceCalendarEvent'
|
||||||
CalendarView,
|
import NewCalendarEventModal from './NewCalendarEventModal'
|
||||||
PmCalendarEvent,
|
|
||||||
} from "../../../types/pm";
|
|
||||||
import { mockCalendarEvents } from "../../../mocks/mockMaintenanceCalendarEvent";
|
|
||||||
import NewCalendarEventModal from "./NewCalendarEventModal";
|
|
||||||
import {
|
import {
|
||||||
getPriorityColor,
|
getPriorityColor,
|
||||||
getWorkOrderStatusColor,
|
getWorkOrderStatusColor,
|
||||||
getWorkOrderStatusIcon,
|
getWorkOrderStatusIcon,
|
||||||
} from "../../../utils/erp";
|
} from '../../../utils/erp'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const MaintenanceCalendar: React.FC = () => {
|
const MaintenanceCalendar: React.FC = () => {
|
||||||
const [currentDate, setCurrentDate] = useState(new Date());
|
const [currentDate, setCurrentDate] = useState(new Date())
|
||||||
const [view, setView] = useState<CalendarView>("month");
|
const [view, setView] = useState<CalendarView>('month')
|
||||||
const [showEventDetailModal, setShowEventDetailModal] = useState(false);
|
const [showEventDetailModal, setShowEventDetailModal] = useState(false)
|
||||||
const [showNewEventModal, setShowNewEventModal] = useState(false);
|
const [showNewEventModal, setShowNewEventModal] = useState(false)
|
||||||
const [selectedEvent, setSelectedEvent] = useState<PmCalendarEvent | null>(
|
const [selectedEvent, setSelectedEvent] = useState<PmCalendarEvent | null>(null)
|
||||||
null
|
const [selectedDate, setSelectedDate] = useState<Date | null>(null)
|
||||||
);
|
const [statusFilter, setStatusFilter] = useState<'all' | WorkOrderStatusEnum>('all')
|
||||||
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
|
||||||
const [statusFilter, setStatusFilter] = useState<"all" | WorkOrderStatusEnum>(
|
|
||||||
"all"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Mock data - replace with actual API calls
|
// Mock data - replace with actual API calls
|
||||||
const [events, setEvents] = useState<PmCalendarEvent[]>(mockCalendarEvents);
|
const [events, setEvents] = useState<PmCalendarEvent[]>(mockCalendarEvents)
|
||||||
|
|
||||||
const getDaysInMonth = (date: Date) => {
|
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) => {
|
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) => {
|
const formatDate = (date: Date) => {
|
||||||
return date.toLocaleDateString("tr-TR", {
|
return date.toLocaleDateString('tr-TR', {
|
||||||
year: "numeric",
|
year: 'numeric',
|
||||||
month: "long",
|
month: 'long',
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const getEventsForDate = (date: Date) => {
|
const getEventsForDate = (date: Date) => {
|
||||||
return events.filter((event) => {
|
return events.filter((event) => {
|
||||||
const eventDate = new Date(event.date);
|
const eventDate = new Date(event.date)
|
||||||
return eventDate.toDateString() === date.toDateString();
|
return eventDate.toDateString() === date.toDateString()
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const navigateMonth = (direction: "prev" | "next") => {
|
const navigateMonth = (direction: 'prev' | 'next') => {
|
||||||
const newDate = new Date(currentDate);
|
const newDate = new Date(currentDate)
|
||||||
if (direction === "prev") {
|
if (direction === 'prev') {
|
||||||
newDate.setMonth(newDate.getMonth() - 1);
|
newDate.setMonth(newDate.getMonth() - 1)
|
||||||
} else {
|
} else {
|
||||||
newDate.setMonth(newDate.getMonth() + 1);
|
newDate.setMonth(newDate.getMonth() + 1)
|
||||||
}
|
}
|
||||||
setCurrentDate(newDate);
|
setCurrentDate(newDate)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleEventClick = (event: PmCalendarEvent) => {
|
const handleEventClick = (event: PmCalendarEvent) => {
|
||||||
setSelectedEvent(event);
|
setSelectedEvent(event)
|
||||||
setShowEventDetailModal(true);
|
setShowEventDetailModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleDayClick = (date: Date) => {
|
const handleDayClick = (date: Date) => {
|
||||||
setSelectedDate(date);
|
setSelectedDate(date)
|
||||||
setShowNewEventModal(true);
|
setShowNewEventModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleNewEventSave = (newEvent: Partial<PmCalendarEvent>) => {
|
const handleNewEventSave = (newEvent: Partial<PmCalendarEvent>) => {
|
||||||
if (newEvent.id) {
|
if (newEvent.id) {
|
||||||
setEvents((prevEvents) => [...prevEvents, newEvent as PmCalendarEvent]);
|
setEvents((prevEvents) => [...prevEvents, newEvent as PmCalendarEvent])
|
||||||
}
|
}
|
||||||
setShowNewEventModal(false);
|
setShowNewEventModal(false)
|
||||||
setSelectedDate(null);
|
setSelectedDate(null)
|
||||||
};
|
}
|
||||||
|
|
||||||
const renderMonthView = () => {
|
const renderMonthView = () => {
|
||||||
const daysInMonth = getDaysInMonth(currentDate);
|
const daysInMonth = getDaysInMonth(currentDate)
|
||||||
const firstDay = getFirstDayOfMonth(currentDate);
|
const firstDay = getFirstDayOfMonth(currentDate)
|
||||||
const days = [];
|
const days = []
|
||||||
|
|
||||||
// Empty cells for days before the first day of the month
|
// Empty cells for days before the first day of the month
|
||||||
for (let i = 0; i < firstDay; i++) {
|
for (let i = 0; i < firstDay; i++) {
|
||||||
days.push(
|
days.push(<div key={`empty-${i}`} className="p-1 border border-gray-200"></div>)
|
||||||
<div key={`empty-${i}`} className="p-1 border border-gray-200"></div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Days of the month
|
// Days of the month
|
||||||
for (let day = 1; day <= daysInMonth; day++) {
|
for (let day = 1; day <= daysInMonth; day++) {
|
||||||
const date = new Date(
|
const date = new Date(currentDate.getFullYear(), currentDate.getMonth(), day)
|
||||||
currentDate.getFullYear(),
|
const dayEvents = getEventsForDate(date)
|
||||||
currentDate.getMonth(),
|
|
||||||
day
|
|
||||||
);
|
|
||||||
const dayEvents = getEventsForDate(date);
|
|
||||||
const filteredDayEvents = dayEvents.filter(
|
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(
|
days.push(
|
||||||
<div
|
<div
|
||||||
key={day}
|
key={day}
|
||||||
className={`p-1.5 border border-gray-200 min-h-[100px] cursor-pointer hover:bg-gray-50 ${
|
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)}
|
onClick={() => handleDayClick(date)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`text-xs font-medium mb-1 ${
|
className={`text-xs font-medium mb-1 ${isToday ? 'text-blue-600' : 'text-gray-900'}`}
|
||||||
isToday ? "text-blue-600" : "text-gray-900"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{day}
|
{day}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -134,11 +119,11 @@ const MaintenanceCalendar: React.FC = () => {
|
||||||
<div
|
<div
|
||||||
key={event.id}
|
key={event.id}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation()
|
||||||
handleEventClick(event);
|
handleEventClick(event)
|
||||||
}}
|
}}
|
||||||
className={`text-xs p-0.5 rounded border-l-2 cursor-pointer hover:bg-gray-50 ${getPriorityColor(
|
className={`text-xs p-0.5 rounded border-l-2 cursor-pointer hover:bg-gray-50 ${getPriorityColor(
|
||||||
event.priority
|
event.priority,
|
||||||
)} ${getWorkOrderStatusColor(event.status)}`}
|
)} ${getWorkOrderStatusColor(event.status)}`}
|
||||||
>
|
>
|
||||||
<div className="font-medium truncate">{event.title}</div>
|
<div className="font-medium truncate">{event.title}</div>
|
||||||
|
|
@ -146,9 +131,7 @@ const MaintenanceCalendar: React.FC = () => {
|
||||||
{getWorkOrderStatusIcon(event.status)}
|
{getWorkOrderStatusIcon(event.status)}
|
||||||
<span>{event.startTime}</span>
|
<span>{event.startTime}</span>
|
||||||
{event.workCenterCode && (
|
{event.workCenterCode && (
|
||||||
<span className="text-gray-500">
|
<span className="text-gray-500">({event.workCenterCode})</span>
|
||||||
({event.workCenterCode})
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -159,14 +142,14 @@ const MaintenanceCalendar: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-7 gap-0 bg-white rounded-lg shadow overflow-hidden">
|
<div className="grid grid-cols-7 gap-0 bg-white rounded-lg shadow overflow-hidden">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
{["Pzt", "Sal", "Çar", "Per", "Cum", "Cmt", "Paz"].map((day) => (
|
{['Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt', 'Paz'].map((day) => (
|
||||||
<div
|
<div
|
||||||
key={day}
|
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"
|
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}
|
{days}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const renderWeekView = () => {
|
const renderWeekView = () => {
|
||||||
const startOfWeek = new Date(currentDate);
|
const startOfWeek = new Date(currentDate)
|
||||||
startOfWeek.setDate(currentDate.getDate() - currentDate.getDay() + 1); // Start from Monday
|
startOfWeek.setDate(currentDate.getDate() - currentDate.getDay() + 1) // Start from Monday
|
||||||
|
|
||||||
const weekDays: Date[] = [];
|
const weekDays: Date[] = []
|
||||||
for (let i = 0; i < 7; i++) {
|
for (let i = 0; i < 7; i++) {
|
||||||
const date = new Date(startOfWeek);
|
const date = new Date(startOfWeek)
|
||||||
date.setDate(startOfWeek.getDate() + i);
|
date.setDate(startOfWeek.getDate() + i)
|
||||||
weekDays.push(date);
|
weekDays.push(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||||
<div className="grid grid-cols-8 border-b border-gray-200">
|
<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">
|
<div className="p-2 bg-gray-50 text-xs font-medium text-gray-700">Saat</div>
|
||||||
Saat
|
|
||||||
</div>
|
|
||||||
{weekDays.map((date, index) => (
|
{weekDays.map((date, index) => (
|
||||||
<div
|
<div key={index} className="p-2 bg-gray-50 text-center border-l border-gray-200">
|
||||||
key={index}
|
|
||||||
className="p-2 bg-gray-50 text-center border-l border-gray-200"
|
|
||||||
>
|
|
||||||
<div className="text-sm font-medium text-gray-700">
|
<div className="text-sm font-medium text-gray-700">
|
||||||
{date.toLocaleDateString("tr-TR", { weekday: "short" })}
|
{date.toLocaleDateString('tr-TR', { weekday: 'short' })}
|
||||||
</div>
|
|
||||||
<div className="text-lg font-bold text-gray-900">
|
|
||||||
{date.getDate()}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-lg font-bold text-gray-900">{date.getDate()}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="max-h-96 overflow-y-auto">
|
<div className="max-h-96 overflow-y-auto">
|
||||||
{Array.from({ length: 12 }, (_, hour) => hour + 8).map((hour) => (
|
{Array.from({ length: 12 }, (_, hour) => hour + 8).map((hour) => (
|
||||||
<div
|
<div key={hour} className="grid grid-cols-8 border-b border-gray-100">
|
||||||
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">
|
<div className="p-1.5 text-xs text-gray-500 bg-gray-50 border-r border-gray-200">
|
||||||
{hour}:00
|
{hour}:00
|
||||||
</div>
|
</div>
|
||||||
{weekDays.map((date, dayIndex) => {
|
{weekDays.map((date, dayIndex) => {
|
||||||
const dayEvents = getEventsForDate(date).filter((event) => {
|
const dayEvents = getEventsForDate(date).filter((event) => {
|
||||||
const eventHour = parseInt(
|
const eventHour = parseInt(event.startTime?.split(':')[0] || '0')
|
||||||
event.startTime?.split(":")[0] || "0"
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
eventHour === hour &&
|
eventHour === hour && (statusFilter === 'all' || event.status === statusFilter)
|
||||||
(statusFilter === "all" || event.status === statusFilter)
|
)
|
||||||
);
|
})
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={dayIndex}
|
key={dayIndex}
|
||||||
className="p-1 border-l border-gray-200 min-h-[50px] cursor-pointer hover:bg-gray-50"
|
className="p-1 border-l border-gray-200 min-h-[50px] cursor-pointer hover:bg-gray-50"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const selectedDateTime = new Date(date);
|
const selectedDateTime = new Date(date)
|
||||||
selectedDateTime.setHours(hour, 0, 0, 0);
|
selectedDateTime.setHours(hour, 0, 0, 0)
|
||||||
handleDayClick(selectedDateTime);
|
handleDayClick(selectedDateTime)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{dayEvents.map((event) => (
|
{dayEvents.map((event) => (
|
||||||
<div
|
<div
|
||||||
key={event.id}
|
key={event.id}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation()
|
||||||
handleEventClick(event);
|
handleEventClick(event)
|
||||||
}}
|
}}
|
||||||
className={`text-xs p-1 rounded mb-1 border-l-2 cursor-pointer hover:bg-gray-50 ${getPriorityColor(
|
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)}`}
|
)} ${getWorkOrderStatusColor(event.status)}`}
|
||||||
>
|
>
|
||||||
<div className="font-medium truncate">
|
<div className="font-medium truncate">{event.title}</div>
|
||||||
{event.title}
|
|
||||||
</div>
|
|
||||||
<div className="text-gray-500">
|
<div className="text-gray-500">
|
||||||
{event.startTime}-{event.endTime}
|
{event.startTime}-{event.endTime}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const getTodayEvents = () => {
|
const getTodayEvents = () => {
|
||||||
const today = new Date();
|
const today = new Date()
|
||||||
return getEventsForDate(today).filter(
|
return getEventsForDate(today).filter(
|
||||||
(event) => statusFilter === "all" || event.status === statusFilter
|
(event) => statusFilter === 'all' || event.status === statusFilter,
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 pt-2">
|
<Container>
|
||||||
{/* Header */}
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
{/* Header */}
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-2xl font-bold text-gray-900">Bakım Takvimi</h2>
|
<div>
|
||||||
<p className="text-gray-600">
|
<h2 className="text-2xl font-bold text-gray-900">Bakım Takvimi</h2>
|
||||||
Bakım planları ve iş emirlerini takip edin. Yeni planlama için
|
<p className="text-gray-600">
|
||||||
gün/saat seçin.
|
Bakım planları ve iş emirlerini takip edin. Yeni planlama için gün/saat seçin.
|
||||||
</p>
|
</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>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setCurrentDate(new Date())}
|
onClick={() => setShowNewEventModal(true)}
|
||||||
className="px-3 py-1.5 text-sm text-blue-600 hover:bg-blue-50 rounded-lg"
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Calendar View */}
|
{/* Calendar Controls */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4">
|
<div className="flex items-center justify-between">
|
||||||
<div className="lg:col-span-3">
|
<div className="flex items-center space-x-4">
|
||||||
{view === "month" && renderMonthView()}
|
<div className="flex items-center space-x-1">
|
||||||
{view === "week" && renderWeekView()}
|
<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>
|
</div>
|
||||||
|
|
||||||
{/* Today's Events Sidebar */}
|
{/* Calendar View */}
|
||||||
<div className="bg-white rounded-lg shadow p-4">
|
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4">
|
||||||
<h4 className="text-base font-semibold text-gray-900 mb-3">
|
<div className="lg:col-span-3">
|
||||||
Bugünün Etkinlikleri
|
{view === 'month' && renderMonthView()}
|
||||||
</h4>
|
{view === 'week' && renderWeekView()}
|
||||||
<div className="space-y-3">
|
</div>
|
||||||
{getTodayEvents().map((event) => (
|
|
||||||
<div
|
{/* Today's Events Sidebar */}
|
||||||
key={event.id}
|
<div className="bg-white rounded-lg shadow p-4">
|
||||||
onClick={() => handleEventClick(event)}
|
<h4 className="text-base font-semibold text-gray-900 mb-3">Bugünün Etkinlikleri</h4>
|
||||||
className={`p-2 border-l-4 rounded-r-lg cursor-pointer hover:bg-gray-50 ${getPriorityColor(
|
<div className="space-y-3">
|
||||||
event.priority
|
{getTodayEvents().map((event) => (
|
||||||
)}`}
|
<div
|
||||||
>
|
key={event.id}
|
||||||
<div className="flex items-start justify-between">
|
onClick={() => handleEventClick(event)}
|
||||||
<div className="flex-1">
|
className={`p-2 border-l-4 rounded-r-lg cursor-pointer hover:bg-gray-50 ${getPriorityColor(
|
||||||
<h5 className="text-sm font-medium text-gray-900">
|
event.priority,
|
||||||
{event.title}
|
)}`}
|
||||||
</h5>
|
>
|
||||||
<p className="text-xs text-gray-500 mt-1">
|
<div className="flex items-start justify-between">
|
||||||
{event.workCenterCode}
|
<div className="flex-1">
|
||||||
</p>
|
<h5 className="text-sm font-medium text-gray-900">{event.title}</h5>
|
||||||
<div className="flex items-center space-x-2 mt-2">
|
<p className="text-xs text-gray-500 mt-1">{event.workCenterCode}</p>
|
||||||
<FaClock className="w-3 h-3 text-gray-400" />
|
<div className="flex items-center space-x-2 mt-2">
|
||||||
<span className="text-xs text-gray-500">
|
<FaClock className="w-3 h-3 text-gray-400" />
|
||||||
{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">
|
<span className="text-xs text-gray-500">
|
||||||
{event.assignedTo}
|
{event.startTime} - {event.endTime}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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>
|
</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>
|
||||||
</div>
|
))}
|
||||||
))}
|
{getTodayEvents().length === 0 && (
|
||||||
{getTodayEvents().length === 0 && (
|
<div className="text-center py-8">
|
||||||
<div className="text-center py-8">
|
<FaCalendar className="w-10 h-10 text-gray-400 mx-auto mb-2" />
|
||||||
<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>
|
||||||
<p className="text-sm text-gray-600">
|
</div>
|
||||||
Bugün için planlanan etkinlik yok
|
)}
|
||||||
</p>
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</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="bg-white rounded-lg p-4 w-full max-w-lg mx-4">
|
||||||
<div className="flex items-start justify-between mb-3">
|
<div className="flex items-start justify-between mb-3">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-semibold text-gray-900">
|
<h3 className="text-base font-semibold text-gray-900">{selectedEvent.title}</h3>
|
||||||
{selectedEvent.title}
|
<p className="text-sm text-gray-500 mt-1">{selectedEvent.workCenterCode}</p>
|
||||||
</h3>
|
|
||||||
<p className="text-sm text-gray-500 mt-1">
|
|
||||||
{selectedEvent.workCenterCode}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowEventDetailModal(false)}
|
onClick={() => setShowEventDetailModal(false)}
|
||||||
|
|
@ -458,46 +409,34 @@ const MaintenanceCalendar: React.FC = () => {
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Tarih & Saat</label>
|
||||||
Tarih & Saat
|
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900">
|
<p className="text-sm text-gray-900">
|
||||||
{selectedEvent.date.toLocaleDateString("tr-TR")} -{" "}
|
{selectedEvent.date.toLocaleDateString('tr-TR')} - {selectedEvent.startTime} /{' '}
|
||||||
{selectedEvent.startTime} / {selectedEvent.endTime}
|
{selectedEvent.endTime}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Süre</label>
|
||||||
Süre
|
<p className="text-sm text-gray-900">{selectedEvent.duration} dakika</p>
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900">
|
|
||||||
{selectedEvent.duration} dakika
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Atanan Kişi</label>
|
||||||
Atanan Kişi
|
<p className="text-sm text-gray-900">{selectedEvent.assignedTo || 'Atanmadı'}</p>
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900">
|
|
||||||
{selectedEvent.assignedTo || "Atanmadı"}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Durum</label>
|
||||||
Durum
|
|
||||||
</label>
|
|
||||||
<span
|
<span
|
||||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getWorkOrderStatusColor(
|
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getWorkOrderStatusColor(
|
||||||
selectedEvent.status
|
selectedEvent.status,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{selectedEvent.status === WorkOrderStatusEnum.Planned
|
{selectedEvent.status === WorkOrderStatusEnum.Planned
|
||||||
? "Planlandı"
|
? 'Planlandı'
|
||||||
: selectedEvent.status === WorkOrderStatusEnum.InProgress
|
: selectedEvent.status === WorkOrderStatusEnum.InProgress
|
||||||
? "Devam Ediyor"
|
? 'Devam Ediyor'
|
||||||
: selectedEvent.status === WorkOrderStatusEnum.Completed
|
: selectedEvent.status === WorkOrderStatusEnum.Completed
|
||||||
? "Tamamlandı"
|
? 'Tamamlandı'
|
||||||
: "Bekliyor"}
|
: 'Bekliyor'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -522,14 +461,14 @@ const MaintenanceCalendar: React.FC = () => {
|
||||||
<NewCalendarEventModal
|
<NewCalendarEventModal
|
||||||
isOpen={showNewEventModal}
|
isOpen={showNewEventModal}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setShowNewEventModal(false);
|
setShowNewEventModal(false)
|
||||||
setSelectedDate(null);
|
setSelectedDate(null)
|
||||||
}}
|
}}
|
||||||
onSave={handleNewEventSave}
|
onSave={handleNewEventSave}
|
||||||
selectedDate={selectedDate || undefined}
|
selectedDate={selectedDate || undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Container>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default MaintenanceCalendar;
|
export default MaintenanceCalendar
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaPlus } from "react-icons/fa";
|
import { FaTimes, FaSave, FaPlus } from 'react-icons/fa'
|
||||||
import {
|
import {
|
||||||
PmWorkCenter,
|
PmWorkCenter,
|
||||||
MaintenancePlanTypeEnum,
|
MaintenancePlanTypeEnum,
|
||||||
FrequencyUnitEnum,
|
FrequencyUnitEnum,
|
||||||
PmMaintenancePlan,
|
PmMaintenancePlan,
|
||||||
} from "../../../types/pm";
|
} from '../../../types/pm'
|
||||||
import { PriorityEnum } from "../../../types/common";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
|
|
||||||
interface MaintenancePlanModalProps {
|
interface MaintenancePlanModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (planData: PmMaintenancePlan[]) => void;
|
onSave: (planData: PmMaintenancePlan[]) => void
|
||||||
selectedWorkCenters: PmWorkCenter[];
|
selectedWorkCenters: PmWorkCenter[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
||||||
|
|
@ -23,55 +23,46 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const [planData, setPlanData] = useState({
|
const [planData, setPlanData] = useState({
|
||||||
planType: MaintenancePlanTypeEnum.Preventive,
|
planType: MaintenancePlanTypeEnum.Preventive,
|
||||||
description: "",
|
description: '',
|
||||||
frequency: 1,
|
frequency: 1,
|
||||||
frequencyUnit: FrequencyUnitEnum.Months,
|
frequencyUnit: FrequencyUnitEnum.Months,
|
||||||
estimatedDuration: 60,
|
estimatedDuration: 60,
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
instructions: "",
|
instructions: '',
|
||||||
requiredSkills: [] as string[],
|
requiredSkills: [] as string[],
|
||||||
nextDue: new Date(),
|
nextDue: new Date(),
|
||||||
});
|
})
|
||||||
|
|
||||||
const [newSkill, setNewSkill] = useState("");
|
const [newSkill, setNewSkill] = useState('')
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (
|
||||||
e: React.ChangeEvent<
|
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||||||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
|
||||||
>
|
|
||||||
) => {
|
) => {
|
||||||
const { name, value, type } = e.target;
|
const { name, value, type } = e.target
|
||||||
setPlanData((prev) => ({
|
setPlanData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[name]:
|
[name]: type === 'date' ? new Date(value) : type === 'number' ? Number(value) : value,
|
||||||
type === "date"
|
}))
|
||||||
? new Date(value)
|
}
|
||||||
: type === "number"
|
|
||||||
? Number(value)
|
|
||||||
: value,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const addSkill = () => {
|
const addSkill = () => {
|
||||||
if (newSkill.trim() && !planData.requiredSkills.includes(newSkill.trim())) {
|
if (newSkill.trim() && !planData.requiredSkills.includes(newSkill.trim())) {
|
||||||
setPlanData((prev) => ({
|
setPlanData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
requiredSkills: [...prev.requiredSkills, newSkill.trim()],
|
requiredSkills: [...prev.requiredSkills, newSkill.trim()],
|
||||||
}));
|
}))
|
||||||
setNewSkill("");
|
setNewSkill('')
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeSkill = (skillToRemove: string) => {
|
const removeSkill = (skillToRemove: string) => {
|
||||||
setPlanData((prev) => ({
|
setPlanData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
requiredSkills: prev.requiredSkills.filter(
|
requiredSkills: prev.requiredSkills.filter((skill) => skill !== skillToRemove),
|
||||||
(skill) => skill !== skillToRemove
|
}))
|
||||||
),
|
}
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
const plansToCreate = selectedWorkCenters.map((workCenter) => ({
|
const plansToCreate = selectedWorkCenters.map((workCenter) => ({
|
||||||
|
|
@ -84,11 +75,11 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
||||||
isActive: true,
|
isActive: true,
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
}));
|
}))
|
||||||
|
|
||||||
onSave(plansToCreate);
|
onSave(plansToCreate)
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<h2 className="text-lg font-semibold text-gray-900">
|
||||||
Bakım Planı Oluştur ({selectedWorkCenters.length} iş merkezi)
|
Bakım Planı Oluştur ({selectedWorkCenters.length} iş merkezi)
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button onClick={onClose} className="p-2 hover:bg-gray-100 rounded-lg transition-colors">
|
||||||
onClick={onClose}
|
|
||||||
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5 text-gray-500" />
|
<FaTimes className="w-5 h-5 text-gray-500" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -110,21 +98,14 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-4 space-y-4">
|
||||||
{/* Selected Work Center List */}
|
{/* Selected Work Center List */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
<h3 className="text-sm font-medium text-gray-900 mb-2">Seçili İş Merkezleri</h3>
|
||||||
Seçili İş Merkezleri
|
|
||||||
</h3>
|
|
||||||
<div className="max-h-28 overflow-y-auto bg-gray-50 rounded-lg p-2">
|
<div className="max-h-28 overflow-y-auto bg-gray-50 rounded-lg p-2">
|
||||||
{selectedWorkCenters.map((workCenter) => (
|
{selectedWorkCenters.map((workCenter) => (
|
||||||
<div
|
<div key={workCenter.id} className="flex items-center justify-between py-0.5">
|
||||||
key={workCenter.id}
|
|
||||||
className="flex items-center justify-between py-0.5"
|
|
||||||
>
|
|
||||||
<span className="text-sm text-gray-700">
|
<span className="text-sm text-gray-700">
|
||||||
{workCenter.code} - {workCenter.name}
|
{workCenter.code} - {workCenter.name}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-500">
|
<span className="text-xs text-gray-500">{workCenter.location}</span>
|
||||||
{workCenter.location}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -132,38 +113,24 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
||||||
|
|
||||||
{/* Plan Details */}
|
{/* Plan Details */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
<h3 className="text-sm font-medium text-gray-900 mb-2">Plan Detayları</h3>
|
||||||
Plan Detayları
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Plan Tipi</label>
|
||||||
Plan Tipi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="planType"
|
name="planType"
|
||||||
value={planData.planType}
|
value={planData.planType}
|
||||||
onChange={handleInputChange}
|
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"
|
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}>
|
<option value={MaintenancePlanTypeEnum.Preventive}>Önleyici Bakım</option>
|
||||||
Önleyici Bakım
|
<option value={MaintenancePlanTypeEnum.Predictive}>Kestirimci Bakım</option>
|
||||||
</option>
|
<option value={MaintenancePlanTypeEnum.Corrective}>Düzeltici Bakım</option>
|
||||||
<option value={MaintenancePlanTypeEnum.Predictive}>
|
<option value={MaintenancePlanTypeEnum.Condition}>Durum Bazlı Bakım</option>
|
||||||
Kestirimci Bakım
|
|
||||||
</option>
|
|
||||||
<option value={MaintenancePlanTypeEnum.Corrective}>
|
|
||||||
Düzeltici Bakım
|
|
||||||
</option>
|
|
||||||
<option value={MaintenancePlanTypeEnum.Condition}>
|
|
||||||
Durum Bazlı Bakım
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Öncelik</label>
|
||||||
Öncelik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="priority"
|
name="priority"
|
||||||
value={planData.priority}
|
value={planData.priority}
|
||||||
|
|
@ -191,7 +158,7 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
||||||
onChange={(e) => setNewSkill(e.target.value)}
|
onChange={(e) => setNewSkill(e.target.value)}
|
||||||
placeholder="Yetenek ekle..."
|
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"
|
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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -241,7 +208,7 @@ const MaintenancePlanModal: React.FC<MaintenancePlanModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default MaintenancePlanModal;
|
export default MaintenancePlanModal
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import {
|
import {
|
||||||
FaPlus,
|
FaPlus,
|
||||||
FaSearch,
|
FaSearch,
|
||||||
|
|
@ -10,489 +10,442 @@ import {
|
||||||
FaTrash,
|
FaTrash,
|
||||||
FaEye,
|
FaEye,
|
||||||
FaTasks,
|
FaTasks,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import {
|
import {
|
||||||
PmMaintenancePlan,
|
PmMaintenancePlan,
|
||||||
MaintenancePlanTypeEnum,
|
MaintenancePlanTypeEnum,
|
||||||
PmMaintenanceWorkOrder,
|
PmMaintenanceWorkOrder,
|
||||||
} from "../../../types/pm";
|
} from '../../../types/pm'
|
||||||
import { mockMaintenancePlans } from "../../../mocks/mockMaintenancePlans";
|
import { mockMaintenancePlans } from '../../../mocks/mockMaintenancePlans'
|
||||||
import NewMaintenancePlanModal from "./NewMaintenancePlanModal";
|
import NewMaintenancePlanModal from './NewMaintenancePlanModal'
|
||||||
import ViewMaintenancePlanModal from "./ViewMaintenancePlanModal";
|
import ViewMaintenancePlanModal from './ViewMaintenancePlanModal'
|
||||||
import EditMaintenancePlanModal from "./EditMaintenancePlanModal";
|
import EditMaintenancePlanModal from './EditMaintenancePlanModal'
|
||||||
import CreateWorkOrderModal from "./CreateWorkOrderModal";
|
import CreateWorkOrderModal from './CreateWorkOrderModal'
|
||||||
import PlanStatusChangeModal from "./PlanStatusChangeModal";
|
import PlanStatusChangeModal from './PlanStatusChangeModal'
|
||||||
import Widget from "../../../components/common/Widget";
|
import Widget from '../../../components/common/Widget'
|
||||||
import { PriorityEnum } from "../../../types/common";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
import {
|
import {
|
||||||
getFrequencyUnitText,
|
getFrequencyUnitText,
|
||||||
getMaintenancePlanTypeColor,
|
getMaintenancePlanTypeColor,
|
||||||
getMaintenancePlanTypeText,
|
getMaintenancePlanTypeText,
|
||||||
getPriorityColor,
|
getPriorityColor,
|
||||||
} from "../../../utils/erp";
|
} from '../../../utils/erp'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
|
||||||
const MaintenancePlans: React.FC = () => {
|
const MaintenancePlans: React.FC = () => {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [typeFilter, setTypeFilter] = useState<MaintenancePlanTypeEnum | "all">(
|
const [typeFilter, setTypeFilter] = useState<MaintenancePlanTypeEnum | 'all'>('all')
|
||||||
"all"
|
const [statusFilter, setStatusFilter] = useState<'active' | 'inactive' | 'all'>('all')
|
||||||
);
|
const [editingPlan, setEditingPlan] = useState<PmMaintenancePlan | null>(null)
|
||||||
const [statusFilter, setStatusFilter] = useState<
|
const [selectedPlans, setSelectedPlans] = useState<string[]>([])
|
||||||
"active" | "inactive" | "all"
|
|
||||||
>("all");
|
|
||||||
const [editingPlan, setEditingPlan] = useState<PmMaintenancePlan | null>(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
const [selectedPlans, setSelectedPlans] = useState<string[]>([]);
|
|
||||||
|
|
||||||
// Modal states
|
// Modal states
|
||||||
const [showNewModal, setShowNewModal] = useState(false);
|
const [showNewModal, setShowNewModal] = useState(false)
|
||||||
const [showViewModal, setShowViewModal] = useState(false);
|
const [showViewModal, setShowViewModal] = useState(false)
|
||||||
const [showEditModal, setShowEditModal] = useState(false);
|
const [showEditModal, setShowEditModal] = useState(false)
|
||||||
const [showWorkOrderModal, setShowWorkOrderModal] = useState(false);
|
const [showWorkOrderModal, setShowWorkOrderModal] = useState(false)
|
||||||
const [showStatusModal, setShowStatusModal] = useState(false);
|
const [showStatusModal, setShowStatusModal] = useState(false)
|
||||||
const [viewingPlan, setViewingPlan] = useState<PmMaintenancePlan | null>(
|
const [viewingPlan, setViewingPlan] = useState<PmMaintenancePlan | null>(null)
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
// Mock data - replace with actual API calls
|
// Mock data - replace with actual API calls
|
||||||
const [plans] = useState<PmMaintenancePlan[]>(mockMaintenancePlans);
|
const [plans] = useState<PmMaintenancePlan[]>(mockMaintenancePlans)
|
||||||
|
|
||||||
const filteredPlans = plans.filter((plan) => {
|
const filteredPlans = plans.filter((plan) => {
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
plan.planCode.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
plan.planCode.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
plan.description.toLowerCase().includes(searchTerm.toLowerCase());
|
plan.description.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
const matchesType = typeFilter === "all" || plan.planType === typeFilter;
|
const matchesType = typeFilter === 'all' || plan.planType === typeFilter
|
||||||
const matchesStatus =
|
const matchesStatus =
|
||||||
statusFilter === "all" ||
|
statusFilter === 'all' ||
|
||||||
(statusFilter === "active" && plan.isActive) ||
|
(statusFilter === 'active' && plan.isActive) ||
|
||||||
(statusFilter === "inactive" && !plan.isActive);
|
(statusFilter === 'inactive' && !plan.isActive)
|
||||||
return matchesSearch && matchesType && matchesStatus;
|
return matchesSearch && matchesType && matchesStatus
|
||||||
});
|
})
|
||||||
|
|
||||||
const isOverdue = (plan: PmMaintenancePlan) => {
|
const isOverdue = (plan: PmMaintenancePlan) => {
|
||||||
if (!plan.nextDue) return false;
|
if (!plan.nextDue) return false
|
||||||
return plan.nextDue < new Date();
|
return plan.nextDue < new Date()
|
||||||
};
|
}
|
||||||
|
|
||||||
const isDueSoon = (plan: PmMaintenancePlan) => {
|
const isDueSoon = (plan: PmMaintenancePlan) => {
|
||||||
if (!plan.nextDue) return false;
|
if (!plan.nextDue) return false
|
||||||
const today = new Date();
|
const today = new Date()
|
||||||
const diffTime = plan.nextDue.getTime() - today.getTime();
|
const diffTime = plan.nextDue.getTime() - today.getTime()
|
||||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
||||||
return diffDays <= 7 && diffDays > 0;
|
return diffDays <= 7 && diffDays > 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const getTotalMaterialCost = (plan: PmMaintenancePlan) => {
|
const getTotalMaterialCost = (plan: PmMaintenancePlan) => {
|
||||||
return plan.priority;
|
return plan.priority
|
||||||
};
|
}
|
||||||
|
|
||||||
// Event handlers
|
// Event handlers
|
||||||
const handleAddPlan = () => {
|
const handleAddPlan = () => {
|
||||||
setShowNewModal(true);
|
setShowNewModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleEdit = (plan: PmMaintenancePlan) => {
|
const handleEdit = (plan: PmMaintenancePlan) => {
|
||||||
setEditingPlan(plan);
|
setEditingPlan(plan)
|
||||||
setShowEditModal(true);
|
setShowEditModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleView = (plan: PmMaintenancePlan) => {
|
const handleView = (plan: PmMaintenancePlan) => {
|
||||||
setViewingPlan(plan);
|
setViewingPlan(plan)
|
||||||
setShowViewModal(true);
|
setShowViewModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSelectPlan = (planId: string) => {
|
const handleSelectPlan = (planId: string) => {
|
||||||
setSelectedPlans((prev) =>
|
setSelectedPlans((prev) =>
|
||||||
prev.includes(planId)
|
prev.includes(planId) ? prev.filter((id) => id !== planId) : [...prev, planId],
|
||||||
? prev.filter((id) => id !== planId)
|
)
|
||||||
: [...prev, planId]
|
}
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCreateWorkOrder = () => {
|
const handleCreateWorkOrder = () => {
|
||||||
setShowWorkOrderModal(true);
|
setShowWorkOrderModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleStatusChange = () => {
|
const handleStatusChange = () => {
|
||||||
setShowStatusModal(true);
|
setShowStatusModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
// Modal handlers
|
// Modal handlers
|
||||||
const handleSaveNewPlan = (newPlan: Partial<PmMaintenancePlan>) => {
|
const handleSaveNewPlan = (newPlan: Partial<PmMaintenancePlan>) => {
|
||||||
console.log("New plan:", newPlan);
|
console.log('New plan:', newPlan)
|
||||||
// TODO: API call to save new plan
|
// TODO: API call to save new plan
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSaveEditPlan = (updatedPlan: PmMaintenancePlan) => {
|
const handleSaveEditPlan = (updatedPlan: PmMaintenancePlan) => {
|
||||||
console.log("Updated plan:", updatedPlan);
|
console.log('Updated plan:', updatedPlan)
|
||||||
// TODO: API call to update plan
|
// TODO: API call to update plan
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSaveWorkOrders = (
|
const handleSaveWorkOrders = (workOrders: Partial<PmMaintenanceWorkOrder>[]) => {
|
||||||
workOrders: Partial<PmMaintenanceWorkOrder>[]
|
console.log('Work orders:', workOrders)
|
||||||
) => {
|
|
||||||
console.log("Work orders:", workOrders);
|
|
||||||
// TODO: API call to create work orders
|
// TODO: API call to create work orders
|
||||||
setSelectedPlans([]);
|
setSelectedPlans([])
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSaveStatusChange = (
|
const handleSaveStatusChange = (planIds: string[], isActive: boolean, reason: string) => {
|
||||||
planIds: string[],
|
console.log('Status change:', { planIds, isActive, reason })
|
||||||
isActive: boolean,
|
|
||||||
reason: string
|
|
||||||
) => {
|
|
||||||
console.log("Status change:", { planIds, isActive, reason });
|
|
||||||
// TODO: API call to update plan statuses
|
// TODO: API call to update plan statuses
|
||||||
setSelectedPlans([]);
|
setSelectedPlans([])
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 pt-2">
|
<Container>
|
||||||
{/* Header */}
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
{/* Header */}
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-2xl font-bold text-gray-900">Bakım Planları</h2>
|
<div>
|
||||||
<p className="text-gray-600">
|
<h2 className="text-2xl font-bold text-gray-900">Bakım Planları</h2>
|
||||||
Periyodik ve düzeltici bakım planlarını yönetin
|
<p className="text-gray-600">Periyodik ve düzeltici bakım planlarını yönetin</p>
|
||||||
</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>
|
</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 */}
|
{/* Summary Cards */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||||
<Widget
|
<Widget title="Toplam Plan" value={plans.length} color="blue" icon="FaCalendar" />
|
||||||
title="Toplam Plan"
|
|
||||||
value={plans.length}
|
|
||||||
color="blue"
|
|
||||||
icon="FaCalendar"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Widget
|
<Widget
|
||||||
title="Aktif Plan"
|
title="Aktif Plan"
|
||||||
value={plans.filter((p) => p.isActive).length}
|
value={plans.filter((p) => p.isActive).length}
|
||||||
color="green"
|
color="green"
|
||||||
icon="FaCheckCircle"
|
icon="FaCheckCircle"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Widget
|
<Widget
|
||||||
title="Geciken"
|
title="Geciken"
|
||||||
value={plans.filter((p) => isOverdue(p)).length}
|
value={plans.filter((p) => isOverdue(p)).length}
|
||||||
color="red"
|
color="red"
|
||||||
icon="FaExclamationTriangle"
|
icon="FaExclamationTriangle"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Widget
|
<Widget
|
||||||
title="Bu Hafta"
|
title="Bu Hafta"
|
||||||
value={plans.filter((p) => isDueSoon(p)).length}
|
value={plans.filter((p) => isDueSoon(p)).length}
|
||||||
color="orange"
|
color="orange"
|
||||||
icon="FaClock"
|
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"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</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 */}
|
{/* Filters */}
|
||||||
<div className="bg-white rounded-lg shadow-md overflow-hidden">
|
<div className="flex space-x-3">
|
||||||
<div className="overflow-x-auto">
|
<div className="flex-1 relative">
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||||
<thead className="bg-gray-50">
|
<input
|
||||||
<tr>
|
type="text"
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
placeholder="Plan ara..."
|
||||||
<input
|
value={searchTerm}
|
||||||
type="checkbox"
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
checked={
|
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"
|
||||||
selectedPlans.length === filteredPlans.length &&
|
/>
|
||||||
filteredPlans.length > 0
|
</div>
|
||||||
}
|
<div className="relative">
|
||||||
onChange={(e) => {
|
<FaFilter className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||||
if (e.target.checked) {
|
<select
|
||||||
setSelectedPlans(filteredPlans.map((p) => p.id));
|
value={typeFilter}
|
||||||
} else {
|
onChange={(e) => setTypeFilter(e.target.value as MaintenancePlanTypeEnum | 'all')}
|
||||||
setSelectedPlans([]);
|
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>
|
||||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
<option value={MaintenancePlanTypeEnum.Preventive}>Önleyici</option>
|
||||||
/>
|
<option value={MaintenancePlanTypeEnum.Corrective}>Düzeltici</option>
|
||||||
</th>
|
<option value={MaintenancePlanTypeEnum.Predictive}>Tahminsel</option>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<option value={MaintenancePlanTypeEnum.Condition}>Duruma Bağlı</option>
|
||||||
Plan Bilgileri
|
</select>
|
||||||
</th>
|
</div>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<div className="relative">
|
||||||
İş Merkezi
|
<select
|
||||||
</th>
|
value={statusFilter}
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
onChange={(e) => setStatusFilter(e.target.value as 'active' | 'inactive' | 'all')}
|
||||||
Tip/Öncelik
|
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"
|
||||||
</th>
|
>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<option value="all">Tüm Durumlar</option>
|
||||||
Sıklık
|
<option value="active">Aktif</option>
|
||||||
</th>
|
<option value="inactive">Pasif</option>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
</select>
|
||||||
Sonraki Bakım
|
</div>
|
||||||
</th>
|
</div>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Maliyet
|
{/* Plans Table */}
|
||||||
</th>
|
<div className="bg-white rounded-lg shadow-md overflow-hidden">
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<div className="overflow-x-auto">
|
||||||
Durum
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
</th>
|
<thead className="bg-gray-50">
|
||||||
<th className="px-4 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<tr>
|
||||||
İşlemler
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
</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
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={selectedPlans.includes(plan.id)}
|
checked={
|
||||||
onChange={() => handleSelectPlan(plan.id)}
|
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"
|
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
</td>
|
</th>
|
||||||
<td className="px-4 py-3 whitespace-nowrap">
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
<div className="flex items-center">
|
Plan Bilgileri
|
||||||
<div>
|
</th>
|
||||||
<div className="text-sm font-medium text-gray-900">
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
{plan.planCode}
|
İş Merkezi
|
||||||
</div>
|
</th>
|
||||||
<div className="text-sm text-gray-500">
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
{plan.description}
|
Tip/Öncelik
|
||||||
</div>
|
</th>
|
||||||
<div className="text-xs text-gray-400 mt-1">
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
{plan.description}
|
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>
|
</div>
|
||||||
</div>
|
</td>
|
||||||
</td>
|
<td className="px-4 py-3 whitespace-nowrap">
|
||||||
<td className="px-4 py-3 whitespace-nowrap">
|
<div className="text-sm text-gray-900">{plan.workCenterId}</div>
|
||||||
<div className="text-sm text-gray-900">
|
<div className="text-sm text-gray-500">İş Merkezi Adı</div>
|
||||||
{plan.workCenterId}
|
</td>
|
||||||
</div>
|
<td className="px-4 py-3 whitespace-nowrap">
|
||||||
<div className="text-sm text-gray-500">İş Merkezi Adı</div>
|
<div className="space-y-1">
|
||||||
</td>
|
<span
|
||||||
<td className="px-4 py-3 whitespace-nowrap">
|
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getMaintenancePlanTypeColor(
|
||||||
<div className="space-y-1">
|
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
|
<span
|
||||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getMaintenancePlanTypeColor(
|
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||||
plan.planType
|
plan.isActive
|
||||||
)}`}
|
? 'bg-green-100 text-green-800'
|
||||||
>
|
: 'bg-gray-100 text-gray-800'
|
||||||
{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")}
|
{plan.isActive ? 'Aktif' : 'Pasif'}
|
||||||
{isOverdue(plan) && (
|
</span>
|
||||||
<div className="text-xs">GECİKMİŞ</div>
|
</td>
|
||||||
)}
|
<td className="px-4 py-3 whitespace-nowrap text-right text-sm font-medium">
|
||||||
{isDueSoon(plan) && !isOverdue(plan) && (
|
<div className="flex items-center justify-end space-x-2">
|
||||||
<div className="text-xs">YAKINDA</div>
|
<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>
|
</div>
|
||||||
) : (
|
</td>
|
||||||
<span className="text-sm text-gray-400">-</span>
|
</tr>
|
||||||
)}
|
))}
|
||||||
</td>
|
</tbody>
|
||||||
<td className="px-4 py-3 whitespace-nowrap">
|
</table>
|
||||||
<div className="text-sm text-gray-900">
|
</div>
|
||||||
₺{getTotalMaterialCost(plan).toLocaleString()}
|
|
||||||
</div>
|
{filteredPlans.length === 0 && (
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-center py-12">
|
||||||
{plan.requiredMaterials.length} malzeme
|
<FaCalendar className="w-16 h-16 text-gray-400 mx-auto mb-4" />
|
||||||
</div>
|
<h3 className="text-lg font-medium text-gray-900 mb-2">Plan bulunamadı</h3>
|
||||||
</td>
|
<p className="text-gray-500 mb-4">
|
||||||
<td className="px-4 py-3 whitespace-nowrap">
|
Arama kriterlerinizi değiştirin veya yeni bir plan oluşturun.
|
||||||
<span
|
</p>
|
||||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
<button
|
||||||
plan.isActive
|
onClick={handleAddPlan}
|
||||||
? "bg-green-100 text-green-800"
|
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
|
||||||
: "bg-gray-100 text-gray-800"
|
>
|
||||||
}`}
|
Yeni Plan Oluştur
|
||||||
>
|
</button>
|
||||||
{plan.isActive ? "Aktif" : "Pasif"}
|
</div>
|
||||||
</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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{filteredPlans.length === 0 && (
|
{/* Bulk Actions */}
|
||||||
<div className="text-center py-12">
|
{selectedPlans.length > 0 && (
|
||||||
<FaCalendar className="w-16 h-16 text-gray-400 mx-auto mb-4" />
|
<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">
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
<div className="flex items-center space-x-4">
|
||||||
Plan bulunamadı
|
<span className="text-sm text-gray-600">{selectedPlans.length} plan seçildi</span>
|
||||||
</h3>
|
<div className="flex space-x-2">
|
||||||
<p className="text-gray-500 mb-4">
|
<button
|
||||||
Arama kriterlerinizi değiştirin veya yeni bir plan oluşturun.
|
onClick={handleCreateWorkOrder}
|
||||||
</p>
|
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"
|
||||||
<button
|
>
|
||||||
onClick={handleAddPlan}
|
<FaTasks className="w-4 h-4" />
|
||||||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
|
<span>İş Emri Oluştur</span>
|
||||||
>
|
</button>
|
||||||
Yeni Plan Oluştur
|
<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>
|
||||||
)}
|
)}
|
||||||
</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 */}
|
{/* Modals */}
|
||||||
<NewMaintenancePlanModal
|
<NewMaintenancePlanModal
|
||||||
isOpen={showNewModal}
|
isOpen={showNewModal}
|
||||||
|
|
@ -517,21 +470,17 @@ const MaintenancePlans: React.FC = () => {
|
||||||
isOpen={showWorkOrderModal}
|
isOpen={showWorkOrderModal}
|
||||||
onClose={() => setShowWorkOrderModal(false)}
|
onClose={() => setShowWorkOrderModal(false)}
|
||||||
onSave={handleSaveWorkOrders}
|
onSave={handleSaveWorkOrders}
|
||||||
selectedPlans={selectedPlans
|
selectedPlans={selectedPlans.map((id) => plans.find((p) => p.id === id)!).filter(Boolean)}
|
||||||
.map((id) => plans.find((p) => p.id === id)!)
|
|
||||||
.filter(Boolean)}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PlanStatusChangeModal
|
<PlanStatusChangeModal
|
||||||
isOpen={showStatusModal}
|
isOpen={showStatusModal}
|
||||||
onClose={() => setShowStatusModal(false)}
|
onClose={() => setShowStatusModal(false)}
|
||||||
onSave={handleSaveStatusChange}
|
onSave={handleSaveStatusChange}
|
||||||
selectedPlans={selectedPlans
|
selectedPlans={selectedPlans.map((id) => plans.find((p) => p.id === id)!).filter(Boolean)}
|
||||||
.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 {
|
import {
|
||||||
FaPlus,
|
FaPlus,
|
||||||
FaSearch,
|
FaSearch,
|
||||||
|
|
@ -12,44 +12,38 @@ import {
|
||||||
FaEnvelope,
|
FaEnvelope,
|
||||||
FaAward,
|
FaAward,
|
||||||
FaClock,
|
FaClock,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import { TeamRoleEnum } from "../../../types/common";
|
import { TeamRoleEnum } from '../../../types/common'
|
||||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||||
import NewTeamModal from "./NewTeamModal";
|
import NewTeamModal from './NewTeamModal'
|
||||||
import ViewTeamModal from "./ViewTeamModal";
|
import ViewTeamModal from './ViewTeamModal'
|
||||||
import EditTeamModal from "./EditTeamModal";
|
import EditTeamModal from './EditTeamModal'
|
||||||
import AssignWorkOrderModal from "./AssignWorkOrderModal";
|
import AssignWorkOrderModal from './AssignWorkOrderModal'
|
||||||
import TeamStatusChangeModal from "./TeamStatusChangeModal";
|
import TeamStatusChangeModal from './TeamStatusChangeModal'
|
||||||
import Widget from "../../../components/common/Widget";
|
import Widget from '../../../components/common/Widget'
|
||||||
import { Team } from "../../../types/common";
|
import { Team } from '../../../types/common'
|
||||||
import {
|
import { getTeamRoleColor, getTeamRoleIcon, getTeamRoleText } from '../../../utils/erp'
|
||||||
getTeamRoleColor,
|
import { Container } from '@/components/shared'
|
||||||
getTeamRoleIcon,
|
|
||||||
getTeamRoleText,
|
|
||||||
} from "../../../utils/erp";
|
|
||||||
|
|
||||||
const MaintenanceTeams: React.FC = () => {
|
const MaintenanceTeams: React.FC = () => {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [roleFilter, setRoleFilter] = useState<TeamRoleEnum | "all">("all");
|
const [roleFilter, setRoleFilter] = useState<TeamRoleEnum | 'all'>('all')
|
||||||
const [statusFilter, setStatusFilter] = useState<
|
const [statusFilter, setStatusFilter] = useState<'active' | 'inactive' | 'all'>('all')
|
||||||
"active" | "inactive" | "all"
|
|
||||||
>("all");
|
|
||||||
|
|
||||||
// Modal states
|
// Modal states
|
||||||
const [showNewTeamModal, setShowNewTeamModal] = useState(false);
|
const [showNewTeamModal, setShowNewTeamModal] = useState(false)
|
||||||
const [showViewTeamModal, setShowViewTeamModal] = useState(false);
|
const [showViewTeamModal, setShowViewTeamModal] = useState(false)
|
||||||
const [showEditTeamModal, setShowEditTeamModal] = useState(false);
|
const [showEditTeamModal, setShowEditTeamModal] = useState(false)
|
||||||
const [showAssignWorkOrderModal, setShowAssignWorkOrderModal] =
|
const [showAssignWorkOrderModal, setShowAssignWorkOrderModal] = useState(false)
|
||||||
useState(false);
|
const [showStatusChangeModal, setShowStatusChangeModal] = useState(false)
|
||||||
const [showStatusChangeModal, setShowStatusChangeModal] = useState(false);
|
|
||||||
|
|
||||||
// Selected data
|
// Selected data
|
||||||
const [viewingTeam, setViewingTeam] = useState<Team | null>(null);
|
const [viewingTeam, setViewingTeam] = useState<Team | null>(null)
|
||||||
const [editingTeam, setEditingTeam] = useState<Team | null>(null);
|
const [editingTeam, setEditingTeam] = useState<Team | null>(null)
|
||||||
const [selectedTeams, setSelectedTeams] = useState<string[]>([]);
|
const [selectedTeams, setSelectedTeams] = useState<string[]>([])
|
||||||
|
|
||||||
// Mock data - replace with actual API calls
|
// 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 filteredTeams = teams.filter((team) => {
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
|
|
@ -57,441 +51,389 @@ const MaintenanceTeams: React.FC = () => {
|
||||||
team.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
team.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
team.members.some(
|
team.members.some(
|
||||||
(member) =>
|
(member) =>
|
||||||
member.employee?.firstName
|
member.employee?.firstName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
.toLowerCase()
|
member.employee?.lastName.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||||
.includes(searchTerm.toLowerCase()) ||
|
)
|
||||||
member.employee?.lastName
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchTerm.toLowerCase())
|
|
||||||
);
|
|
||||||
const matchesRole =
|
const matchesRole =
|
||||||
roleFilter === "all" ||
|
roleFilter === 'all' || team.members.some((member) => member.role === roleFilter)
|
||||||
team.members.some((member) => member.role === roleFilter);
|
|
||||||
const matchesStatus =
|
const matchesStatus =
|
||||||
statusFilter === "all" ||
|
statusFilter === 'all' ||
|
||||||
(statusFilter === "active" && team.isActive) ||
|
(statusFilter === 'active' && team.isActive) ||
|
||||||
(statusFilter === "inactive" && !team.isActive);
|
(statusFilter === 'inactive' && !team.isActive)
|
||||||
return matchesSearch && matchesRole && matchesStatus;
|
return matchesSearch && matchesRole && matchesStatus
|
||||||
});
|
})
|
||||||
|
|
||||||
const getTeamLeader = (team: Team) => {
|
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) => {
|
const getActiveMembers = (team: Team) => {
|
||||||
return team.members.filter((member) => member.isActive);
|
return team.members.filter((member) => member.isActive)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleAddTeam = () => {
|
const handleAddTeam = () => {
|
||||||
setEditingTeam(null);
|
setEditingTeam(null)
|
||||||
setShowNewTeamModal(true);
|
setShowNewTeamModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleViewTeam = (team: Team) => {
|
const handleViewTeam = (team: Team) => {
|
||||||
setViewingTeam(team);
|
setViewingTeam(team)
|
||||||
setShowViewTeamModal(true);
|
setShowViewTeamModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleEditTeam = (team: Team) => {
|
const handleEditTeam = (team: Team) => {
|
||||||
setEditingTeam(team);
|
setEditingTeam(team)
|
||||||
setShowEditTeamModal(true);
|
setShowEditTeamModal(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSelectTeam = (teamId: string) => {
|
const handleSelectTeam = (teamId: string) => {
|
||||||
setSelectedTeams((prev) =>
|
setSelectedTeams((prev) =>
|
||||||
prev.includes(teamId)
|
prev.includes(teamId) ? prev.filter((id) => id !== teamId) : [...prev, teamId],
|
||||||
? prev.filter((id) => id !== teamId)
|
)
|
||||||
: [...prev, teamId]
|
}
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNewTeamSave = (newTeam: Partial<Team>) => {
|
const handleNewTeamSave = (newTeam: Partial<Team>) => {
|
||||||
if (newTeam.id) {
|
if (newTeam.id) {
|
||||||
setTeams((prev) => [...prev, newTeam as Team]);
|
setTeams((prev) => [...prev, newTeam as Team])
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleEditTeamSave = (updatedTeam: Team) => {
|
const handleEditTeamSave = (updatedTeam: Team) => {
|
||||||
setTeams((prev) =>
|
setTeams((prev) => prev.map((team) => (team.id === updatedTeam.id ? updatedTeam : team)))
|
||||||
prev.map((team) => (team.id === updatedTeam.id ? updatedTeam : team))
|
}
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAssignWorkOrders = (
|
const handleAssignWorkOrders = (assignments: { teamId: string; planIds: string[] }[]) => {
|
||||||
assignments: { teamId: string; planIds: string[] }[]
|
|
||||||
) => {
|
|
||||||
// Here you would typically make API calls to assign work orders
|
// 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
|
// Show success message or handle the actual assignment logic
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleStatusChange = (
|
const handleStatusChange = (teamIds: string[], newStatus: boolean, reason?: string) => {
|
||||||
teamIds: string[],
|
|
||||||
newStatus: boolean,
|
|
||||||
reason?: string
|
|
||||||
) => {
|
|
||||||
setTeams((prev) =>
|
setTeams((prev) =>
|
||||||
prev.map((team) =>
|
prev.map((team) =>
|
||||||
teamIds.includes(team.id)
|
teamIds.includes(team.id)
|
||||||
? { ...team, isActive: newStatus, lastModificationTime: new Date() }
|
? { ...team, isActive: newStatus, lastModificationTime: new Date() }
|
||||||
: team
|
: team,
|
||||||
)
|
),
|
||||||
);
|
)
|
||||||
setSelectedTeams([]);
|
setSelectedTeams([])
|
||||||
// Here you would typically log the status change with reason
|
// 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) =>
|
const selectedTeamObjects = teams.filter((team) => selectedTeams.includes(team.id))
|
||||||
selectedTeams.includes(team.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 pt-2">
|
<Container>
|
||||||
{/* Header */}
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
{/* Header */}
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h2 className="text-2xl font-bold text-gray-900">Bakım Ekipleri</h2>
|
<div>
|
||||||
<p className="text-gray-600">
|
<h2 className="text-2xl font-bold text-gray-900">Bakım Ekipleri</h2>
|
||||||
Bakım ekiplerini ve üyelerini yönetin
|
<p className="text-gray-600">Bakım ekiplerini ve üyelerini yönetin</p>
|
||||||
</p>
|
</div>
|
||||||
</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>
|
|
||||||
<button
|
<button
|
||||||
onClick={handleAddTeam}
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Bulk Actions */}
|
{/* Summary Cards */}
|
||||||
{selectedTeams.length > 0 && (
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||||
<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">
|
<Widget title="Toplam Ekip" value={teams.length} color="blue" icon="FaUsers" />
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<span className="text-sm text-gray-600">
|
<Widget
|
||||||
{selectedTeams.length} ekip seçildi
|
title="Aktif Ekip"
|
||||||
</span>
|
value={teams.filter((t) => t.isActive).length}
|
||||||
<div className="flex space-x-2">
|
color="green"
|
||||||
<button
|
icon="FaCheckCircle"
|
||||||
onClick={() => setShowAssignWorkOrderModal(true)}
|
/>
|
||||||
className="bg-green-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-green-700"
|
|
||||||
>
|
<Widget
|
||||||
İş Emri Ata
|
title="Toplam Üye"
|
||||||
</button>
|
value={teams.reduce((total, team) => total + getActiveMembers(team).length, 0)}
|
||||||
<button
|
color="orange"
|
||||||
onClick={() => setShowStatusChangeModal(true)}
|
icon="FaUser"
|
||||||
className="bg-blue-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-blue-700"
|
/>
|
||||||
>
|
|
||||||
Durum Değiştir
|
<Widget
|
||||||
</button>
|
title="Uzmanlık"
|
||||||
<button
|
value={teams.reduce((total, team) => total + (team.specializations?.length || 0), 0)}
|
||||||
onClick={() => setSelectedTeams([])}
|
color="purple"
|
||||||
className="bg-gray-600 text-white px-2.5 py-1.5 rounded text-sm hover:bg-gray-700"
|
icon="FaWrench"
|
||||||
>
|
/>
|
||||||
Temizle
|
</div>
|
||||||
</button>
|
|
||||||
</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>
|
||||||
</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 */}
|
{/* Modals */}
|
||||||
<NewTeamModal
|
<NewTeamModal
|
||||||
|
|
@ -527,8 +469,8 @@ const MaintenanceTeams: React.FC = () => {
|
||||||
onSave={handleStatusChange}
|
onSave={handleStatusChange}
|
||||||
selectedTeams={selectedTeamObjects}
|
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 React, { useState, useEffect } from 'react'
|
||||||
import { FaTimes, FaSave, FaCalendar, FaClock } from "react-icons/fa";
|
import { FaTimes, FaSave, FaCalendar, FaClock } from 'react-icons/fa'
|
||||||
import { PmCalendarEvent, WorkOrderStatusEnum } from "../../../types/pm";
|
import { PmCalendarEvent, WorkOrderStatusEnum } from '../../../types/pm'
|
||||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { PriorityEnum } from "../../../types/common";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
|
|
||||||
interface NewCalendarEventModalProps {
|
interface NewCalendarEventModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (event: Partial<PmCalendarEvent>) => void;
|
onSave: (event: Partial<PmCalendarEvent>) => void
|
||||||
selectedDate?: Date;
|
selectedDate?: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||||
|
|
@ -20,185 +20,165 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const getInitialStartTime = () => {
|
const getInitialStartTime = () => {
|
||||||
if (selectedDate) {
|
if (selectedDate) {
|
||||||
const hour = selectedDate.getHours();
|
const hour = selectedDate.getHours()
|
||||||
const minute = selectedDate.getMinutes();
|
const minute = selectedDate.getMinutes()
|
||||||
return `${hour.toString().padStart(2, "0")}:${minute
|
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}`;
|
|
||||||
}
|
}
|
||||||
return "09:00";
|
return '09:00'
|
||||||
};
|
}
|
||||||
|
|
||||||
const getInitialEndTime = () => {
|
const getInitialEndTime = () => {
|
||||||
if (selectedDate) {
|
if (selectedDate) {
|
||||||
const endTime = new Date(selectedDate);
|
const endTime = new Date(selectedDate)
|
||||||
endTime.setHours(endTime.getHours() + 1);
|
endTime.setHours(endTime.getHours() + 1)
|
||||||
const hour = endTime.getHours();
|
const hour = endTime.getHours()
|
||||||
const minute = endTime.getMinutes();
|
const minute = endTime.getMinutes()
|
||||||
return `${hour.toString().padStart(2, "0")}:${minute
|
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}`;
|
|
||||||
}
|
}
|
||||||
return "10:00";
|
return '10:00'
|
||||||
};
|
}
|
||||||
|
|
||||||
const [eventData, setEventData] = useState<Partial<PmCalendarEvent>>({
|
const [eventData, setEventData] = useState<Partial<PmCalendarEvent>>({
|
||||||
title: "",
|
title: '',
|
||||||
type: "plan",
|
type: 'plan',
|
||||||
date: selectedDate || new Date(),
|
date: selectedDate || new Date(),
|
||||||
startTime: getInitialStartTime(),
|
startTime: getInitialStartTime(),
|
||||||
endTime: getInitialEndTime(),
|
endTime: getInitialEndTime(),
|
||||||
status: WorkOrderStatusEnum.Planned,
|
status: WorkOrderStatusEnum.Planned,
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
assignedTo: "",
|
assignedTo: '',
|
||||||
workCenterCode: "",
|
workCenterCode: '',
|
||||||
duration: 60,
|
duration: 60,
|
||||||
});
|
})
|
||||||
|
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
|
|
||||||
// Update form data when selectedDate changes
|
// Update form data when selectedDate changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedDate) {
|
if (selectedDate) {
|
||||||
const getInitialStartTime = () => {
|
const getInitialStartTime = () => {
|
||||||
const hour = selectedDate.getHours();
|
const hour = selectedDate.getHours()
|
||||||
const minute = selectedDate.getMinutes();
|
const minute = selectedDate.getMinutes()
|
||||||
return `${hour.toString().padStart(2, "0")}:${minute
|
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
|
||||||
.toString()
|
}
|
||||||
.padStart(2, "0")}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getInitialEndTime = () => {
|
const getInitialEndTime = () => {
|
||||||
const endTime = new Date(selectedDate);
|
const endTime = new Date(selectedDate)
|
||||||
endTime.setHours(endTime.getHours() + 1);
|
endTime.setHours(endTime.getHours() + 1)
|
||||||
const hour = endTime.getHours();
|
const hour = endTime.getHours()
|
||||||
const minute = endTime.getMinutes();
|
const minute = endTime.getMinutes()
|
||||||
return `${hour.toString().padStart(2, "0")}:${minute
|
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
|
||||||
.toString()
|
}
|
||||||
.padStart(2, "0")}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
setEventData((prev) => ({
|
setEventData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
date: selectedDate,
|
date: selectedDate,
|
||||||
startTime: getInitialStartTime(),
|
startTime: getInitialStartTime(),
|
||||||
endTime: getInitialEndTime(),
|
endTime: getInitialEndTime(),
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
}, [selectedDate]);
|
}, [selectedDate])
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (!eventData.title?.trim()) {
|
if (!eventData.title?.trim()) {
|
||||||
newErrors.title = "Başlık gerekli";
|
newErrors.title = 'Başlık gerekli'
|
||||||
}
|
}
|
||||||
if (!eventData.workCenterCode?.trim()) {
|
if (!eventData.workCenterCode?.trim()) {
|
||||||
newErrors.workCenterCode = "İş Merkezi seçimi gerekli";
|
newErrors.workCenterCode = 'İş Merkezi seçimi gerekli'
|
||||||
}
|
}
|
||||||
if (!eventData.startTime) {
|
if (!eventData.startTime) {
|
||||||
newErrors.startTime = "Başlangıç saati gerekli";
|
newErrors.startTime = 'Başlangıç saati gerekli'
|
||||||
}
|
}
|
||||||
if (!eventData.endTime) {
|
if (!eventData.endTime) {
|
||||||
newErrors.endTime = "Bitiş saati gerekli";
|
newErrors.endTime = 'Bitiş saati gerekli'
|
||||||
}
|
}
|
||||||
if (eventData.startTime && eventData.endTime) {
|
if (eventData.startTime && eventData.endTime) {
|
||||||
const start = new Date(`2000-01-01 ${eventData.startTime}`);
|
const start = new Date(`2000-01-01 ${eventData.startTime}`)
|
||||||
const end = new Date(`2000-01-01 ${eventData.endTime}`);
|
const end = new Date(`2000-01-01 ${eventData.endTime}`)
|
||||||
if (start >= end) {
|
if (start >= end) {
|
||||||
newErrors.endTime = "Bitiş saati başlangıç saatinden sonra olmalı";
|
newErrors.endTime = 'Bitiş saati başlangıç saatinden sonra olmalı'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
// Calculate duration
|
// Calculate duration
|
||||||
if (eventData.startTime && eventData.endTime) {
|
if (eventData.startTime && eventData.endTime) {
|
||||||
const start = new Date(`2000-01-01 ${eventData.startTime}`);
|
const start = new Date(`2000-01-01 ${eventData.startTime}`)
|
||||||
const end = new Date(`2000-01-01 ${eventData.endTime}`);
|
const end = new Date(`2000-01-01 ${eventData.endTime}`)
|
||||||
const durationMinutes = (end.getTime() - start.getTime()) / (1000 * 60);
|
const durationMinutes = (end.getTime() - start.getTime()) / (1000 * 60)
|
||||||
eventData.duration = durationMinutes;
|
eventData.duration = durationMinutes
|
||||||
}
|
}
|
||||||
|
|
||||||
onSave({
|
onSave({
|
||||||
...eventData,
|
...eventData,
|
||||||
id: `E${Date.now()}`, // Generate a simple ID
|
id: `E${Date.now()}`, // Generate a simple ID
|
||||||
});
|
})
|
||||||
onClose();
|
onClose()
|
||||||
// Reset form
|
// Reset form
|
||||||
setEventData({
|
setEventData({
|
||||||
title: "",
|
title: '',
|
||||||
type: "plan",
|
type: 'plan',
|
||||||
date: selectedDate || new Date(),
|
date: selectedDate || new Date(),
|
||||||
startTime: getInitialStartTime(),
|
startTime: getInitialStartTime(),
|
||||||
endTime: getInitialEndTime(),
|
endTime: getInitialEndTime(),
|
||||||
status: WorkOrderStatusEnum.Planned,
|
status: WorkOrderStatusEnum.Planned,
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
assignedTo: "",
|
assignedTo: '',
|
||||||
workCenterCode: "",
|
workCenterCode: '',
|
||||||
duration: 60,
|
duration: 60,
|
||||||
});
|
})
|
||||||
setErrors({});
|
setErrors({})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (
|
||||||
field: keyof PmCalendarEvent,
|
field: keyof PmCalendarEvent,
|
||||||
value:
|
value: string | Date | PriorityEnum | WorkOrderStatusEnum | 'plan' | 'workorder' | 'scheduled',
|
||||||
| string
|
|
||||||
| Date
|
|
||||||
| PriorityEnum
|
|
||||||
| WorkOrderStatusEnum
|
|
||||||
| "plan"
|
|
||||||
| "workorder"
|
|
||||||
| "scheduled"
|
|
||||||
) => {
|
) => {
|
||||||
setEventData((prev) => ({
|
setEventData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
}));
|
}))
|
||||||
// Clear error when user starts typing
|
// Clear error when user starts typing
|
||||||
if (errors[field]) {
|
if (errors[field]) {
|
||||||
setErrors((prev) => ({
|
setErrors((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: "",
|
[field]: '',
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const generateTimeOptions = () => {
|
const generateTimeOptions = () => {
|
||||||
const options = [];
|
const options = []
|
||||||
for (let hour = 0; hour < 24; hour++) {
|
for (let hour = 0; hour < 24; hour++) {
|
||||||
for (let minute = 0; minute < 60; minute += 30) {
|
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()
|
.toString()
|
||||||
.padStart(2, "0")}`;
|
.padStart(2, '0')}`
|
||||||
options.push(timeString);
|
options.push(timeString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return options;
|
return options
|
||||||
};
|
}
|
||||||
|
|
||||||
const timeOptions = generateTimeOptions();
|
const timeOptions = generateTimeOptions()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<div className="bg-white rounded-lg w-full max-w-lg mx-4 max-h-[90vh] overflow-y-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<h2 className="text-xl font-bold text-gray-900">
|
<h2 className="text-xl font-bold text-gray-900">Yeni Bakım Planlaması</h2>
|
||||||
Yeni Bakım Planlaması
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||||
</h2>
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5" />
|
<FaTimes className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -208,35 +188,24 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||||
{/* Basic Info */}
|
{/* Basic Info */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Başlık *</label>
|
||||||
Başlık *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={eventData.title || ""}
|
value={eventData.title || ''}
|
||||||
onChange={(e) => handleInputChange("title", e.target.value)}
|
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 ${
|
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ığı"
|
placeholder="Bakım planı başlığı"
|
||||||
/>
|
/>
|
||||||
{errors.title && (
|
{errors.title && <p className="text-red-500 text-xs mt-1">{errors.title}</p>}
|
||||||
<p className="text-red-500 text-xs mt-1">{errors.title}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Tip</label>
|
||||||
Tip
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={eventData.type || "plan"}
|
value={eventData.type || 'plan'}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('type', e.target.value as 'plan' | 'workorder')}
|
||||||
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"
|
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>
|
<option value="plan">Bakım Planı</option>
|
||||||
|
|
@ -245,14 +214,10 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Öncelik</label>
|
||||||
Öncelik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={eventData.priority || PriorityEnum.Normal}
|
value={eventData.priority || PriorityEnum.Normal}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('priority', e.target.value as PriorityEnum)}
|
||||||
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"
|
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>
|
<option value={PriorityEnum.Low}>Düşük</option>
|
||||||
|
|
@ -263,16 +228,12 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">İş Merkezi *</label>
|
||||||
İş Merkezi *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={eventData.workCenterCode || ""}
|
value={eventData.workCenterCode || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('workCenterCode', e.target.value)}
|
||||||
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 ${
|
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>
|
<option value="">İş merkezi seçin</option>
|
||||||
|
|
@ -283,21 +244,15 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.workCenterCode && (
|
{errors.workCenterCode && (
|
||||||
<p className="text-red-500 text-xs mt-1">
|
<p className="text-red-500 text-xs mt-1">{errors.workCenterCode}</p>
|
||||||
{errors.workCenterCode}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Atanan Kişi</label>
|
||||||
Atanan Kişi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={eventData.assignedTo || ""}
|
value={eventData.assignedTo || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('assignedTo', e.target.value)}
|
||||||
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"
|
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>
|
<option value="">Kişi seçin</option>
|
||||||
|
|
@ -319,10 +274,8 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={eventData.date?.toISOString().split("T")[0] || ""}
|
value={eventData.date?.toISOString().split('T')[0] || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('date', new Date(e.target.value))}
|
||||||
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"
|
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>
|
||||||
|
|
@ -333,10 +286,10 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||||
Başlangıç Saati *
|
Başlangıç Saati *
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={eventData.startTime || "09:00"}
|
value={eventData.startTime || '09:00'}
|
||||||
onChange={(e) => handleInputChange("startTime", e.target.value)}
|
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 ${
|
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) => (
|
{timeOptions.map((time) => (
|
||||||
|
|
@ -345,20 +298,16 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.startTime && (
|
{errors.startTime && <p className="text-red-500 text-xs mt-1">{errors.startTime}</p>}
|
||||||
<p className="text-red-500 text-xs mt-1">{errors.startTime}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Bitiş Saati *</label>
|
||||||
Bitiş Saati *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={eventData.endTime || "10:00"}
|
value={eventData.endTime || '10:00'}
|
||||||
onChange={(e) => handleInputChange("endTime", e.target.value)}
|
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 ${
|
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) => (
|
{timeOptions.map((time) => (
|
||||||
|
|
@ -367,44 +316,29 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.endTime && (
|
{errors.endTime && <p className="text-red-500 text-xs mt-1">{errors.endTime}</p>}
|
||||||
<p className="text-red-500 text-xs mt-1">{errors.endTime}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Status */}
|
{/* Status */}
|
||||||
{eventData.type === "workorder" && (
|
{eventData.type === 'workorder' && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Durum</label>
|
||||||
Durum
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={eventData.status || "scheduled"}
|
value={eventData.status || 'scheduled'}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleInputChange(
|
handleInputChange('status', e.target.value as WorkOrderStatusEnum | 'scheduled')
|
||||||
"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"
|
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="scheduled">Planlanmış</option>
|
||||||
<option value={WorkOrderStatusEnum.Created}>Oluşturuldu</option>
|
<option value={WorkOrderStatusEnum.Created}>Oluşturuldu</option>
|
||||||
<option value={WorkOrderStatusEnum.Planned}>Planlandı</option>
|
<option value={WorkOrderStatusEnum.Planned}>Planlandı</option>
|
||||||
<option value={WorkOrderStatusEnum.Released}>
|
<option value={WorkOrderStatusEnum.Released}>Serbest Bırakıldı</option>
|
||||||
Serbest Bırakıldı
|
<option value={WorkOrderStatusEnum.InProgress}>Devam Ediyor</option>
|
||||||
</option>
|
|
||||||
<option value={WorkOrderStatusEnum.InProgress}>
|
|
||||||
Devam Ediyor
|
|
||||||
</option>
|
|
||||||
<option value={WorkOrderStatusEnum.OnHold}>Beklemede</option>
|
<option value={WorkOrderStatusEnum.OnHold}>Beklemede</option>
|
||||||
<option value={WorkOrderStatusEnum.Completed}>
|
<option value={WorkOrderStatusEnum.Completed}>Tamamlandı</option>
|
||||||
Tamamlandı
|
<option value={WorkOrderStatusEnum.Cancelled}>İptal Edildi</option>
|
||||||
</option>
|
|
||||||
<option value={WorkOrderStatusEnum.Cancelled}>
|
|
||||||
İptal Edildi
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -428,7 +362,7 @@ const NewCalendarEventModal: React.FC<NewCalendarEventModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default NewCalendarEventModal;
|
export default NewCalendarEventModal
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaUpload, FaMinus } from "react-icons/fa";
|
import { FaTimes, FaSave, FaUpload, FaMinus } from 'react-icons/fa'
|
||||||
import {
|
import {
|
||||||
PmFaultNotification,
|
PmFaultNotification,
|
||||||
FaultTypeEnum,
|
FaultTypeEnum,
|
||||||
CriticalityLevelEnum,
|
CriticalityLevelEnum,
|
||||||
NotificationStatusEnum,
|
NotificationStatusEnum,
|
||||||
} from "../../../types/pm";
|
} from '../../../types/pm'
|
||||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { PriorityEnum } from "../../../types/common";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
|
|
||||||
interface NewFaultNotificationModalProps {
|
interface NewFaultNotificationModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (notification: Partial<PmFaultNotification>) => void;
|
onSave: (notification: Partial<PmFaultNotification>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
|
|
@ -21,12 +21,8 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
onClose,
|
onClose,
|
||||||
onSave,
|
onSave,
|
||||||
}) => {
|
}) => {
|
||||||
const [notificationData, setNotificationData] = useState<
|
const [notificationData, setNotificationData] = useState<Partial<PmFaultNotification>>({
|
||||||
Partial<PmFaultNotification>
|
notificationCode: `ARZ-${new Date().getFullYear()}-${String(Date.now()).slice(-3)}`,
|
||||||
>({
|
|
||||||
notificationCode: `ARZ-${new Date().getFullYear()}-${String(
|
|
||||||
Date.now()
|
|
||||||
).slice(-3)}`,
|
|
||||||
faultType: FaultTypeEnum.Mechanical,
|
faultType: FaultTypeEnum.Mechanical,
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
severity: CriticalityLevelEnum.Medium,
|
severity: CriticalityLevelEnum.Medium,
|
||||||
|
|
@ -34,57 +30,55 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
followUpRequired: false,
|
followUpRequired: false,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
images: [],
|
images: [],
|
||||||
});
|
})
|
||||||
|
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
const [uploadedImages, setUploadedImages] = useState<string[]>([]);
|
const [uploadedImages, setUploadedImages] = useState<string[]>([])
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (!notificationData.workCenterId) {
|
if (!notificationData.workCenterId) {
|
||||||
newErrors.workCenterId = "İş merkezi seçimi gerekli";
|
newErrors.workCenterId = 'İş merkezi seçimi gerekli'
|
||||||
}
|
}
|
||||||
if (!notificationData.title?.trim()) {
|
if (!notificationData.title?.trim()) {
|
||||||
newErrors.title = "Başlık gerekli";
|
newErrors.title = 'Başlık gerekli'
|
||||||
}
|
}
|
||||||
if (!notificationData.description?.trim()) {
|
if (!notificationData.description?.trim()) {
|
||||||
newErrors.description = "Açıklama gerekli";
|
newErrors.description = 'Açıklama gerekli'
|
||||||
}
|
}
|
||||||
if (!notificationData.location?.trim()) {
|
if (!notificationData.location?.trim()) {
|
||||||
newErrors.location = "Konum gerekli";
|
newErrors.location = 'Konum gerekli'
|
||||||
}
|
}
|
||||||
if (!notificationData.reportedBy?.trim()) {
|
if (!notificationData.reportedBy?.trim()) {
|
||||||
newErrors.reportedBy = "Bildiren kişi gerekli";
|
newErrors.reportedBy = 'Bildiren kişi gerekli'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (
|
||||||
field: keyof PmFaultNotification,
|
field: keyof PmFaultNotification,
|
||||||
value: string | number | boolean | undefined
|
value: string | number | boolean | undefined,
|
||||||
) => {
|
) => {
|
||||||
setNotificationData((prev) => ({
|
setNotificationData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
}));
|
}))
|
||||||
// Clear error when user starts typing
|
// Clear error when user starts typing
|
||||||
if (errors[field]) {
|
if (errors[field]) {
|
||||||
setErrors((prev) => ({
|
setErrors((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: "",
|
[field]: '',
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleWorkCenterChange = (workCenterId: string) => {
|
const handleWorkCenterChange = (workCenterId: string) => {
|
||||||
const selectedWorkCenter = mockWorkCenters.find(
|
const selectedWorkCenter = mockWorkCenters.find((eq) => eq.id === workCenterId)
|
||||||
(eq) => eq.id === workCenterId
|
|
||||||
);
|
|
||||||
if (selectedWorkCenter) {
|
if (selectedWorkCenter) {
|
||||||
setNotificationData((prev) => ({
|
setNotificationData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
|
|
@ -92,29 +86,29 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
workCenterCode: selectedWorkCenter.code,
|
workCenterCode: selectedWorkCenter.code,
|
||||||
workCenterName: selectedWorkCenter.name,
|
workCenterName: selectedWorkCenter.name,
|
||||||
location: selectedWorkCenter.location,
|
location: selectedWorkCenter.location,
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleImageUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleImageUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const files = event.target.files;
|
const files = event.target.files
|
||||||
if (files) {
|
if (files) {
|
||||||
const imageNames = Array.from(files).map((file) => file.name);
|
const imageNames = Array.from(files).map((file) => file.name)
|
||||||
setUploadedImages((prev) => [...prev, ...imageNames]);
|
setUploadedImages((prev) => [...prev, ...imageNames])
|
||||||
setNotificationData((prev) => ({
|
setNotificationData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
images: [...(prev.images || []), ...imageNames],
|
images: [...(prev.images || []), ...imageNames],
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeImage = (index: number) => {
|
const removeImage = (index: number) => {
|
||||||
setUploadedImages((prev) => prev.filter((_, i) => i !== index));
|
setUploadedImages((prev) => prev.filter((_, i) => i !== index))
|
||||||
setNotificationData((prev) => ({
|
setNotificationData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
images: prev.images?.filter((_, i) => i !== index) || [],
|
images: prev.images?.filter((_, i) => i !== index) || [],
|
||||||
}));
|
}))
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
|
|
@ -124,16 +118,14 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
reportedAt: new Date(),
|
reportedAt: new Date(),
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
|
|
||||||
onSave(notificationToSave);
|
onSave(notificationToSave)
|
||||||
onClose();
|
onClose()
|
||||||
|
|
||||||
// Reset form
|
// Reset form
|
||||||
setNotificationData({
|
setNotificationData({
|
||||||
notificationCode: `ARZ-${new Date().getFullYear()}-${String(
|
notificationCode: `ARZ-${new Date().getFullYear()}-${String(Date.now()).slice(-3)}`,
|
||||||
Date.now()
|
|
||||||
).slice(-3)}`,
|
|
||||||
faultType: FaultTypeEnum.Mechanical,
|
faultType: FaultTypeEnum.Mechanical,
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
severity: CriticalityLevelEnum.Medium,
|
severity: CriticalityLevelEnum.Medium,
|
||||||
|
|
@ -141,24 +133,19 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
followUpRequired: false,
|
followUpRequired: false,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
images: [],
|
images: [],
|
||||||
});
|
})
|
||||||
setUploadedImages([]);
|
setUploadedImages([])
|
||||||
setErrors({});
|
setErrors({})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<div className="bg-white rounded-lg w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<h2 className="text-xl font-bold text-gray-900">
|
<h2 className="text-xl font-bold text-gray-900">Yeni Arıza Bildirimi</h2>
|
||||||
Yeni Arıza Bildirimi
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||||
</h2>
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5" />
|
<FaTimes className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -173,24 +160,20 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={notificationData.notificationCode || ""}
|
value={notificationData.notificationCode || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('notificationCode', e.target.value)}
|
||||||
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"
|
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"
|
placeholder="ARZ-2024-001"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||||
İş Merkezi *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={notificationData.workCenterId || ""}
|
value={notificationData.workCenterId || ''}
|
||||||
onChange={(e) => handleWorkCenterChange(e.target.value)}
|
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 ${
|
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>
|
<option value="">İş merkezi seçin</option>
|
||||||
|
|
@ -201,9 +184,7 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.workCenterId && (
|
{errors.workCenterId && (
|
||||||
<p className="text-red-500 text-xs mt-1">
|
<p className="text-red-500 text-xs mt-1">{errors.workCenterId}</p>
|
||||||
{errors.workCenterId}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -216,16 +197,14 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={notificationData.title || ""}
|
value={notificationData.title || ''}
|
||||||
onChange={(e) => handleInputChange("title", e.target.value)}
|
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 ${
|
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ığı"
|
placeholder="Arıza başlığı"
|
||||||
/>
|
/>
|
||||||
{errors.title && (
|
{errors.title && <p className="text-red-500 text-xs mt-1">{errors.title}</p>}
|
||||||
<p className="text-red-500 text-xs mt-1">{errors.title}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -233,20 +212,16 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
Detaylı Açıklama *
|
Detaylı Açıklama *
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={notificationData.description || ""}
|
value={notificationData.description || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||||
handleInputChange("description", e.target.value)
|
|
||||||
}
|
|
||||||
rows={2}
|
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 ${
|
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"
|
placeholder="Arızanın detaylı açıklaması, belirtiler ve gözlemler"
|
||||||
/>
|
/>
|
||||||
{errors.description && (
|
{errors.description && (
|
||||||
<p className="text-red-500 text-xs mt-1">
|
<p className="text-red-500 text-xs mt-1">{errors.description}</p>
|
||||||
{errors.description}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -254,21 +229,17 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
{/* Location and Reporter */}
|
{/* Location and Reporter */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Konum *</label>
|
||||||
Konum *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={notificationData.location || ""}
|
value={notificationData.location || ''}
|
||||||
onChange={(e) => handleInputChange("location", e.target.value)}
|
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 ${
|
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"
|
placeholder="Atölye A - Hat 1"
|
||||||
/>
|
/>
|
||||||
{errors.location && (
|
{errors.location && <p className="text-red-500 text-xs mt-1">{errors.location}</p>}
|
||||||
<p className="text-red-500 text-xs mt-1">{errors.location}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -276,12 +247,10 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
Bildiren Kişi *
|
Bildiren Kişi *
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={notificationData.reportedBy || ""}
|
value={notificationData.reportedBy || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('reportedBy', e.target.value)}
|
||||||
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 ${
|
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>
|
<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 className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Arıza Türü</label>
|
||||||
Arıza Türü
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={notificationData.faultType || FaultTypeEnum.Mechanical}
|
value={notificationData.faultType || FaultTypeEnum.Mechanical}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('faultType', e.target.value as FaultTypeEnum)}
|
||||||
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"
|
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>
|
<option value={FaultTypeEnum.Mechanical}>Mekanik</option>
|
||||||
|
|
@ -324,14 +286,10 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Öncelik</label>
|
||||||
Öncelik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={notificationData.priority || PriorityEnum.Normal}
|
value={notificationData.priority || PriorityEnum.Normal}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('priority', e.target.value as PriorityEnum)}
|
||||||
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"
|
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>
|
<option value={PriorityEnum.Low}>Düşük</option>
|
||||||
|
|
@ -342,16 +300,11 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Kritiklik</label>
|
||||||
Kritiklik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={notificationData.severity || CriticalityLevelEnum.Medium}
|
value={notificationData.severity || CriticalityLevelEnum.Medium}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleInputChange(
|
handleInputChange('severity', e.target.value as CriticalityLevelEnum)
|
||||||
"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"
|
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>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={notificationData.estimatedRepairTime || ""}
|
value={notificationData.estimatedRepairTime || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleInputChange(
|
handleInputChange('estimatedRepairTime', parseInt(e.target.value) || undefined)
|
||||||
"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"
|
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"
|
placeholder="120"
|
||||||
|
|
@ -389,23 +339,17 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={notificationData.followUpRequired || false}
|
checked={notificationData.followUpRequired || false}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('followUpRequired', e.target.checked)}
|
||||||
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"
|
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">
|
<span className="ml-2 text-sm text-gray-700">Takip gerekli</span>
|
||||||
Takip gerekli
|
|
||||||
</span>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Image Upload */}
|
{/* Image Upload */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Fotoğraflar</label>
|
||||||
Fotoğraflar
|
|
||||||
</label>
|
|
||||||
<div className="border-2 border-dashed border-gray-300 rounded-lg p-4">
|
<div className="border-2 border-dashed border-gray-300 rounded-lg p-4">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<FaUpload className="mx-auto h-10 w-10 text-gray-400" />
|
<FaUpload className="mx-auto h-10 w-10 text-gray-400" />
|
||||||
|
|
@ -433,9 +377,7 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
|
|
||||||
{uploadedImages.length > 0 && (
|
{uploadedImages.length > 0 && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<h4 className="text-sm font-medium text-gray-700 mb-1">
|
<h4 className="text-sm font-medium text-gray-700 mb-1">Yüklenen Fotoğraflar:</h4>
|
||||||
Yüklenen Fotoğraflar:
|
|
||||||
</h4>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{uploadedImages.map((image, index) => (
|
{uploadedImages.map((image, index) => (
|
||||||
<div
|
<div
|
||||||
|
|
@ -476,7 +418,7 @@ const NewFaultNotificationModal: React.FC<NewFaultNotificationModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default NewFaultNotificationModal;
|
export default NewFaultNotificationModal
|
||||||
|
|
|
||||||
|
|
@ -1,109 +1,93 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||||
import {
|
import {
|
||||||
PmMaintenancePlan,
|
PmMaintenancePlan,
|
||||||
MaintenancePlanTypeEnum,
|
MaintenancePlanTypeEnum,
|
||||||
FrequencyUnitEnum,
|
FrequencyUnitEnum,
|
||||||
PmPlanMaterial,
|
PmPlanMaterial,
|
||||||
} from "../../../types/pm";
|
} from '../../../types/pm'
|
||||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||||
import { mockMaterials } from "../../../mocks/mockMaterials";
|
import { mockMaterials } from '../../../mocks/mockMaterials'
|
||||||
import { mockUnits } from "../../../mocks/mockUnits";
|
import { mockUnits } from '../../../mocks/mockUnits'
|
||||||
import { PriorityEnum } from "../../../types/common";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
|
|
||||||
interface NewMaintenancePlanModalProps {
|
interface NewMaintenancePlanModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (plan: Partial<PmMaintenancePlan>) => void;
|
onSave: (plan: Partial<PmMaintenancePlan>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialPlan: Partial<PmMaintenancePlan> = {
|
const initialPlan: Partial<PmMaintenancePlan> = {
|
||||||
planCode: "",
|
planCode: '',
|
||||||
workCenterId: "",
|
workCenterId: '',
|
||||||
planType: MaintenancePlanTypeEnum.Preventive,
|
planType: MaintenancePlanTypeEnum.Preventive,
|
||||||
description: "",
|
description: '',
|
||||||
frequency: 1,
|
frequency: 1,
|
||||||
frequencyUnit: FrequencyUnitEnum.Months,
|
frequencyUnit: FrequencyUnitEnum.Months,
|
||||||
estimatedDuration: 60,
|
estimatedDuration: 60,
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
instructions: "",
|
instructions: '',
|
||||||
requiredMaterials: [],
|
requiredMaterials: [],
|
||||||
requiredSkills: [],
|
requiredSkills: [],
|
||||||
nextDue: new Date(),
|
nextDue: new Date(),
|
||||||
isActive: true,
|
isActive: true,
|
||||||
};
|
}
|
||||||
|
|
||||||
const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
onSave,
|
onSave,
|
||||||
}) => {
|
}) => {
|
||||||
const [planData, setPlanData] =
|
const [planData, setPlanData] = useState<Partial<PmMaintenancePlan>>(initialPlan)
|
||||||
useState<Partial<PmMaintenancePlan>>(initialPlan);
|
const [requiredSkills, setRequiredSkills] = useState<string[]>([])
|
||||||
const [requiredSkills, setRequiredSkills] = useState<string[]>([]);
|
const [requiredMaterials, setRequiredMaterials] = useState<PmPlanMaterial[]>([])
|
||||||
const [requiredMaterials, setRequiredMaterials] = useState<PmPlanMaterial[]>(
|
const [newSkill, setNewSkill] = useState('')
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [newSkill, setNewSkill] = useState("");
|
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (
|
||||||
e: React.ChangeEvent<
|
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||||||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
|
||||||
>
|
|
||||||
) => {
|
) => {
|
||||||
const { name, value, type } = e.target;
|
const { name, value, type } = e.target
|
||||||
setPlanData((prev) => ({
|
setPlanData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[name]:
|
[name]: type === 'date' ? new Date(value) : type === 'number' ? Number(value) : value,
|
||||||
type === "date"
|
}))
|
||||||
? new Date(value)
|
}
|
||||||
: type === "number"
|
|
||||||
? Number(value)
|
|
||||||
: value,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const addSkill = () => {
|
const addSkill = () => {
|
||||||
if (newSkill.trim() && !requiredSkills.includes(newSkill.trim())) {
|
if (newSkill.trim() && !requiredSkills.includes(newSkill.trim())) {
|
||||||
setRequiredSkills([...requiredSkills, newSkill.trim()]);
|
setRequiredSkills([...requiredSkills, newSkill.trim()])
|
||||||
setNewSkill("");
|
setNewSkill('')
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeSkill = (skillToRemove: string) => {
|
const removeSkill = (skillToRemove: string) => {
|
||||||
setRequiredSkills(
|
setRequiredSkills(requiredSkills.filter((skill) => skill !== skillToRemove))
|
||||||
requiredSkills.filter((skill) => skill !== skillToRemove)
|
}
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const addMaterial = () => {
|
const addMaterial = () => {
|
||||||
const newMaterial: PmPlanMaterial = {
|
const newMaterial: PmPlanMaterial = {
|
||||||
id: `MAT${Date.now()}`,
|
id: `MAT${Date.now()}`,
|
||||||
planId: "",
|
planId: '',
|
||||||
materialId: "",
|
materialId: '',
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
unitId: "",
|
unitId: '',
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
};
|
}
|
||||||
setRequiredMaterials([...requiredMaterials, newMaterial]);
|
setRequiredMaterials([...requiredMaterials, newMaterial])
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeMaterial = (index: number) => {
|
const removeMaterial = (index: number) => {
|
||||||
setRequiredMaterials(requiredMaterials.filter((_, i) => i !== index));
|
setRequiredMaterials(requiredMaterials.filter((_, i) => i !== index))
|
||||||
};
|
}
|
||||||
|
|
||||||
const updateMaterial = (
|
const updateMaterial = (index: number, field: string, value: string | number | boolean) => {
|
||||||
index: number,
|
|
||||||
field: string,
|
|
||||||
value: string | number | boolean
|
|
||||||
) => {
|
|
||||||
const updated = requiredMaterials.map((material, i) =>
|
const updated = requiredMaterials.map((material, i) =>
|
||||||
i === index ? { ...material, [field]: value } : material
|
i === index ? { ...material, [field]: value } : material,
|
||||||
);
|
)
|
||||||
setRequiredMaterials(updated);
|
setRequiredMaterials(updated)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
const planToSave = {
|
const planToSave = {
|
||||||
|
|
@ -112,26 +96,21 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
requiredMaterials,
|
requiredMaterials,
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
onSave(planToSave);
|
onSave(planToSave)
|
||||||
setPlanData(initialPlan);
|
setPlanData(initialPlan)
|
||||||
setRequiredSkills([]);
|
setRequiredSkills([])
|
||||||
setRequiredMaterials([]);
|
setRequiredMaterials([])
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<div className="bg-white rounded-lg w-full max-w-4xl mx-4 max-h-[90vh] overflow-y-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">
|
<h2 className="text-lg font-semibold text-gray-900">Yeni Bakım Planı</h2>
|
||||||
Yeni Bakım Planı
|
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||||
</h2>
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -140,18 +119,14 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-4 space-y-4">
|
||||||
{/* Basic Information */}
|
{/* Basic Information */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Temel Bilgiler</h3>
|
||||||
Temel Bilgiler
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Plan Kodu *</label>
|
||||||
Plan Kodu *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="planCode"
|
name="planCode"
|
||||||
value={planData.planCode || ""}
|
value={planData.planCode || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
placeholder="örn: PM-CNC-001"
|
||||||
|
|
@ -159,12 +134,10 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||||
İş Merkezi *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="workCenterId"
|
name="workCenterId"
|
||||||
value={planData.workCenterId || ""}
|
value={planData.workCenterId || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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
|
required
|
||||||
|
|
@ -178,35 +151,21 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Plan Tipi *</label>
|
||||||
Plan Tipi *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="planType"
|
name="planType"
|
||||||
value={
|
value={planData.planType || MaintenancePlanTypeEnum.Preventive}
|
||||||
planData.planType || MaintenancePlanTypeEnum.Preventive
|
|
||||||
}
|
|
||||||
onChange={handleInputChange}
|
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"
|
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}>
|
<option value={MaintenancePlanTypeEnum.Preventive}>Önleyici Bakım</option>
|
||||||
Önleyici Bakım
|
<option value={MaintenancePlanTypeEnum.Predictive}>Kestirimci Bakım</option>
|
||||||
</option>
|
<option value={MaintenancePlanTypeEnum.Corrective}>Düzeltici Bakım</option>
|
||||||
<option value={MaintenancePlanTypeEnum.Predictive}>
|
<option value={MaintenancePlanTypeEnum.Condition}>Durum Bazlı Bakım</option>
|
||||||
Kestirimci Bakım
|
|
||||||
</option>
|
|
||||||
<option value={MaintenancePlanTypeEnum.Corrective}>
|
|
||||||
Düzeltici Bakım
|
|
||||||
</option>
|
|
||||||
<option value={MaintenancePlanTypeEnum.Condition}>
|
|
||||||
Durum Bazlı Bakım
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Öncelik</label>
|
||||||
Öncelik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="priority"
|
name="priority"
|
||||||
value={planData.priority || PriorityEnum.Normal}
|
value={planData.priority || PriorityEnum.Normal}
|
||||||
|
|
@ -220,12 +179,10 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama *</label>
|
||||||
Açıklama *
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
name="description"
|
name="description"
|
||||||
value={planData.description || ""}
|
value={planData.description || ''}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
rows={2}
|
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"
|
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 */}
|
{/* Frequency Settings */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Sıklık Ayarları</h3>
|
||||||
Sıklık Ayarları
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Sıklık *</label>
|
||||||
Sıklık *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
name="frequency"
|
name="frequency"
|
||||||
|
|
@ -295,11 +248,7 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
name="nextDue"
|
name="nextDue"
|
||||||
value={
|
value={planData.nextDue ? planData.nextDue.toISOString().split('T')[0] : ''}
|
||||||
planData.nextDue
|
|
||||||
? planData.nextDue.toISOString().split("T")[0]
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
onChange={handleInputChange}
|
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"
|
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 */}
|
{/* Instructions */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Bakım Talimatları</h3>
|
||||||
Bakım Talimatları
|
|
||||||
</h3>
|
|
||||||
<textarea
|
<textarea
|
||||||
name="instructions"
|
name="instructions"
|
||||||
value={planData.instructions || ""}
|
value={planData.instructions || ''}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
rows={3}
|
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"
|
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 */}
|
{/* Required Skills */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<h3 className="text-base font-medium text-gray-900">
|
<h3 className="text-base font-medium text-gray-900">Gerekli Yetenekler</h3>
|
||||||
Gerekli Yetenekler
|
|
||||||
</h3>
|
|
||||||
<div className="flex space-x-1.5">
|
<div className="flex space-x-1.5">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -335,7 +280,7 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
onChange={(e) => setNewSkill(e.target.value)}
|
onChange={(e) => setNewSkill(e.target.value)}
|
||||||
placeholder="Yetenek ekle..."
|
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"
|
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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -368,9 +313,7 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
{/* Required Materials */}
|
{/* Required Materials */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<h3 className="text-base font-medium text-gray-900">
|
<h3 className="text-base font-medium text-gray-900">Gerekli Malzemeler</h3>
|
||||||
Gerekli Malzemeler
|
|
||||||
</h3>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={addMaterial}
|
onClick={addMaterial}
|
||||||
|
|
@ -382,23 +325,14 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{requiredMaterials.map((material, index) => (
|
{requiredMaterials.map((material, index) => (
|
||||||
<div
|
<div key={index} className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg">
|
||||||
key={index}
|
|
||||||
className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg"
|
|
||||||
>
|
|
||||||
<select
|
<select
|
||||||
value={material.materialId}
|
value={material.materialId}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const selectedMaterial = mockMaterials.find(
|
const selectedMaterial = mockMaterials.find((m) => m.id === e.target.value)
|
||||||
(m) => m.id === e.target.value
|
updateMaterial(index, 'materialId', e.target.value)
|
||||||
);
|
|
||||||
updateMaterial(index, "materialId", e.target.value);
|
|
||||||
if (selectedMaterial) {
|
if (selectedMaterial) {
|
||||||
updateMaterial(
|
updateMaterial(index, 'unitId', selectedMaterial.baseUnitId)
|
||||||
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"
|
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
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={material.quantity}
|
value={material.quantity}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateMaterial(index, 'quantity', Number(e.target.value))}
|
||||||
updateMaterial(index, "quantity", Number(e.target.value))
|
|
||||||
}
|
|
||||||
placeholder="Miktar"
|
placeholder="Miktar"
|
||||||
min="1"
|
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"
|
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
|
<select
|
||||||
value={material.unitId}
|
value={material.unitId}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateMaterial(index, 'unitId', e.target.value)}
|
||||||
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"
|
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>
|
<option value="">Birim</option>
|
||||||
|
|
@ -437,32 +367,25 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
{material.materialId &&
|
{material.materialId &&
|
||||||
(() => {
|
(() => {
|
||||||
const selectedMaterial = mockMaterials.find(
|
const selectedMaterial = mockMaterials.find(
|
||||||
(m) => m.id === material.materialId
|
(m) => m.id === material.materialId,
|
||||||
);
|
)
|
||||||
const baseUnitCode = selectedMaterial?.baseUnit;
|
const baseUnitCode = selectedMaterial?.baseUnit
|
||||||
const existsInList = mockUnits.some(
|
const existsInList = mockUnits.some((unit) => unit.id === baseUnitCode?.id)
|
||||||
(unit) => unit.id === baseUnitCode?.id
|
|
||||||
);
|
|
||||||
if (baseUnitCode && !existsInList) {
|
if (baseUnitCode && !existsInList) {
|
||||||
return (
|
return (
|
||||||
<option
|
<option key={baseUnitCode.id} value={baseUnitCode.id}>
|
||||||
key={baseUnitCode.id}
|
|
||||||
value={baseUnitCode.id}
|
|
||||||
>
|
|
||||||
{baseUnitCode.code}
|
{baseUnitCode.code}
|
||||||
</option>
|
</option>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
})()}
|
})()}
|
||||||
</select>
|
</select>
|
||||||
<label className="flex items-center space-x-2">
|
<label className="flex items-center space-x-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={material.isRequired}
|
checked={material.isRequired}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateMaterial(index, 'isRequired', e.target.checked)}
|
||||||
updateMaterial(index, "isRequired", e.target.checked)
|
|
||||||
}
|
|
||||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm text-gray-700">Zorunlu</span>
|
<span className="text-sm text-gray-700">Zorunlu</span>
|
||||||
|
|
@ -478,8 +401,8 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
))}
|
))}
|
||||||
{requiredMaterials.length === 0 && (
|
{requiredMaterials.length === 0 && (
|
||||||
<p className="text-gray-500 text-center py-4">
|
<p className="text-gray-500 text-center py-4">
|
||||||
Henüz malzeme eklenmedi. "Malzeme Ekle" butonunu kullanarak
|
Henüz malzeme eklenmedi. "Malzeme Ekle" butonunu kullanarak malzeme
|
||||||
malzeme ekleyebilirsiniz.
|
ekleyebilirsiniz.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -506,7 +429,7 @@ const NewMaintenancePlanModal: React.FC<NewMaintenancePlanModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default NewMaintenancePlanModal;
|
export default NewMaintenancePlanModal
|
||||||
|
|
|
||||||
|
|
@ -1,170 +1,151 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||||
import { TeamRoleEnum } from "../../../types/common";
|
import { TeamRoleEnum } from '../../../types/common'
|
||||||
import MultiSelectEmployee from "../../../components/common/MultiSelectEmployee";
|
import MultiSelectEmployee from '../../../components/common/MultiSelectEmployee'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { Team, TeamMember } from "../../../types/common";
|
import { Team, TeamMember } from '../../../types/common'
|
||||||
|
|
||||||
interface NewTeamModalProps {
|
interface NewTeamModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (team: Partial<Team>) => void;
|
onSave: (team: Partial<Team>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
const NewTeamModal: React.FC<NewTeamModalProps> = ({ isOpen, onClose, onSave }) => {
|
||||||
isOpen,
|
|
||||||
onClose,
|
|
||||||
onSave,
|
|
||||||
}) => {
|
|
||||||
const [teamData, setTeamData] = useState<Partial<Team>>({
|
const [teamData, setTeamData] = useState<Partial<Team>>({
|
||||||
code: "",
|
code: '',
|
||||||
name: "",
|
name: '',
|
||||||
description: "",
|
description: '',
|
||||||
isActive: true,
|
isActive: true,
|
||||||
specializations: [],
|
specializations: [],
|
||||||
members: [],
|
members: [],
|
||||||
});
|
})
|
||||||
|
|
||||||
const [selectedEmployees, setSelectedEmployees] = useState<string[]>([]);
|
const [selectedEmployees, setSelectedEmployees] = useState<string[]>([])
|
||||||
const [newSpecialization, setNewSpecialization] = useState("");
|
const [newSpecialization, setNewSpecialization] = useState('')
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (!teamData.code?.trim()) {
|
if (!teamData.code?.trim()) {
|
||||||
newErrors.teamCode = "Ekip kodu gerekli";
|
newErrors.teamCode = 'Ekip kodu gerekli'
|
||||||
}
|
}
|
||||||
if (!teamData.name?.trim()) {
|
if (!teamData.name?.trim()) {
|
||||||
newErrors.teamName = "Ekip adı gerekli";
|
newErrors.teamName = 'Ekip adı gerekli'
|
||||||
}
|
}
|
||||||
if (!teamData.description?.trim()) {
|
if (!teamData.description?.trim()) {
|
||||||
newErrors.description = "Açıklama gerekli";
|
newErrors.description = 'Açıklama gerekli'
|
||||||
}
|
}
|
||||||
if (!teamData.managerId) {
|
if (!teamData.managerId) {
|
||||||
newErrors.managerId = "Ekip yöneticisi seçilmeli";
|
newErrors.managerId = 'Ekip yöneticisi seçilmeli'
|
||||||
}
|
}
|
||||||
if (selectedEmployees.length === 0) {
|
if (selectedEmployees.length === 0) {
|
||||||
newErrors.members = "En az bir ekip üyesi seçilmeli";
|
newErrors.members = 'En az bir ekip üyesi seçilmeli'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (field: keyof Team, value: string | boolean | string[]) => {
|
||||||
field: keyof Team,
|
|
||||||
value: string | boolean | string[]
|
|
||||||
) => {
|
|
||||||
setTeamData((prev) => ({
|
setTeamData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
}));
|
}))
|
||||||
// Clear error when user starts typing
|
// Clear error when user starts typing
|
||||||
if (errors[field]) {
|
if (errors[field]) {
|
||||||
setErrors((prev) => ({
|
setErrors((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: "",
|
[field]: '',
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const addSpecialization = () => {
|
const addSpecialization = () => {
|
||||||
if (
|
if (newSpecialization.trim() && !teamData.specializations?.includes(newSpecialization.trim())) {
|
||||||
newSpecialization.trim() &&
|
|
||||||
!teamData.specializations?.includes(newSpecialization.trim())
|
|
||||||
) {
|
|
||||||
setTeamData((prev) => ({
|
setTeamData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
specializations: [
|
specializations: [...(prev.specializations || []), newSpecialization.trim()],
|
||||||
...(prev.specializations || []),
|
}))
|
||||||
newSpecialization.trim(),
|
setNewSpecialization('')
|
||||||
],
|
|
||||||
}));
|
|
||||||
setNewSpecialization("");
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeSpecialization = (index: number) => {
|
const removeSpecialization = (index: number) => {
|
||||||
setTeamData((prev) => ({
|
setTeamData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
specializations:
|
specializations: prev.specializations?.filter((_, i) => i !== index) || [],
|
||||||
prev.specializations?.filter((_, i) => i !== index) || [],
|
}))
|
||||||
}));
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const handleEmployeeSelection = (employees: string[]) => {
|
const handleEmployeeSelection = (employees: string[]) => {
|
||||||
setSelectedEmployees(employees);
|
setSelectedEmployees(employees)
|
||||||
if (errors.members) {
|
if (errors.members) {
|
||||||
setErrors((prev) => ({
|
setErrors((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
members: "",
|
members: '',
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleMemberRoleChange = (employeeName: string, role: TeamRoleEnum) => {
|
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) {
|
if (role === TeamRoleEnum.Lead && employee) {
|
||||||
handleInputChange("managerId", employee.id);
|
handleInputChange('managerId', employee.id)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
const members: TeamMember[] = selectedEmployees.map(
|
const members: TeamMember[] = selectedEmployees.map((employeeName, index) => {
|
||||||
(employeeName, index) => {
|
const employee = mockEmployees.find((emp) => emp.fullName === employeeName)
|
||||||
const employee = mockEmployees.find(
|
const isLeader = index === 0 // First selected employee becomes leader
|
||||||
(emp) => emp.fullName === employeeName
|
|
||||||
);
|
|
||||||
const isLeader = index === 0; // First selected employee becomes leader
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: `TM${Date.now()}-${index}`,
|
id: `TM${Date.now()}-${index}`,
|
||||||
teamId: "",
|
teamId: '',
|
||||||
employeeId: employee?.id || "",
|
employeeId: employee?.id || '',
|
||||||
employee: employee,
|
employee: employee,
|
||||||
role: isLeader ? TeamRoleEnum.Lead : TeamRoleEnum.Member,
|
role: isLeader ? TeamRoleEnum.Lead : TeamRoleEnum.Member,
|
||||||
joinDate: new Date(),
|
joinDate: new Date(),
|
||||||
isActive: true,
|
isActive: true,
|
||||||
};
|
|
||||||
}
|
}
|
||||||
);
|
})
|
||||||
|
|
||||||
const teamId = `MT${Date.now()}`;
|
const teamId = `MT${Date.now()}`
|
||||||
|
|
||||||
// Update members with the teamId
|
// Update members with the teamId
|
||||||
members.forEach((member) => {
|
members.forEach((member) => {
|
||||||
member.teamId = teamId;
|
member.teamId = teamId
|
||||||
});
|
})
|
||||||
|
|
||||||
const teamToSave: Partial<Team> = {
|
const teamToSave: Partial<Team> = {
|
||||||
...teamData,
|
...teamData,
|
||||||
id: teamId,
|
id: teamId,
|
||||||
managerId: members.find((m) => m.role === TeamRoleEnum.Lead)
|
managerId: members.find((m) => m.role === TeamRoleEnum.Lead)?.employeeId,
|
||||||
?.employeeId,
|
|
||||||
members,
|
members,
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
|
|
||||||
onSave(teamToSave);
|
onSave(teamToSave)
|
||||||
onClose();
|
onClose()
|
||||||
|
|
||||||
// Reset form
|
// Reset form
|
||||||
setTeamData({
|
setTeamData({
|
||||||
code: "",
|
code: '',
|
||||||
name: "",
|
name: '',
|
||||||
description: "",
|
description: '',
|
||||||
isActive: true,
|
isActive: true,
|
||||||
specializations: [],
|
specializations: [],
|
||||||
members: [],
|
members: [],
|
||||||
});
|
})
|
||||||
setSelectedEmployees([]);
|
setSelectedEmployees([])
|
||||||
setNewSpecialization("");
|
setNewSpecialization('')
|
||||||
setErrors({});
|
setErrors({})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isOpen && (
|
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">
|
<div className="bg-white rounded-lg w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">
|
<h2 className="text-lg font-semibold text-gray-900">Yeni Ekip Oluştur</h2>
|
||||||
Yeni Ekip Oluştur
|
|
||||||
</h2>
|
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
className="text-gray-400 hover:text-gray-600 transition-colors"
|
||||||
|
|
@ -188,91 +167,69 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
||||||
{/* Basic Info */}
|
{/* Basic Info */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Ekip Kodu *</label>
|
||||||
Ekip Kodu *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={teamData.code || ""}
|
value={teamData.code || ''}
|
||||||
onChange={(e) => handleInputChange("code", e.target.value)}
|
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 ${
|
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"
|
placeholder="MEC-001"
|
||||||
/>
|
/>
|
||||||
{errors.code && (
|
{errors.code && <p className="text-red-500 text-xs mt-1">{errors.code}</p>}
|
||||||
<p className="text-red-500 text-xs mt-1">{errors.code}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Ekip Adı *</label>
|
||||||
Ekip Adı *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={teamData.name || ""}
|
value={teamData.name || ''}
|
||||||
onChange={(e) => handleInputChange("name", e.target.value)}
|
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 ${
|
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"
|
placeholder="Mekanik Bakım Ekibi"
|
||||||
/>
|
/>
|
||||||
{errors.name && (
|
{errors.name && <p className="text-red-500 text-xs mt-1">{errors.name}</p>}
|
||||||
<p className="text-red-500 text-xs mt-1">{errors.name}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama *</label>
|
||||||
Açıklama *
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={teamData.description || ""}
|
value={teamData.description || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||||
handleInputChange("description", e.target.value)
|
|
||||||
}
|
|
||||||
rows={2}
|
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 ${
|
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ı"
|
placeholder="Ekip açıklaması ve sorumlulukları"
|
||||||
/>
|
/>
|
||||||
{errors.description && (
|
{errors.description && (
|
||||||
<p className="text-red-500 text-xs mt-1">
|
<p className="text-red-500 text-xs mt-1">{errors.description}</p>
|
||||||
{errors.description}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Team Members */}
|
{/* Team Members */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Ekip Üyeleri *</label>
|
||||||
Ekip Üyeleri *
|
|
||||||
</label>
|
|
||||||
<MultiSelectEmployee
|
<MultiSelectEmployee
|
||||||
selectedEmployees={selectedEmployees}
|
selectedEmployees={selectedEmployees}
|
||||||
onChange={handleEmployeeSelection}
|
onChange={handleEmployeeSelection}
|
||||||
placeholder="Ekip üyelerini seçin"
|
placeholder="Ekip üyelerini seçin"
|
||||||
className={errors.members ? "border-red-500" : ""}
|
className={errors.members ? 'border-red-500' : ''}
|
||||||
/>
|
/>
|
||||||
{errors.members && (
|
{errors.members && <p className="text-red-500 text-xs mt-1">{errors.members}</p>}
|
||||||
<p className="text-red-500 text-xs mt-1">{errors.members}</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Selected Members Display */}
|
{/* Selected Members Display */}
|
||||||
{selectedEmployees.length > 0 && (
|
{selectedEmployees.length > 0 && (
|
||||||
<div className="mt-3 p-3 bg-gray-50 rounded-lg">
|
<div className="mt-3 p-3 bg-gray-50 rounded-lg">
|
||||||
<h4 className="text-sm font-medium text-gray-700 mb-2">
|
<h4 className="text-sm font-medium text-gray-700 mb-2">Seçili Ekip Üyeleri:</h4>
|
||||||
Seçili Ekip Üyeleri:
|
|
||||||
</h4>
|
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
{selectedEmployees.map((employeeName, index) => {
|
{selectedEmployees.map((employeeName, index) => {
|
||||||
const employee = mockEmployees.find(
|
const employee = mockEmployees.find((emp) => emp.fullName === employeeName)
|
||||||
(emp) => emp.fullName === employeeName
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
|
|
@ -286,12 +243,8 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-900">
|
<p className="text-sm font-medium text-gray-900">{employeeName}</p>
|
||||||
{employeeName}
|
<p className="text-sm text-gray-600">{employee?.jobPosition?.name}</p>
|
||||||
</p>
|
|
||||||
<p className="text-sm text-gray-600">
|
|
||||||
{employee?.jobPosition?.name}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -302,25 +255,18 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
||||||
: TeamRoleEnum.Member
|
: TeamRoleEnum.Member
|
||||||
}
|
}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleMemberRoleChange(
|
handleMemberRoleChange(employeeName, e.target.value as TeamRoleEnum)
|
||||||
employeeName,
|
|
||||||
e.target.value as TeamRoleEnum
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
className="text-xs px-1.5 py-1 border border-gray-300 rounded"
|
className="text-xs px-1.5 py-1 border border-gray-300 rounded"
|
||||||
>
|
>
|
||||||
<option value={TeamRoleEnum.Member}>Üye</option>
|
<option value={TeamRoleEnum.Member}>Üye</option>
|
||||||
<option value={TeamRoleEnum.Lead}>Lider</option>
|
<option value={TeamRoleEnum.Lead}>Lider</option>
|
||||||
<option value={TeamRoleEnum.Specialist}>
|
<option value={TeamRoleEnum.Specialist}>Uzman</option>
|
||||||
Uzman
|
<option value={TeamRoleEnum.Manager}>Yönetici</option>
|
||||||
</option>
|
|
||||||
<option value={TeamRoleEnum.Manager}>
|
|
||||||
Yönetici
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</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"
|
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"
|
placeholder="Uzmanlık alanı ekle"
|
||||||
onKeyPress={(e) => {
|
onKeyPress={(e) => {
|
||||||
if (e.key === "Enter") {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
addSpecialization();
|
addSpecialization()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -355,26 +301,25 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
||||||
<FaPlus className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{teamData.specializations &&
|
{teamData.specializations && teamData.specializations.length > 0 && (
|
||||||
teamData.specializations.length > 0 && (
|
<div className="flex flex-wrap gap-2">
|
||||||
<div className="flex flex-wrap gap-2">
|
{teamData.specializations.map((spec, index) => (
|
||||||
{teamData.specializations.map((spec, index) => (
|
<span
|
||||||
<span
|
key={index}
|
||||||
key={index}
|
className="inline-flex items-center px-2.5 py-1 rounded-full text-xs bg-blue-100 text-blue-800"
|
||||||
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}
|
<FaMinus className="w-3 h-3" />
|
||||||
<button
|
</button>
|
||||||
type="button"
|
</span>
|
||||||
onClick={() => removeSpecialization(index)}
|
))}
|
||||||
className="ml-2 text-blue-600 hover:text-blue-800"
|
</div>
|
||||||
>
|
)}
|
||||||
<FaMinus className="w-3 h-3" />
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -384,9 +329,7 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={teamData.isActive || false}
|
checked={teamData.isActive || false}
|
||||||
onChange={(e) =>
|
onChange={(e) => handleInputChange('isActive', e.target.checked)}
|
||||||
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"
|
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>
|
<span className="ml-2 text-sm text-gray-700">Ekip aktif</span>
|
||||||
|
|
@ -413,7 +356,7 @@ const NewTeamModal: React.FC<NewTeamModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default NewTeamModal;
|
export default NewTeamModal
|
||||||
|
|
|
||||||
|
|
@ -1,95 +1,82 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaPlus, FaMinus } from "react-icons/fa";
|
import { FaTimes, FaSave, FaPlus, FaMinus } from 'react-icons/fa'
|
||||||
import {
|
import {
|
||||||
PmWorkCenter,
|
PmWorkCenter,
|
||||||
WorkCenterStatusEnum,
|
WorkCenterStatusEnum,
|
||||||
CriticalityLevelEnum,
|
CriticalityLevelEnum,
|
||||||
PmWorkCenterSpecification,
|
PmWorkCenterSpecification,
|
||||||
} from "../../../types/pm";
|
} from '../../../types/pm'
|
||||||
|
|
||||||
interface NewWorkCenterModalProps {
|
interface NewWorkCenterModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (workCenter: Partial<PmWorkCenter>) => void;
|
onSave: (workCenter: Partial<PmWorkCenter>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialWorkCenter: Partial<PmWorkCenter> = {
|
const initialWorkCenter: Partial<PmWorkCenter> = {
|
||||||
code: "",
|
code: '',
|
||||||
name: "",
|
name: '',
|
||||||
description: "",
|
description: '',
|
||||||
manufacturer: "",
|
manufacturer: '',
|
||||||
model: "",
|
model: '',
|
||||||
serialNumber: "",
|
serialNumber: '',
|
||||||
installationDate: new Date(),
|
installationDate: new Date(),
|
||||||
location: "",
|
location: '',
|
||||||
departmentId: "",
|
departmentId: '',
|
||||||
status: WorkCenterStatusEnum.Operational,
|
status: WorkCenterStatusEnum.Operational,
|
||||||
criticality: CriticalityLevelEnum.Medium,
|
criticality: CriticalityLevelEnum.Medium,
|
||||||
specifications: [],
|
specifications: [],
|
||||||
isActive: true,
|
isActive: true,
|
||||||
};
|
}
|
||||||
|
|
||||||
const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({ isOpen, onClose, onSave }) => {
|
||||||
isOpen,
|
const [workCenter, setWorkCenter] = useState<Partial<PmWorkCenter>>(initialWorkCenter)
|
||||||
onClose,
|
const [specifications, setSpecifications] = useState<PmWorkCenterSpecification[]>([])
|
||||||
onSave,
|
|
||||||
}) => {
|
|
||||||
const [workCenter, setWorkCenter] =
|
|
||||||
useState<Partial<PmWorkCenter>>(initialWorkCenter);
|
|
||||||
const [specifications, setSpecifications] = useState<
|
|
||||||
PmWorkCenterSpecification[]
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (
|
||||||
e: React.ChangeEvent<
|
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
|
||||||
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
|
|
||||||
>
|
|
||||||
) => {
|
) => {
|
||||||
const { name, value, type } = e.target;
|
const { name, value, type } = e.target
|
||||||
setWorkCenter((prev) => ({
|
setWorkCenter((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[name]: type === "date" ? new Date(value) : value,
|
[name]: type === 'date' ? new Date(value) : value,
|
||||||
}));
|
}))
|
||||||
};
|
}
|
||||||
|
|
||||||
const addSpecification = () => {
|
const addSpecification = () => {
|
||||||
const newSpec: PmWorkCenterSpecification = {
|
const newSpec: PmWorkCenterSpecification = {
|
||||||
id: `SPEC${Date.now()}`,
|
id: `SPEC${Date.now()}`,
|
||||||
workCenterId: "",
|
workCenterId: '',
|
||||||
specificationName: "",
|
specificationName: '',
|
||||||
specificationValue: "",
|
specificationValue: '',
|
||||||
unit: "",
|
unit: '',
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
};
|
}
|
||||||
setSpecifications([...specifications, newSpec]);
|
setSpecifications([...specifications, newSpec])
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeSpecification = (index: number) => {
|
const removeSpecification = (index: number) => {
|
||||||
setSpecifications(specifications.filter((_, i) => i !== index));
|
setSpecifications(specifications.filter((_, i) => i !== index))
|
||||||
};
|
}
|
||||||
|
|
||||||
const updateSpecification = (
|
const updateSpecification = (index: number, field: string, value: string | boolean) => {
|
||||||
index: number,
|
|
||||||
field: string,
|
|
||||||
value: string | boolean
|
|
||||||
) => {
|
|
||||||
const updated = specifications.map((spec, i) =>
|
const updated = specifications.map((spec, i) =>
|
||||||
i === index ? { ...spec, [field]: value } : spec
|
i === index ? { ...spec, [field]: value } : spec,
|
||||||
);
|
)
|
||||||
setSpecifications(updated);
|
setSpecifications(updated)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
const workCenterData = {
|
const workCenterData = {
|
||||||
...workCenter,
|
...workCenter,
|
||||||
specifications,
|
specifications,
|
||||||
workCenterType: {
|
workCenterType: {
|
||||||
id: "ET001",
|
id: 'ET001',
|
||||||
code: "GENERAL",
|
code: 'GENERAL',
|
||||||
name: "Genel İş Merkezi",
|
name: 'Genel İş Merkezi',
|
||||||
category: "Genel",
|
category: 'Genel',
|
||||||
isActive: true,
|
isActive: true,
|
||||||
},
|
},
|
||||||
maintenancePlans: [],
|
maintenancePlans: [],
|
||||||
|
|
@ -97,25 +84,20 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
downTimeHistory: [],
|
downTimeHistory: [],
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
};
|
}
|
||||||
onSave(workCenterData);
|
onSave(workCenterData)
|
||||||
setWorkCenter(initialWorkCenter);
|
setWorkCenter(initialWorkCenter)
|
||||||
setSpecifications([]);
|
setSpecifications([])
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<div className="bg-white rounded-lg w-full max-w-4xl mx-4 max-h-[90vh] overflow-y-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">
|
<h2 className="text-lg font-semibold text-gray-900">Yeni İş Merkezi Ekle</h2>
|
||||||
Yeni İş Merkezi Ekle
|
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||||
</h2>
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -124,9 +106,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-4 space-y-4">
|
||||||
{/* Basic Information */}
|
{/* Basic Information */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Temel Bilgiler</h3>
|
||||||
Temel Bilgiler
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
|
@ -135,7 +115,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="workCenterCode"
|
name="workCenterCode"
|
||||||
value={workCenter.code || ""}
|
value={workCenter.code || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
placeholder="örn: CNC-001"
|
||||||
|
|
@ -149,7 +129,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="name"
|
name="name"
|
||||||
value={workCenter.name || ""}
|
value={workCenter.name || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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ı"
|
placeholder="örn: CNC Torna Tezgahı"
|
||||||
|
|
@ -157,12 +137,10 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama</label>
|
||||||
Açıklama
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
name="description"
|
name="description"
|
||||||
value={workCenter.description || ""}
|
value={workCenter.description || ''}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
rows={2}
|
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"
|
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 */}
|
{/* Technical Details */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Teknik Detaylar</h3>
|
||||||
Teknik Detaylar
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Üretici</label>
|
||||||
Üretici
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="manufacturer"
|
name="manufacturer"
|
||||||
value={workCenter.manufacturer || ""}
|
value={workCenter.manufacturer || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
placeholder="örn: HAAS Automation"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Model</label>
|
||||||
Model
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="model"
|
name="model"
|
||||||
value={workCenter.model || ""}
|
value={workCenter.model || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
placeholder="örn: ST-30"
|
||||||
|
|
@ -211,7 +183,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="serialNumber"
|
name="serialNumber"
|
||||||
value={workCenter.serialNumber || ""}
|
value={workCenter.serialNumber || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
placeholder="örn: SN123456789"
|
||||||
|
|
@ -226,8 +198,8 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
name="installationDate"
|
name="installationDate"
|
||||||
value={
|
value={
|
||||||
workCenter.installationDate
|
workCenter.installationDate
|
||||||
? workCenter.installationDate.toISOString().split("T")[0]
|
? workCenter.installationDate.toISOString().split('T')[0]
|
||||||
: ""
|
: ''
|
||||||
}
|
}
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
name="warrantyExpiry"
|
||||||
value={
|
value={
|
||||||
workCenter.warrantyExpiry
|
workCenter.warrantyExpiry
|
||||||
? workCenter.warrantyExpiry.toISOString().split("T")[0]
|
? workCenter.warrantyExpiry.toISOString().split('T')[0]
|
||||||
: ""
|
: ''
|
||||||
}
|
}
|
||||||
onChange={handleInputChange}
|
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"
|
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>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Lokasyon *</label>
|
||||||
Lokasyon *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="location"
|
name="location"
|
||||||
value={workCenter.location || ""}
|
value={workCenter.location || ''}
|
||||||
onChange={handleInputChange}
|
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"
|
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"
|
placeholder="örn: Atölye A - Hat 1"
|
||||||
|
|
@ -269,29 +239,19 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
|
|
||||||
{/* Status and Priority */}
|
{/* Status and Priority */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Durum ve Öncelik</h3>
|
||||||
Durum ve Öncelik
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Durum</label>
|
||||||
Durum
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
name="status"
|
name="status"
|
||||||
value={workCenter.status || WorkCenterStatusEnum.Operational}
|
value={workCenter.status || WorkCenterStatusEnum.Operational}
|
||||||
onChange={handleInputChange}
|
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"
|
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}>
|
<option value={WorkCenterStatusEnum.Operational}>Operasyonel</option>
|
||||||
Operasyonel
|
<option value={WorkCenterStatusEnum.UnderMaintenance}>Bakımda</option>
|
||||||
</option>
|
<option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option>
|
||||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>
|
|
||||||
Bakımda
|
|
||||||
</option>
|
|
||||||
<option value={WorkCenterStatusEnum.OutOfOrder}>
|
|
||||||
Arızalı
|
|
||||||
</option>
|
|
||||||
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -317,9 +277,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
{/* Specifications */}
|
{/* Specifications */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<h3 className="text-base font-medium text-gray-900">
|
<h3 className="text-base font-medium text-gray-900">Teknik Özellikler</h3>
|
||||||
Teknik Özellikler
|
|
||||||
</h3>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={addSpecification}
|
onClick={addSpecification}
|
||||||
|
|
@ -331,19 +289,12 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{specifications.map((spec, index) => (
|
{specifications.map((spec, index) => (
|
||||||
<div
|
<div key={index} className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg">
|
||||||
key={index}
|
|
||||||
className="flex items-center space-x-2 p-2 bg-gray-50 rounded-lg"
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={spec.specificationName}
|
value={spec.specificationName}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateSpecification(
|
updateSpecification(index, 'specificationName', e.target.value)
|
||||||
index,
|
|
||||||
"specificationName",
|
|
||||||
e.target.value
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
placeholder="Özellik adı"
|
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"
|
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"
|
type="text"
|
||||||
value={spec.specificationValue}
|
value={spec.specificationValue}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateSpecification(
|
updateSpecification(index, 'specificationValue', e.target.value)
|
||||||
index,
|
|
||||||
"specificationValue",
|
|
||||||
e.target.value
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
placeholder="Değer"
|
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"
|
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
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={spec.unit || ""}
|
value={spec.unit || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateSpecification(index, 'unit', e.target.value)}
|
||||||
updateSpecification(index, "unit", e.target.value)
|
|
||||||
}
|
|
||||||
placeholder="Birim"
|
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"
|
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
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={spec.isRequired}
|
checked={spec.isRequired}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateSpecification(index, 'isRequired', e.target.checked)}
|
||||||
updateSpecification(
|
|
||||||
index,
|
|
||||||
"isRequired",
|
|
||||||
e.target.checked
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500 h-4 w-4"
|
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>
|
<span className="text-sm text-gray-700">Zorunlu</span>
|
||||||
|
|
@ -396,8 +335,8 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
))}
|
))}
|
||||||
{specifications.length === 0 && (
|
{specifications.length === 0 && (
|
||||||
<p className="text-gray-500 text-center py-4">
|
<p className="text-gray-500 text-center py-4">
|
||||||
Henüz teknik özellik eklenmedi. "Özellik Ekle" butonunu
|
Henüz teknik özellik eklenmedi. "Özellik Ekle" butonunu kullanarak özellik
|
||||||
kullanarak özellik ekleyebilirsiniz.
|
ekleyebilirsiniz.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -424,7 +363,7 @@ const NewWorkCenterModal: React.FC<NewWorkCenterModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default NewWorkCenterModal;
|
export default NewWorkCenterModal
|
||||||
|
|
|
||||||
|
|
@ -1,85 +1,76 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaPlus, FaTrash, FaCalendar, FaClock } from "react-icons/fa";
|
import { FaTimes, FaPlus, FaTrash, FaCalendar, FaClock } from 'react-icons/fa'
|
||||||
import {
|
import {
|
||||||
PmMaintenanceWorkOrder,
|
PmMaintenanceWorkOrder,
|
||||||
WorkOrderTypeEnum,
|
WorkOrderTypeEnum,
|
||||||
WorkOrderStatusEnum,
|
WorkOrderStatusEnum,
|
||||||
PmWorkOrderMaterial,
|
PmWorkOrderMaterial,
|
||||||
PmWorkOrderActivity,
|
PmWorkOrderActivity,
|
||||||
} from "../../../types/pm";
|
} from '../../../types/pm'
|
||||||
import { mockWorkCenters } from "../../../mocks/mockWorkCenters";
|
import { mockWorkCenters } from '../../../mocks/mockWorkCenters'
|
||||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { mockMaterials } from "../../../mocks/mockMaterials";
|
import { mockMaterials } from '../../../mocks/mockMaterials'
|
||||||
import { PriorityEnum } from "../../../types/common";
|
import { PriorityEnum } from '../../../types/common'
|
||||||
|
|
||||||
interface NewWorkOrderModalProps {
|
interface NewWorkOrderModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (
|
onSave: (
|
||||||
workOrder: Omit<
|
workOrder: Omit<PmMaintenanceWorkOrder, 'id' | 'creationTime' | 'lastModificationTime'>,
|
||||||
PmMaintenanceWorkOrder,
|
) => void
|
||||||
"id" | "creationTime" | "lastModificationTime"
|
|
||||||
>
|
|
||||||
) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({ isOpen, onClose, onSave }) => {
|
||||||
isOpen,
|
|
||||||
onClose,
|
|
||||||
onSave,
|
|
||||||
}) => {
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
workOrderNumber: `WO-${Date.now()}`,
|
workOrderNumber: `WO-${Date.now()}`,
|
||||||
workCenterId: "",
|
workCenterId: '',
|
||||||
orderType: WorkOrderTypeEnum.Corrective,
|
orderType: WorkOrderTypeEnum.Corrective,
|
||||||
priority: PriorityEnum.Normal,
|
priority: PriorityEnum.Normal,
|
||||||
status: WorkOrderStatusEnum.Created,
|
status: WorkOrderStatusEnum.Created,
|
||||||
description: "",
|
description: '',
|
||||||
reportedBy: "",
|
reportedBy: '',
|
||||||
assignedTo: "",
|
assignedTo: '',
|
||||||
maintenanceTeamId: "",
|
maintenanceTeamId: '',
|
||||||
scheduledStart: "",
|
scheduledStart: '',
|
||||||
scheduledEnd: "",
|
scheduledEnd: '',
|
||||||
estimatedCost: 0,
|
estimatedCost: 0,
|
||||||
notes: "",
|
notes: '',
|
||||||
});
|
})
|
||||||
|
|
||||||
const [materials, setMaterials] = useState<
|
const [materials, setMaterials] = useState<Omit<PmWorkOrderMaterial, 'id' | 'workOrderId'>[]>([])
|
||||||
Omit<PmWorkOrderMaterial, "id" | "workOrderId">[]
|
const [activities, setActivities] = useState<Omit<PmWorkOrderActivity, 'id' | 'workOrderId'>[]>(
|
||||||
>([]);
|
[],
|
||||||
const [activities, setActivities] = useState<
|
)
|
||||||
Omit<PmWorkOrderActivity, "id" | "workOrderId">[]
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
>([]);
|
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (!formData.description.trim()) {
|
if (!formData.description.trim()) {
|
||||||
newErrors.description = "Açıklama alanı zorunludur";
|
newErrors.description = 'Açıklama alanı zorunludur'
|
||||||
}
|
}
|
||||||
if (!formData.workCenterId) {
|
if (!formData.workCenterId) {
|
||||||
newErrors.workCenterId = "İş Merkezi seçimi zorunludur";
|
newErrors.workCenterId = 'İş Merkezi seçimi zorunludur'
|
||||||
}
|
}
|
||||||
if (!formData.reportedBy.trim()) {
|
if (!formData.reportedBy.trim()) {
|
||||||
newErrors.reportedBy = "Bildiren kişi zorunludur";
|
newErrors.reportedBy = 'Bildiren kişi zorunludur'
|
||||||
}
|
}
|
||||||
if (formData.estimatedCost < 0) {
|
if (formData.estimatedCost < 0) {
|
||||||
newErrors.estimatedCost = "Tahmini maliyet negatif olamaz";
|
newErrors.estimatedCost = 'Tahmini maliyet negatif olamaz'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
if (!validateForm()) return;
|
if (!validateForm()) return
|
||||||
|
|
||||||
const newWorkOrder: Omit<
|
const newWorkOrder: Omit<
|
||||||
PmMaintenanceWorkOrder,
|
PmMaintenanceWorkOrder,
|
||||||
"id" | "creationTime" | "lastModificationTime"
|
'id' | 'creationTime' | 'lastModificationTime'
|
||||||
> = {
|
> = {
|
||||||
workOrderNumber: formData.workOrderNumber,
|
workOrderNumber: formData.workOrderNumber,
|
||||||
workCenterId: formData.workCenterId,
|
workCenterId: formData.workCenterId,
|
||||||
|
|
@ -90,12 +81,8 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
reportedBy: formData.reportedBy,
|
reportedBy: formData.reportedBy,
|
||||||
assignedTo: formData.assignedTo || undefined,
|
assignedTo: formData.assignedTo || undefined,
|
||||||
maintenanceTeamId: formData.maintenanceTeamId || undefined,
|
maintenanceTeamId: formData.maintenanceTeamId || undefined,
|
||||||
scheduledStart: formData.scheduledStart
|
scheduledStart: formData.scheduledStart ? new Date(formData.scheduledStart) : undefined,
|
||||||
? new Date(formData.scheduledStart)
|
scheduledEnd: formData.scheduledEnd ? new Date(formData.scheduledEnd) : undefined,
|
||||||
: undefined,
|
|
||||||
scheduledEnd: formData.scheduledEnd
|
|
||||||
? new Date(formData.scheduledEnd)
|
|
||||||
: undefined,
|
|
||||||
actualStart: undefined,
|
actualStart: undefined,
|
||||||
actualEnd: undefined,
|
actualEnd: undefined,
|
||||||
estimatedCost: formData.estimatedCost,
|
estimatedCost: formData.estimatedCost,
|
||||||
|
|
@ -103,51 +90,47 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
materials: materials.map((material, index) => ({
|
materials: materials.map((material, index) => ({
|
||||||
...material,
|
...material,
|
||||||
id: `mat-${index}`,
|
id: `mat-${index}`,
|
||||||
workOrderId: "",
|
workOrderId: '',
|
||||||
totalCost: material.plannedQuantity * material.unitCost,
|
totalCost: material.plannedQuantity * material.unitCost,
|
||||||
})),
|
})),
|
||||||
activities: activities.map((activity, index) => ({
|
activities: activities.map((activity, index) => ({
|
||||||
...activity,
|
...activity,
|
||||||
id: `act-${index}`,
|
id: `act-${index}`,
|
||||||
workOrderId: "",
|
workOrderId: '',
|
||||||
actualDuration: 0,
|
actualDuration: 0,
|
||||||
completedAt: undefined,
|
completedAt: undefined,
|
||||||
})),
|
})),
|
||||||
notes: formData.notes || undefined,
|
notes: formData.notes || undefined,
|
||||||
completionNotes: undefined,
|
completionNotes: undefined,
|
||||||
};
|
}
|
||||||
|
|
||||||
onSave(newWorkOrder);
|
onSave(newWorkOrder)
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const addMaterial = () => {
|
const addMaterial = () => {
|
||||||
setMaterials([
|
setMaterials([
|
||||||
...materials,
|
...materials,
|
||||||
{
|
{
|
||||||
materialId: "",
|
materialId: '',
|
||||||
materialCode: "",
|
materialCode: '',
|
||||||
materialName: "",
|
materialName: '',
|
||||||
plannedQuantity: 1,
|
plannedQuantity: 1,
|
||||||
actualQuantity: 0,
|
actualQuantity: 0,
|
||||||
unitCost: 0,
|
unitCost: 0,
|
||||||
totalCost: 0,
|
totalCost: 0,
|
||||||
},
|
},
|
||||||
]);
|
])
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeMaterial = (index: number) => {
|
const removeMaterial = (index: number) => {
|
||||||
setMaterials(materials.filter((_, i) => i !== index));
|
setMaterials(materials.filter((_, i) => i !== index))
|
||||||
};
|
}
|
||||||
|
|
||||||
const updateMaterial = (
|
const updateMaterial = (index: number, field: string, value: string | number) => {
|
||||||
index: number,
|
const updated = [...materials]
|
||||||
field: string,
|
if (field === 'materialId') {
|
||||||
value: string | number
|
const selectedMaterial = mockMaterials.find((m) => m.id === value)
|
||||||
) => {
|
|
||||||
const updated = [...materials];
|
|
||||||
if (field === "materialId") {
|
|
||||||
const selectedMaterial = mockMaterials.find((m) => m.id === value);
|
|
||||||
if (selectedMaterial) {
|
if (selectedMaterial) {
|
||||||
updated[index] = {
|
updated[index] = {
|
||||||
...updated[index],
|
...updated[index],
|
||||||
|
|
@ -155,66 +138,59 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
materialCode: selectedMaterial.code,
|
materialCode: selectedMaterial.code,
|
||||||
materialName: selectedMaterial.name,
|
materialName: selectedMaterial.name,
|
||||||
unitCost: selectedMaterial.costPrice,
|
unitCost: selectedMaterial.costPrice,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const material = updated[index];
|
const material = updated[index]
|
||||||
if (field === "plannedQuantity") {
|
if (field === 'plannedQuantity') {
|
||||||
material.plannedQuantity = value as number;
|
material.plannedQuantity = value as number
|
||||||
} else if (field === "unitCost") {
|
} else if (field === 'unitCost') {
|
||||||
material.unitCost = value as number;
|
material.unitCost = value as number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setMaterials(updated);
|
setMaterials(updated)
|
||||||
};
|
}
|
||||||
|
|
||||||
const addActivity = () => {
|
const addActivity = () => {
|
||||||
setActivities([
|
setActivities([
|
||||||
...activities,
|
...activities,
|
||||||
{
|
{
|
||||||
activityDescription: "",
|
activityDescription: '',
|
||||||
plannedDuration: 60,
|
plannedDuration: 60,
|
||||||
actualDuration: 0,
|
actualDuration: 0,
|
||||||
performedBy: "",
|
performedBy: '',
|
||||||
notes: "",
|
notes: '',
|
||||||
},
|
},
|
||||||
]);
|
])
|
||||||
};
|
}
|
||||||
|
|
||||||
const removeActivity = (index: number) => {
|
const removeActivity = (index: number) => {
|
||||||
setActivities(activities.filter((_, i) => i !== index));
|
setActivities(activities.filter((_, i) => i !== index))
|
||||||
};
|
}
|
||||||
|
|
||||||
const updateActivity = (
|
const updateActivity = (index: number, field: string, value: string | number) => {
|
||||||
index: number,
|
const updated = [...activities]
|
||||||
field: string,
|
const activity = updated[index]
|
||||||
value: string | number
|
if (field === 'activityDescription') {
|
||||||
) => {
|
activity.activityDescription = value as string
|
||||||
const updated = [...activities];
|
} else if (field === 'plannedDuration') {
|
||||||
const activity = updated[index];
|
activity.plannedDuration = value as number
|
||||||
if (field === "activityDescription") {
|
} else if (field === 'performedBy') {
|
||||||
activity.activityDescription = value as string;
|
activity.performedBy = value as string
|
||||||
} else if (field === "plannedDuration") {
|
} else if (field === 'notes') {
|
||||||
activity.plannedDuration = value as number;
|
activity.notes = value as string
|
||||||
} 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 (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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="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">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h3 className="text-lg font-semibold text-gray-900">Yeni İş Emri</h3>
|
<h3 className="text-lg font-semibold text-gray-900">Yeni İş Emri</h3>
|
||||||
<button
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5" />
|
<FaTimes className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -223,31 +199,23 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
{/* Basic Information */}
|
{/* Basic Information */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">İş Emri No</label>
|
||||||
İş Emri No
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.workOrderNumber}
|
value={formData.workOrderNumber}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, workOrderNumber: e.target.value })}
|
||||||
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"
|
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
|
readOnly
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">İş Merkezi *</label>
|
||||||
İş Merkezi *
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.workCenterId}
|
value={formData.workCenterId}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, workCenterId: e.target.value })}
|
||||||
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 ${
|
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>
|
<option value="">İş Merkezi Seçin</option>
|
||||||
|
|
@ -258,16 +226,12 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.workCenterId && (
|
{errors.workCenterId && (
|
||||||
<p className="mt-1 text-xs text-red-600">
|
<p className="mt-1 text-xs text-red-600">{errors.workCenterId}</p>
|
||||||
{errors.workCenterId}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">İş Emri Tipi</label>
|
||||||
İş Emri Tipi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.orderType}
|
value={formData.orderType}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
|
@ -282,16 +246,12 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
<option value={WorkOrderTypeEnum.Corrective}>Düzeltici</option>
|
<option value={WorkOrderTypeEnum.Corrective}>Düzeltici</option>
|
||||||
<option value={WorkOrderTypeEnum.Emergency}>Acil</option>
|
<option value={WorkOrderTypeEnum.Emergency}>Acil</option>
|
||||||
<option value={WorkOrderTypeEnum.Inspection}>İnceleme</option>
|
<option value={WorkOrderTypeEnum.Inspection}>İnceleme</option>
|
||||||
<option value={WorkOrderTypeEnum.Calibration}>
|
<option value={WorkOrderTypeEnum.Calibration}>Kalibrasyon</option>
|
||||||
Kalibrasyon
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Öncelik</label>
|
||||||
Öncelik
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.priority}
|
value={formData.priority}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
|
@ -311,17 +271,13 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Açıklama *</label>
|
||||||
Açıklama *
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||||
setFormData({ ...formData, description: e.target.value })
|
|
||||||
}
|
|
||||||
rows={2}
|
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 ${
|
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ı..."
|
placeholder="İş emri açıklaması..."
|
||||||
/>
|
/>
|
||||||
|
|
@ -333,17 +289,13 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
{/* Assignment */}
|
{/* Assignment */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Bildiren *</label>
|
||||||
Bildiren *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.reportedBy}
|
value={formData.reportedBy}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, reportedBy: e.target.value })}
|
||||||
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 ${
|
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ı"
|
placeholder="Bildiren kişi adı"
|
||||||
/>
|
/>
|
||||||
|
|
@ -353,14 +305,10 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Atanan Kişi</label>
|
||||||
Atanan Kişi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.assignedTo}
|
value={formData.assignedTo}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, assignedTo: e.target.value })}
|
||||||
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"
|
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>
|
<option value="">Kişi Seçin</option>
|
||||||
|
|
@ -373,9 +321,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Bakım Ekibi</label>
|
||||||
Bakım Ekibi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.maintenanceTeamId}
|
value={formData.maintenanceTeamId}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
|
@ -406,9 +352,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={formData.scheduledStart}
|
value={formData.scheduledStart}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, scheduledStart: e.target.value })}
|
||||||
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"
|
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>
|
||||||
|
|
@ -421,9 +365,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={formData.scheduledEnd}
|
value={formData.scheduledEnd}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, scheduledEnd: e.target.value })}
|
||||||
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"
|
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>
|
||||||
|
|
@ -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 ${
|
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"
|
placeholder="0.00"
|
||||||
/>
|
/>
|
||||||
{errors.estimatedCost && (
|
{errors.estimatedCost && (
|
||||||
<p className="mt-1 text-xs text-red-600">
|
<p className="mt-1 text-xs text-red-600">{errors.estimatedCost}</p>
|
||||||
{errors.estimatedCost}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -459,9 +399,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
{/* Materials */}
|
{/* Materials */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h4 className="text-base font-medium text-gray-900">
|
<h4 className="text-base font-medium text-gray-900">Malzemeler</h4>
|
||||||
Malzemeler
|
|
||||||
</h4>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={addMaterial}
|
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 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 className="grid grid-cols-1 md:grid-cols-5 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Malzeme</label>
|
||||||
Malzeme
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={material.materialId}
|
value={material.materialId}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateMaterial(index, 'materialId', e.target.value)}
|
||||||
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"
|
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>
|
<option value="">Malzeme Seçin</option>
|
||||||
|
|
@ -503,11 +437,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
min="0"
|
min="0"
|
||||||
value={material.plannedQuantity}
|
value={material.plannedQuantity}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateMaterial(
|
updateMaterial(index, 'plannedQuantity', parseInt(e.target.value) || 0)
|
||||||
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"
|
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"
|
step="0.01"
|
||||||
value={material.unitCost}
|
value={material.unitCost}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateMaterial(
|
updateMaterial(index, 'unitCost', parseFloat(e.target.value) || 0)
|
||||||
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"
|
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>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Toplam</label>
|
||||||
Toplam
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={`₺${(
|
value={`₺${(material.plannedQuantity * material.unitCost).toFixed(2)}`}
|
||||||
material.plannedQuantity * material.unitCost
|
|
||||||
).toFixed(2)}`}
|
|
||||||
readOnly
|
readOnly
|
||||||
className="w-full px-2.5 py-1.5 text-sm border border-gray-300 rounded-lg bg-gray-100"
|
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 */}
|
{/* Activities */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h4 className="text-base font-medium text-gray-900">
|
<h4 className="text-base font-medium text-gray-900">Aktiviteler</h4>
|
||||||
Aktiviteler
|
|
||||||
</h4>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={addActivity}
|
onClick={addActivity}
|
||||||
|
|
@ -584,13 +504,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={activity.activityDescription}
|
value={activity.activityDescription}
|
||||||
onChange={(e) =>
|
onChange={(e) => updateActivity(index, 'activityDescription', e.target.value)}
|
||||||
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"
|
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ı"
|
placeholder="Aktivite açıklaması"
|
||||||
/>
|
/>
|
||||||
|
|
@ -605,11 +519,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
min="0"
|
min="0"
|
||||||
value={activity.plannedDuration}
|
value={activity.plannedDuration}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateActivity(
|
updateActivity(index, 'plannedDuration', parseInt(e.target.value) || 0)
|
||||||
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"
|
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 */}
|
{/* Notes */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Notlar</label>
|
||||||
Notlar
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={formData.notes}
|
value={formData.notes}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
|
||||||
setFormData({ ...formData, notes: e.target.value })
|
|
||||||
}
|
|
||||||
rows={2}
|
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"
|
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..."
|
placeholder="Ek notlar..."
|
||||||
|
|
@ -663,7 +569,7 @@ const NewWorkOrderModal: React.FC<NewWorkOrderModalProps> = ({
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default NewWorkOrderModal;
|
export default NewWorkOrderModal
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,12 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from 'react'
|
||||||
import {
|
import { FaTimes, FaSave, FaExclamationTriangle, FaInfoCircle } from 'react-icons/fa'
|
||||||
FaTimes,
|
import { PmMaintenancePlan } from '../../../types/pm'
|
||||||
FaSave,
|
|
||||||
FaExclamationTriangle,
|
|
||||||
FaInfoCircle,
|
|
||||||
} from "react-icons/fa";
|
|
||||||
import { PmMaintenancePlan } from "../../../types/pm";
|
|
||||||
|
|
||||||
interface PlanStatusChangeModalProps {
|
interface PlanStatusChangeModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (planIds: string[], isActive: boolean, reason: string) => void;
|
onSave: (planIds: string[], isActive: boolean, reason: string) => void
|
||||||
selectedPlans: PmMaintenancePlan[];
|
selectedPlans: PmMaintenancePlan[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
|
|
@ -20,47 +15,43 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
onSave,
|
onSave,
|
||||||
selectedPlans,
|
selectedPlans,
|
||||||
}) => {
|
}) => {
|
||||||
const [targetStatus, setTargetStatus] = useState<boolean>(true);
|
const [targetStatus, setTargetStatus] = useState<boolean>(true)
|
||||||
const [reason, setReason] = useState("");
|
const [reason, setReason] = useState('')
|
||||||
|
|
||||||
const activePlans = selectedPlans.filter((plan) => plan.isActive);
|
const activePlans = selectedPlans.filter((plan) => plan.isActive)
|
||||||
const inactivePlans = selectedPlans.filter((plan) => !plan.isActive);
|
const inactivePlans = selectedPlans.filter((plan) => !plan.isActive)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
setTargetStatus(true);
|
setTargetStatus(true)
|
||||||
setReason("");
|
setReason('')
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen])
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (!reason.trim()) {
|
if (!reason.trim()) {
|
||||||
alert("Lütfen durum değişikliği için bir açıklama girin.");
|
alert('Lütfen durum değişikliği için bir açıklama girin.')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
const planIds = selectedPlans.map((plan) => plan.id);
|
const planIds = selectedPlans.map((plan) => plan.id)
|
||||||
onSave(planIds, targetStatus, reason.trim());
|
onSave(planIds, targetStatus, reason.trim())
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const getStatusText = (isActive: boolean) => {
|
const getStatusText = (isActive: boolean) => {
|
||||||
return isActive ? "Aktif" : "Pasif";
|
return isActive ? 'Aktif' : 'Pasif'
|
||||||
};
|
}
|
||||||
|
|
||||||
const getStatusColor = (isActive: boolean) => {
|
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) => {
|
const getTargetStatusColor = (isActive: boolean) => {
|
||||||
return isActive ? "text-green-600" : "text-red-600";
|
return isActive ? 'text-green-600' : 'text-red-600'
|
||||||
};
|
}
|
||||||
|
|
||||||
const willChange = selectedPlans.filter(
|
const willChange = selectedPlans.filter((plan) => plan.isActive !== targetStatus)
|
||||||
(plan) => plan.isActive !== targetStatus
|
const willNotChange = selectedPlans.filter((plan) => plan.isActive === targetStatus)
|
||||||
);
|
|
||||||
const willNotChange = selectedPlans.filter(
|
|
||||||
(plan) => plan.isActive === targetStatus
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isOpen && (
|
isOpen && (
|
||||||
|
|
@ -69,12 +60,8 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-gray-900">
|
<h2 className="text-2xl font-bold text-gray-900">Plan Durumu Değiştir</h2>
|
||||||
Plan Durumu Değiştir
|
<p className="text-gray-600">{selectedPlans.length} plan için durum değişikliği</p>
|
||||||
</h2>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
{selectedPlans.length} plan için durum değişikliği
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
|
@ -88,40 +75,28 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
<div className="p-3 space-y-3">
|
<div className="p-3 space-y-3">
|
||||||
{/* Current Status Summary */}
|
{/* Current Status Summary */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
<h3 className="text-sm font-medium text-gray-900 mb-2">Mevcut Durum</h3>
|
||||||
Mevcut Durum
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div className="bg-green-50 border border-green-200 rounded-lg p-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="flex items-center space-x-1.5">
|
||||||
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
|
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
|
||||||
<span className="text-sm font-medium text-green-800">
|
<span className="text-sm font-medium text-green-800">Aktif Planlar</span>
|
||||||
Aktif Planlar
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-base font-bold text-green-600 mt-1">
|
<p className="text-base font-bold text-green-600 mt-1">{activePlans.length}</p>
|
||||||
{activePlans.length}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
|
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
|
||||||
<div className="flex items-center space-x-1.5">
|
<div className="flex items-center space-x-1.5">
|
||||||
<div className="w-3 h-3 bg-red-500 rounded-full"></div>
|
<div className="w-3 h-3 bg-red-500 rounded-full"></div>
|
||||||
<span className="text-sm font-medium text-red-800">
|
<span className="text-sm font-medium text-red-800">Pasif Planlar</span>
|
||||||
Pasif Planlar
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-base font-bold text-red-600 mt-1">
|
<p className="text-base font-bold text-red-600 mt-1">{inactivePlans.length}</p>
|
||||||
{inactivePlans.length}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Target Status Selection */}
|
{/* Target Status Selection */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
<h3 className="text-sm font-medium text-gray-900 mb-2">Hedef Durum</h3>
|
||||||
Hedef Durum
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2">
|
<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">
|
<label className="flex items-center space-x-3 p-2 border border-gray-200 rounded-lg hover:bg-gray-50">
|
||||||
<input
|
<input
|
||||||
|
|
@ -133,9 +108,7 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center space-x-1.5">
|
<div className="flex items-center space-x-1.5">
|
||||||
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
|
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
|
||||||
<span className="text-sm font-medium text-gray-900">
|
<span className="text-sm font-medium text-gray-900">Aktif</span>
|
||||||
Aktif
|
|
||||||
</span>
|
|
||||||
<span className="text-xs text-gray-500">
|
<span className="text-xs text-gray-500">
|
||||||
(Planlar bakım takvimine dahil edilir)
|
(Planlar bakım takvimine dahil edilir)
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -151,9 +124,7 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center space-x-1.5">
|
<div className="flex items-center space-x-1.5">
|
||||||
<div className="w-3 h-3 bg-red-500 rounded-full"></div>
|
<div className="w-3 h-3 bg-red-500 rounded-full"></div>
|
||||||
<span className="text-sm font-medium text-gray-900">
|
<span className="text-sm font-medium text-gray-900">Pasif</span>
|
||||||
Pasif
|
|
||||||
</span>
|
|
||||||
<span className="text-xs text-gray-500">
|
<span className="text-xs text-gray-500">
|
||||||
(Planlar bakım takvimine dahil edilmez)
|
(Planlar bakım takvimine dahil edilmez)
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -164,9 +135,7 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
|
|
||||||
{/* Change Impact */}
|
{/* Change Impact */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
<h3 className="text-sm font-medium text-gray-900 mb-2">Değişiklik Etkisi</h3>
|
||||||
Değişiklik Etkisi
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
{willChange.length > 0 && (
|
{willChange.length > 0 && (
|
||||||
<div className="mb-2">
|
<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="bg-yellow-50 border border-yellow-200 rounded-lg p-2 max-h-28 overflow-y-auto">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{willChange.map((plan) => (
|
{willChange.map((plan) => (
|
||||||
<div
|
<div key={plan.id} className="flex items-center justify-between text-sm">
|
||||||
key={plan.id}
|
|
||||||
className="flex items-center justify-between text-sm"
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<span className="font-mono text-gray-600">
|
<span className="font-mono text-gray-600">{plan.planCode}</span>
|
||||||
{plan.planCode}
|
|
||||||
</span>
|
|
||||||
<span className="text-gray-500">-</span>
|
<span className="text-gray-500">-</span>
|
||||||
<span className="text-gray-700">
|
<span className="text-gray-700">{plan.description}</span>
|
||||||
{plan.description}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-full text-xs ${getStatusColor(
|
className={`px-2 py-1 rounded-full text-xs ${getStatusColor(
|
||||||
plan.isActive
|
plan.isActive,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getStatusText(plan.isActive)}
|
{getStatusText(plan.isActive)}
|
||||||
|
|
@ -203,7 +165,7 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
<span className="text-gray-400">→</span>
|
<span className="text-gray-400">→</span>
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-full text-xs ${getStatusColor(
|
className={`px-2 py-1 rounded-full text-xs ${getStatusColor(
|
||||||
targetStatus
|
targetStatus,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getStatusText(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="bg-blue-50 border border-blue-200 rounded-lg p-2 max-h-28 overflow-y-auto">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{willNotChange.map((plan) => (
|
{willNotChange.map((plan) => (
|
||||||
<div
|
<div key={plan.id} className="flex items-center justify-between text-sm">
|
||||||
key={plan.id}
|
|
||||||
className="flex items-center justify-between text-sm"
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<span className="font-mono text-gray-600">
|
<span className="font-mono text-gray-600">{plan.planCode}</span>
|
||||||
{plan.planCode}
|
|
||||||
</span>
|
|
||||||
<span className="text-gray-500">-</span>
|
<span className="text-gray-500">-</span>
|
||||||
<span className="text-gray-700">
|
<span className="text-gray-700">{plan.description}</span>
|
||||||
{plan.description}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-full text-xs ${getStatusColor(
|
className={`px-2 py-1 rounded-full text-xs ${getStatusColor(
|
||||||
plan.isActive
|
plan.isActive,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getStatusText(plan.isActive)}
|
{getStatusText(plan.isActive)}
|
||||||
|
|
@ -281,10 +236,9 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm font-medium text-red-800">Uyarı</h4>
|
<h4 className="text-sm font-medium text-red-800">Uyarı</h4>
|
||||||
<p className="text-sm text-red-700 mt-1">
|
<p className="text-sm text-red-700 mt-1">
|
||||||
Planları pasif hale getirmek, gelecek bakım işlemlerinin
|
Planları pasif hale getirmek, gelecek bakım işlemlerinin otomatik olarak
|
||||||
otomatik olarak planlanmasını durdurur. Bu planlar manuel
|
planlanmasını durdurur. Bu planlar manuel olarak aktif hale getirilene kadar
|
||||||
olarak aktif hale getirilene kadar bakım takviminde
|
bakım takviminde görünmeyecektir.
|
||||||
görünmeyecektir.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -296,13 +250,10 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
<div className="flex items-start space-x-2">
|
<div className="flex items-start space-x-2">
|
||||||
<FaInfoCircle className="w-5 h-5 text-green-600 mt-0.5" />
|
<FaInfoCircle className="w-5 h-5 text-green-600 mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm font-medium text-green-800">
|
<h4 className="text-sm font-medium text-green-800">Bilgi</h4>
|
||||||
Bilgi
|
|
||||||
</h4>
|
|
||||||
<p className="text-sm text-green-700 mt-1">
|
<p className="text-sm text-green-700 mt-1">
|
||||||
Planları aktif hale getirmek, bakım takviminde
|
Planları aktif hale getirmek, bakım takviminde görünmelerini ve otomatik iş
|
||||||
görünmelerini ve otomatik iş emri oluşturulmalarını
|
emri oluşturulmalarını sağlar. Sonraki bakım tarihleri plan ayarlarına göre
|
||||||
sağlar. Sonraki bakım tarihleri plan ayarlarına göre
|
|
||||||
hesaplanacaktır.
|
hesaplanacaktır.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -326,11 +277,11 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
disabled={!reason.trim()}
|
disabled={!reason.trim()}
|
||||||
className={`flex items-center space-x-2 px-3 py-1.5 text-sm rounded-lg transition-colors ${
|
className={`flex items-center space-x-2 px-3 py-1.5 text-sm rounded-lg transition-colors ${
|
||||||
!reason.trim()
|
!reason.trim()
|
||||||
? "bg-gray-300 text-gray-500 cursor-not-allowed"
|
? 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
||||||
: `${getTargetStatusColor(targetStatus)} ${
|
: `${getTargetStatusColor(targetStatus)} ${
|
||||||
targetStatus
|
targetStatus
|
||||||
? "bg-green-600 hover:bg-green-700 text-white"
|
? 'bg-green-600 hover:bg-green-700 text-white'
|
||||||
: "bg-red-600 hover:bg-red-700 text-white"
|
: 'bg-red-600 hover:bg-red-700 text-white'
|
||||||
}`
|
}`
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|
@ -343,7 +294,7 @@ const PlanStatusChangeModal: React.FC<PlanStatusChangeModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default PlanStatusChangeModal;
|
export default PlanStatusChangeModal
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,21 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaPlay, FaCalendar, FaUser } from "react-icons/fa";
|
import { FaTimes, FaPlay, FaCalendar, FaUser } from 'react-icons/fa'
|
||||||
import { PmMaintenanceWorkOrder, WorkOrderStatusEnum } from "../../../types/pm";
|
import { PmMaintenanceWorkOrder, WorkOrderStatusEnum } from '../../../types/pm'
|
||||||
import { mockEmployees } from "../../../mocks/mockEmployees";
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { mockMaintenanceTeams } from "../../../mocks/mockMaintenanceTeams";
|
import { mockMaintenanceTeams } from '../../../mocks/mockMaintenanceTeams'
|
||||||
|
|
||||||
interface StartWorkOrderModalProps {
|
interface StartWorkOrderModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onStart: (
|
onStart: (workOrders: PmMaintenanceWorkOrder[], startData: StartWorkOrderData) => void
|
||||||
workOrders: PmMaintenanceWorkOrder[],
|
workOrders: PmMaintenanceWorkOrder[]
|
||||||
startData: StartWorkOrderData
|
|
||||||
) => void;
|
|
||||||
workOrders: PmMaintenanceWorkOrder[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StartWorkOrderData {
|
interface StartWorkOrderData {
|
||||||
actualStart: Date;
|
actualStart: Date
|
||||||
assignedTo?: string;
|
assignedTo?: string
|
||||||
maintenanceTeamId?: string;
|
maintenanceTeamId?: string
|
||||||
notes?: string;
|
notes?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
|
|
@ -29,57 +26,57 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
actualStart: new Date().toISOString().slice(0, 16),
|
actualStart: new Date().toISOString().slice(0, 16),
|
||||||
assignedTo: "",
|
assignedTo: '',
|
||||||
maintenanceTeamId: "",
|
maintenanceTeamId: '',
|
||||||
notes: "",
|
notes: '',
|
||||||
});
|
})
|
||||||
|
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (!formData.actualStart) {
|
if (!formData.actualStart) {
|
||||||
newErrors.actualStart = "Başlangıç tarihi zorunludur";
|
newErrors.actualStart = 'Başlangıç tarihi zorunludur'
|
||||||
}
|
}
|
||||||
|
|
||||||
const startDate = new Date(formData.actualStart);
|
const startDate = new Date(formData.actualStart)
|
||||||
const now = new Date();
|
const now = new Date()
|
||||||
if (startDate > now) {
|
if (startDate > now) {
|
||||||
newErrors.actualStart = "Başlangıç tarihi gelecekte olamaz";
|
newErrors.actualStart = 'Başlangıç tarihi gelecekte olamaz'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
if (!validateForm()) return;
|
if (!validateForm()) return
|
||||||
|
|
||||||
const startData: StartWorkOrderData = {
|
const startData: StartWorkOrderData = {
|
||||||
actualStart: new Date(formData.actualStart),
|
actualStart: new Date(formData.actualStart),
|
||||||
assignedTo: formData.assignedTo || undefined,
|
assignedTo: formData.assignedTo || undefined,
|
||||||
maintenanceTeamId: formData.maintenanceTeamId || undefined,
|
maintenanceTeamId: formData.maintenanceTeamId || undefined,
|
||||||
notes: formData.notes || undefined,
|
notes: formData.notes || undefined,
|
||||||
};
|
}
|
||||||
|
|
||||||
onStart(workOrders, startData);
|
onStart(workOrders, startData)
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const canStartWorkOrders = workOrders.every(
|
const canStartWorkOrders = workOrders.every(
|
||||||
(wo) =>
|
(wo) =>
|
||||||
wo.status === WorkOrderStatusEnum.Created ||
|
wo.status === WorkOrderStatusEnum.Created ||
|
||||||
wo.status === WorkOrderStatusEnum.Planned ||
|
wo.status === WorkOrderStatusEnum.Planned ||
|
||||||
wo.status === WorkOrderStatusEnum.Released
|
wo.status === WorkOrderStatusEnum.Released,
|
||||||
);
|
)
|
||||||
|
|
||||||
const inProgressWorkOrders = workOrders.filter(
|
const inProgressWorkOrders = workOrders.filter(
|
||||||
(wo) => wo.status === WorkOrderStatusEnum.InProgress
|
(wo) => wo.status === WorkOrderStatusEnum.InProgress,
|
||||||
);
|
)
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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" />
|
<FaPlay className="w-5 h-5 mr-2 text-green-600" />
|
||||||
İş Emirlerini Başlat
|
İş Emirlerini Başlat
|
||||||
</h3>
|
</h3>
|
||||||
<button
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5" />
|
<FaTimes className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -104,47 +98,33 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
</h4>
|
</h4>
|
||||||
<div className="max-h-32 overflow-y-auto border border-gray-200 rounded-lg">
|
<div className="max-h-32 overflow-y-auto border border-gray-200 rounded-lg">
|
||||||
{workOrders.map((workOrder) => (
|
{workOrders.map((workOrder) => (
|
||||||
<div
|
<div key={workOrder.id} className="p-3 border-b border-gray-100 last:border-b-0">
|
||||||
key={workOrder.id}
|
|
||||||
className="p-3 border-b border-gray-100 last:border-b-0"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-sm text-gray-900">
|
<p className="font-medium text-sm text-gray-900">{workOrder.workOrderNumber}</p>
|
||||||
{workOrder.workOrderNumber}
|
<p className="text-xs text-gray-600 truncate">{workOrder.description}</p>
|
||||||
</p>
|
|
||||||
<p className="text-xs text-gray-600 truncate">
|
|
||||||
{workOrder.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
workOrder.status === WorkOrderStatusEnum.Created
|
workOrder.status === WorkOrderStatusEnum.Created
|
||||||
? "bg-gray-100 text-gray-800"
|
? 'bg-gray-100 text-gray-800'
|
||||||
: workOrder.status === WorkOrderStatusEnum.Planned
|
: workOrder.status === WorkOrderStatusEnum.Planned
|
||||||
? "bg-blue-100 text-blue-800"
|
? 'bg-blue-100 text-blue-800'
|
||||||
: workOrder.status === WorkOrderStatusEnum.Released
|
: workOrder.status === WorkOrderStatusEnum.Released
|
||||||
? "bg-purple-100 text-purple-800"
|
? 'bg-purple-100 text-purple-800'
|
||||||
: workOrder.status === WorkOrderStatusEnum.InProgress
|
: workOrder.status === WorkOrderStatusEnum.InProgress
|
||||||
? "bg-orange-100 text-orange-800"
|
? 'bg-orange-100 text-orange-800'
|
||||||
: "bg-gray-100 text-gray-800"
|
: 'bg-gray-100 text-gray-800'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{workOrder.status === WorkOrderStatusEnum.Created &&
|
{workOrder.status === WorkOrderStatusEnum.Created && 'Oluşturuldu'}
|
||||||
"Oluşturuldu"}
|
{workOrder.status === WorkOrderStatusEnum.Planned && 'Planlandı'}
|
||||||
{workOrder.status === WorkOrderStatusEnum.Planned &&
|
{workOrder.status === WorkOrderStatusEnum.Released && 'Serbest Bırakıldı'}
|
||||||
"Planlandı"}
|
{workOrder.status === WorkOrderStatusEnum.InProgress && 'Devam Ediyor'}
|
||||||
{workOrder.status === WorkOrderStatusEnum.Released &&
|
{workOrder.status === WorkOrderStatusEnum.OnHold && 'Beklemede'}
|
||||||
"Serbest Bırakıldı"}
|
{workOrder.status === WorkOrderStatusEnum.Completed && 'Tamamlandı'}
|
||||||
{workOrder.status === WorkOrderStatusEnum.InProgress &&
|
{workOrder.status === WorkOrderStatusEnum.Cancelled && 'İptal Edildi'}
|
||||||
"Devam Ediyor"}
|
|
||||||
{workOrder.status === WorkOrderStatusEnum.OnHold &&
|
|
||||||
"Beklemede"}
|
|
||||||
{workOrder.status === WorkOrderStatusEnum.Completed &&
|
|
||||||
"Tamamlandı"}
|
|
||||||
{workOrder.status === WorkOrderStatusEnum.Cancelled &&
|
|
||||||
"İptal Edildi"}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -161,9 +141,8 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<h5 className="font-medium text-yellow-800 mb-1">Uyarı</h5>
|
<h5 className="font-medium text-yellow-800 mb-1">Uyarı</h5>
|
||||||
<p className="text-yellow-700 text-sm">
|
<p className="text-yellow-700 text-sm">
|
||||||
Bazı iş emirleri başlatılamaz durumda. Sadece "Oluşturuldu",
|
Bazı iş emirleri başlatılamaz durumda. Sadece "Oluşturuldu", "Planlandı" veya
|
||||||
"Planlandı" veya "Serbest Bırakıldı" durumundaki iş emirleri
|
"Serbest Bırakıldı" durumundaki iş emirleri başlatılabilir.
|
||||||
başlatılabilir.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -177,9 +156,8 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<h5 className="font-medium text-blue-800 mb-1">Bilgi</h5>
|
<h5 className="font-medium text-blue-800 mb-1">Bilgi</h5>
|
||||||
<p className="text-blue-700 text-sm">
|
<p className="text-blue-700 text-sm">
|
||||||
{inProgressWorkOrders.length} iş emri zaten devam ediyor
|
{inProgressWorkOrders.length} iş emri zaten devam ediyor durumunda. Bu iş emirleri
|
||||||
durumunda. Bu iş emirleri için sadece atama ve not
|
için sadece atama ve not güncellemesi yapılacak.
|
||||||
güncellemesi yapılacak.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -196,11 +174,9 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={formData.actualStart}
|
value={formData.actualStart}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, actualStart: e.target.value })}
|
||||||
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 ${
|
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 && (
|
{errors.actualStart && (
|
||||||
|
|
@ -217,9 +193,7 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={formData.assignedTo}
|
value={formData.assignedTo}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, assignedTo: e.target.value })}
|
||||||
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"
|
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>
|
<option value="">Kişi Seçin</option>
|
||||||
|
|
@ -232,9 +206,7 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Bakım Ekibi</label>
|
||||||
Bakım Ekibi
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.maintenanceTeamId}
|
value={formData.maintenanceTeamId}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
|
@ -262,9 +234,7 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={formData.notes}
|
value={formData.notes}
|
||||||
onChange={(e) =>
|
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
|
||||||
setFormData({ ...formData, notes: e.target.value })
|
|
||||||
}
|
|
||||||
rows={2}
|
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"
|
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..."
|
placeholder="İş emirlerinin başlatılması ile ilgili notlar..."
|
||||||
|
|
@ -287,7 +257,7 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
(wo) =>
|
(wo) =>
|
||||||
wo.status === WorkOrderStatusEnum.Created ||
|
wo.status === WorkOrderStatusEnum.Created ||
|
||||||
wo.status === WorkOrderStatusEnum.Planned ||
|
wo.status === WorkOrderStatusEnum.Planned ||
|
||||||
wo.status === WorkOrderStatusEnum.Released
|
wo.status === WorkOrderStatusEnum.Released,
|
||||||
).length
|
).length
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -301,10 +271,10 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<span className="text-gray-600">Başlangıç Tarihi:</span>
|
<span className="text-gray-600">Başlangıç Tarihi:</span>
|
||||||
<span className="ml-2 font-medium">
|
<span className="ml-2 font-medium">
|
||||||
{new Date(formData.actualStart).toLocaleDateString("tr-TR")}{" "}
|
{new Date(formData.actualStart).toLocaleDateString('tr-TR')}{' '}
|
||||||
{new Date(formData.actualStart).toLocaleTimeString("tr-TR", {
|
{new Date(formData.actualStart).toLocaleTimeString('tr-TR', {
|
||||||
hour: "2-digit",
|
hour: '2-digit',
|
||||||
minute: "2-digit",
|
minute: '2-digit',
|
||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -331,7 +301,7 @@ const StartWorkOrderModal: React.FC<StartWorkOrderModalProps> = ({
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default StartWorkOrderModal;
|
export default StartWorkOrderModal
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,13 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react'
|
||||||
import { FaTimes, FaSave, FaExclamationTriangle } from "react-icons/fa";
|
import { FaTimes, FaSave, FaExclamationTriangle } from 'react-icons/fa'
|
||||||
import {
|
import { PmWorkCenter, WorkCenterStatusEnum, CriticalityLevelEnum } from '../../../types/pm'
|
||||||
PmWorkCenter,
|
import { getCriticalityLevelText, getWorkCenterStatusText } from '../../../utils/erp'
|
||||||
WorkCenterStatusEnum,
|
|
||||||
CriticalityLevelEnum,
|
|
||||||
} from "../../../types/pm";
|
|
||||||
import {
|
|
||||||
getCriticalityLevelText,
|
|
||||||
getWorkCenterStatusText,
|
|
||||||
} from "../../../utils/erp";
|
|
||||||
|
|
||||||
interface StatusUpdateModalProps {
|
interface StatusUpdateModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (updatedWorkCenters: PmWorkCenter[]) => void;
|
onSave: (updatedWorkCenters: PmWorkCenter[]) => void
|
||||||
selectedWorkCenters: PmWorkCenter[];
|
selectedWorkCenters: PmWorkCenter[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
||||||
|
|
@ -23,17 +16,15 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
||||||
onSave,
|
onSave,
|
||||||
selectedWorkCenters,
|
selectedWorkCenters,
|
||||||
}) => {
|
}) => {
|
||||||
const [newStatus, setNewStatus] = useState<WorkCenterStatusEnum>(
|
const [newStatus, setNewStatus] = useState<WorkCenterStatusEnum>(WorkCenterStatusEnum.Operational)
|
||||||
WorkCenterStatusEnum.Operational
|
|
||||||
);
|
|
||||||
const [newCriticality, setNewCriticality] = useState<CriticalityLevelEnum>(
|
const [newCriticality, setNewCriticality] = useState<CriticalityLevelEnum>(
|
||||||
CriticalityLevelEnum.Medium
|
CriticalityLevelEnum.Medium,
|
||||||
);
|
)
|
||||||
const [updateStatus, setUpdateStatus] = useState(true);
|
const [updateStatus, setUpdateStatus] = useState(true)
|
||||||
const [updateCriticality, setUpdateCriticality] = useState(false);
|
const [updateCriticality, setUpdateCriticality] = useState(false)
|
||||||
const [reason, setReason] = useState("");
|
const [reason, setReason] = useState('')
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
const updatedWorkCenters = selectedWorkCenters.map((workCenter) => ({
|
const updatedWorkCenters = selectedWorkCenters.map((workCenter) => ({
|
||||||
|
|
@ -41,15 +32,15 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
||||||
status: updateStatus ? newStatus : workCenter.status,
|
status: updateStatus ? newStatus : workCenter.status,
|
||||||
criticality: updateCriticality ? newCriticality : workCenter.criticality,
|
criticality: updateCriticality ? newCriticality : workCenter.criticality,
|
||||||
lastModificationTime: new Date(),
|
lastModificationTime: new Date(),
|
||||||
}));
|
}))
|
||||||
|
|
||||||
onSave(updatedWorkCenters);
|
onSave(updatedWorkCenters)
|
||||||
onClose();
|
onClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const isStatusCritical =
|
const isStatusCritical =
|
||||||
newStatus === WorkCenterStatusEnum.OutOfOrder ||
|
newStatus === WorkCenterStatusEnum.OutOfOrder ||
|
||||||
newStatus === WorkCenterStatusEnum.UnderMaintenance;
|
newStatus === WorkCenterStatusEnum.UnderMaintenance
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<h2 className="text-lg font-semibold text-gray-900">
|
||||||
Durum Güncelle ({selectedWorkCenters.length} iş merkezi)
|
Durum Güncelle ({selectedWorkCenters.length} iş merkezi)
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||||
onClick={onClose}
|
|
||||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -71,15 +59,10 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-4 space-y-4">
|
||||||
{/* Selected WorkCenter List */}
|
{/* Selected WorkCenter List */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Seçili İş Merkezleri</h3>
|
||||||
Seçili İş Merkezleri
|
|
||||||
</h3>
|
|
||||||
<div className="max-h-28 overflow-y-auto bg-gray-50 rounded-lg p-2">
|
<div className="max-h-28 overflow-y-auto bg-gray-50 rounded-lg p-2">
|
||||||
{selectedWorkCenters.map((workCenter) => (
|
{selectedWorkCenters.map((workCenter) => (
|
||||||
<div
|
<div key={workCenter.id} className="flex items-center justify-between py-0.5">
|
||||||
key={workCenter.id}
|
|
||||||
className="flex items-center justify-between py-0.5"
|
|
||||||
>
|
|
||||||
<span className="text-sm text-gray-700">
|
<span className="text-sm text-gray-700">
|
||||||
{workCenter.code} - {workCenter.name}
|
{workCenter.code} - {workCenter.name}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -106,35 +89,22 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
||||||
onChange={(e) => setUpdateStatus(e.target.checked)}
|
onChange={(e) => setUpdateStatus(e.target.checked)}
|
||||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
<label
|
<label htmlFor="updateStatus" className="text-sm font-medium text-gray-700">
|
||||||
htmlFor="updateStatus"
|
|
||||||
className="text-sm font-medium text-gray-700"
|
|
||||||
>
|
|
||||||
Operasyonel durumu güncelle
|
Operasyonel durumu güncelle
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{updateStatus && (
|
{updateStatus && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">Yeni Durum</label>
|
||||||
Yeni Durum
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
value={newStatus}
|
value={newStatus}
|
||||||
onChange={(e) =>
|
onChange={(e) => setNewStatus(e.target.value as WorkCenterStatusEnum)}
|
||||||
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"
|
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}>
|
<option value={WorkCenterStatusEnum.Operational}>Operasyonel</option>
|
||||||
Operasyonel
|
<option value={WorkCenterStatusEnum.UnderMaintenance}>Bakımda</option>
|
||||||
</option>
|
<option value={WorkCenterStatusEnum.OutOfOrder}>Arızalı</option>
|
||||||
<option value={WorkCenterStatusEnum.UnderMaintenance}>
|
|
||||||
Bakımda
|
|
||||||
</option>
|
|
||||||
<option value={WorkCenterStatusEnum.OutOfOrder}>
|
|
||||||
Arızalı
|
|
||||||
</option>
|
|
||||||
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
<option value={WorkCenterStatusEnum.Retired}>Emekli</option>
|
||||||
</select>
|
</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="mt-2 p-2 bg-yellow-50 border border-yellow-200 rounded-lg">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<FaExclamationTriangle className="w-4 h-4 text-yellow-600" />
|
<FaExclamationTriangle className="w-4 h-4 text-yellow-600" />
|
||||||
<span className="text-sm text-yellow-800 font-medium">
|
<span className="text-sm text-yellow-800 font-medium">Dikkat!</span>
|
||||||
Dikkat!
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-yellow-700 mt-1">
|
<p className="text-sm text-yellow-700 mt-1">
|
||||||
Bu durum değişikliği iş merkezinin üretim kapasitesini
|
Bu durum değişikliği iş merkezinin üretim kapasitesini etkileyebilir.
|
||||||
etkileyebilir.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -163,10 +130,7 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
||||||
onChange={(e) => setUpdateCriticality(e.target.checked)}
|
onChange={(e) => setUpdateCriticality(e.target.checked)}
|
||||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
<label
|
<label htmlFor="updateCriticality" className="text-sm font-medium text-gray-700">
|
||||||
htmlFor="updateCriticality"
|
|
||||||
className="text-sm font-medium text-gray-700"
|
|
||||||
>
|
|
||||||
Kritiklik seviyesini güncelle
|
Kritiklik seviyesini güncelle
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -178,9 +142,7 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={newCriticality}
|
value={newCriticality}
|
||||||
onChange={(e) =>
|
onChange={(e) => setNewCriticality(e.target.value as CriticalityLevelEnum)}
|
||||||
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"
|
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>
|
<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">
|
<div className="text-sm text-blue-800 space-y-1">
|
||||||
<p>• {selectedWorkCenters.length} iş merkezi güncellenecek</p>
|
<p>• {selectedWorkCenters.length} iş merkezi güncellenecek</p>
|
||||||
{updateStatus && (
|
{updateStatus && (
|
||||||
<p>
|
<p>• Durum "{getWorkCenterStatusText(newStatus)}" olarak değiştirilecek</p>
|
||||||
• Durum "{getWorkCenterStatusText(newStatus)}" olarak
|
|
||||||
değiştirilecek
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
{updateCriticality && (
|
{updateCriticality && (
|
||||||
<p>
|
<p>
|
||||||
• Kritiklik seviyesi "
|
• Kritiklik seviyesi "{getCriticalityLevelText(newCriticality)}" olarak
|
||||||
{getCriticalityLevelText(newCriticality)}" olarak
|
|
||||||
değiştirilecek
|
değiştirilecek
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
@ -249,7 +207,7 @@ const StatusUpdateModal: React.FC<StatusUpdateModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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 {
|
import {
|
||||||
FaTimes,
|
FaTimes,
|
||||||
FaSave,
|
FaSave,
|
||||||
FaExclamationTriangle,
|
FaExclamationTriangle,
|
||||||
FaCheckCircle,
|
FaCheckCircle,
|
||||||
FaTimesCircle,
|
FaTimesCircle,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import { Team } from "../../../types/common";
|
import { Team } from '../../../types/common'
|
||||||
|
|
||||||
interface TeamStatusChangeModalProps {
|
interface TeamStatusChangeModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onSave: (teamIds: string[], newStatus: boolean, reason?: string) => void;
|
onSave: (teamIds: string[], newStatus: boolean, reason?: string) => void
|
||||||
selectedTeams: Team[];
|
selectedTeams: Team[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
||||||
|
|
@ -21,60 +21,60 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
||||||
onSave,
|
onSave,
|
||||||
selectedTeams,
|
selectedTeams,
|
||||||
}) => {
|
}) => {
|
||||||
const [newStatus, setNewStatus] = useState<boolean>(true);
|
const [newStatus, setNewStatus] = useState<boolean>(true)
|
||||||
const [reason, setReason] = useState("");
|
const [reason, setReason] = useState('')
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
|
|
||||||
const activeTeams = selectedTeams.filter((team) => team.isActive);
|
const activeTeams = selectedTeams.filter((team) => team.isActive)
|
||||||
const inactiveTeams = selectedTeams.filter((team) => !team.isActive);
|
const inactiveTeams = selectedTeams.filter((team) => !team.isActive)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
// Reset form when modal opens
|
// Reset form when modal opens
|
||||||
setNewStatus(true);
|
setNewStatus(true)
|
||||||
setReason("");
|
setReason('')
|
||||||
setErrors({});
|
setErrors({})
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen])
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {}
|
||||||
|
|
||||||
if (!newStatus && !reason.trim()) {
|
if (!newStatus && !reason.trim()) {
|
||||||
newErrors.reason = "Ekipleri pasif yaparken sebep belirtmeniz gerekli";
|
newErrors.reason = 'Ekipleri pasif yaparken sebep belirtmeniz gerekli'
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
const teamIds = selectedTeams.map((team) => team.id);
|
const teamIds = selectedTeams.map((team) => team.id)
|
||||||
onSave(teamIds, newStatus, reason.trim() || undefined);
|
onSave(teamIds, newStatus, reason.trim() || undefined)
|
||||||
onClose();
|
onClose()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const getImpactAnalysis = () => {
|
const getImpactAnalysis = () => {
|
||||||
const teamsToActivate = newStatus ? inactiveTeams : [];
|
const teamsToActivate = newStatus ? inactiveTeams : []
|
||||||
const teamsToDeactivate = newStatus ? [] : activeTeams;
|
const teamsToDeactivate = newStatus ? [] : activeTeams
|
||||||
|
|
||||||
return {
|
return {
|
||||||
teamsToActivate,
|
teamsToActivate,
|
||||||
teamsToDeactivate,
|
teamsToDeactivate,
|
||||||
totalMembers: selectedTeams.reduce(
|
totalMembers: selectedTeams.reduce(
|
||||||
(total, team) => total + team.members.filter((m) => m.isActive).length,
|
(total, team) => total + team.members.filter((m) => m.isActive).length,
|
||||||
0
|
0,
|
||||||
),
|
),
|
||||||
activeMembers: activeTeams.reduce(
|
activeMembers: activeTeams.reduce(
|
||||||
(total, team) => total + team.members.filter((m) => m.isActive).length,
|
(total, team) => total + team.members.filter((m) => m.isActive).length,
|
||||||
0
|
0,
|
||||||
),
|
),
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const impact = getImpactAnalysis();
|
const impact = getImpactAnalysis()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isOpen && (
|
isOpen && (
|
||||||
|
|
@ -83,12 +83,8 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-gray-900">
|
<h2 className="text-2xl font-bold text-gray-900">Ekip Durumu Değiştir</h2>
|
||||||
Ekip Durumu Değiştir
|
<p className="text-gray-600">{selectedTeams.length} ekibin durumunu değiştirin</p>
|
||||||
</h2>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
{selectedTeams.length} ekibin durumunu değiştirin
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
|
@ -102,9 +98,7 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
||||||
<div className="p-3 space-y-3">
|
<div className="p-3 space-y-3">
|
||||||
{/* Status Selection */}
|
{/* Status Selection */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
<h3 className="text-sm font-medium text-gray-900 mb-2">Yeni Durum</h3>
|
||||||
Yeni Durum
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<label className="flex items-center p-2 border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer">
|
<label className="flex items-center p-2 border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer">
|
||||||
<input
|
<input
|
||||||
|
|
@ -148,41 +142,31 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
||||||
<textarea
|
<textarea
|
||||||
value={reason}
|
value={reason}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setReason(e.target.value);
|
setReason(e.target.value)
|
||||||
if (errors.reason) {
|
if (errors.reason) {
|
||||||
setErrors((prev) => ({ ...prev, reason: "" }));
|
setErrors((prev) => ({ ...prev, reason: '' }))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
rows={2}
|
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 ${
|
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..."
|
placeholder="Ekiplerin neden pasif yapıldığını açıklayın..."
|
||||||
/>
|
/>
|
||||||
{errors.reason && (
|
{errors.reason && <p className="text-red-500 text-xs mt-1">{errors.reason}</p>}
|
||||||
<p className="text-red-500 text-xs mt-1">{errors.reason}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Impact Analysis */}
|
{/* Impact Analysis */}
|
||||||
<div className="bg-gray-50 rounded-lg p-3">
|
<div className="bg-gray-50 rounded-lg p-3">
|
||||||
<h3 className="text-sm font-medium text-gray-900 mb-3">
|
<h3 className="text-sm font-medium text-gray-900 mb-3">Etki Analizi</h3>
|
||||||
Etki Analizi
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-2 gap-3 mb-3">
|
<div className="grid grid-cols-2 gap-3 mb-3">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="text-base font-bold text-blue-600">
|
<div className="text-base font-bold text-blue-600">{selectedTeams.length}</div>
|
||||||
{selectedTeams.length}
|
<div className="text-sm text-gray-600">Toplam Seçili Ekip</div>
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-600">
|
|
||||||
Toplam Seçili Ekip
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="text-base font-bold text-green-600">
|
<div className="text-base font-bold text-green-600">{impact.totalMembers}</div>
|
||||||
{impact.totalMembers}
|
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-600">Toplam Üye Sayısı</div>
|
<div className="text-sm text-gray-600">Toplam Üye Sayısı</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -192,12 +176,10 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm font-medium text-yellow-800">
|
<h4 className="text-sm font-medium text-yellow-800">Uyarı</h4>
|
||||||
Uyarı
|
|
||||||
</h4>
|
|
||||||
<p className="text-sm text-yellow-700 mt-1">
|
<p className="text-sm text-yellow-700 mt-1">
|
||||||
{impact.teamsToDeactivate.length} aktif ekip pasif
|
{impact.teamsToDeactivate.length} aktif ekip pasif yapılacak. Bu ekipler
|
||||||
yapılacak. Bu ekipler artık yeni iş emirleri alamayacak.
|
artık yeni iş emirleri alamayacak.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -209,12 +191,10 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
<FaCheckCircle className="w-5 h-5 text-green-600 mt-0.5" />
|
<FaCheckCircle className="w-5 h-5 text-green-600 mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm font-medium text-green-800">
|
<h4 className="text-sm font-medium text-green-800">Bilgi</h4>
|
||||||
Bilgi
|
|
||||||
</h4>
|
|
||||||
<p className="text-sm text-green-700 mt-1">
|
<p className="text-sm text-green-700 mt-1">
|
||||||
{impact.teamsToActivate.length} pasif ekip aktif hale
|
{impact.teamsToActivate.length} pasif ekip aktif hale gelecek. Bu ekipler
|
||||||
gelecek. Bu ekipler tekrar iş emirleri alabilecek.
|
tekrar iş emirleri alabilecek.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -224,9 +204,7 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
||||||
|
|
||||||
{/* Selected Teams List */}
|
{/* Selected Teams List */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-medium text-gray-900 mb-2">
|
<h3 className="text-sm font-medium text-gray-900 mb-2">Etkilenecek Ekipler</h3>
|
||||||
Etkilenecek Ekipler
|
|
||||||
</h3>
|
|
||||||
<div className="max-h-32 overflow-y-auto border border-gray-200 rounded-lg">
|
<div className="max-h-32 overflow-y-auto border border-gray-200 rounded-lg">
|
||||||
{selectedTeams.map((team) => (
|
{selectedTeams.map((team) => (
|
||||||
<div
|
<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"
|
className="flex items-center justify-between p-2 border-b border-gray-100 last:border-b-0"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-medium text-sm text-gray-900">
|
<h4 className="font-medium text-sm text-gray-900">{team.name}</h4>
|
||||||
{team.name}
|
|
||||||
</h4>
|
|
||||||
<p className="text-sm text-gray-600">{team.code}</p>
|
<p className="text-sm text-gray-600">{team.code}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 text-xs font-semibold rounded-full ${
|
className={`px-2 py-1 text-xs font-semibold rounded-full ${
|
||||||
team.isActive
|
team.isActive
|
||||||
? "bg-green-100 text-green-800"
|
? 'bg-green-100 text-green-800'
|
||||||
: "bg-gray-100 text-gray-800"
|
: 'bg-gray-100 text-gray-800'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{team.isActive ? "Aktif" : "Pasif"}
|
{team.isActive ? 'Aktif' : 'Pasif'}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-sm text-gray-500">→</span>
|
<span className="text-sm text-gray-500">→</span>
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 text-xs font-semibold rounded-full ${
|
className={`px-2 py-1 text-xs font-semibold rounded-full ${
|
||||||
newStatus
|
newStatus ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'
|
||||||
? "bg-green-100 text-green-800"
|
|
||||||
: "bg-gray-100 text-gray-800"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{newStatus ? "Aktif" : "Pasif"}
|
{newStatus ? 'Aktif' : 'Pasif'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -277,9 +251,7 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
||||||
<button
|
<button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
className={`px-3 py-1.5 text-sm text-white rounded-lg transition-colors flex items-center space-x-2 ${
|
className={`px-3 py-1.5 text-sm text-white rounded-lg transition-colors flex items-center space-x-2 ${
|
||||||
newStatus
|
newStatus ? 'bg-green-600 hover:bg-green-700' : 'bg-red-600 hover:bg-red-700'
|
||||||
? "bg-green-600 hover:bg-green-700"
|
|
||||||
: "bg-red-600 hover:bg-red-700"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<FaSave className="w-4 h-4" />
|
<FaSave className="w-4 h-4" />
|
||||||
|
|
@ -289,7 +261,7 @@ const TeamStatusChangeModal: React.FC<TeamStatusChangeModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default TeamStatusChangeModal;
|
export default TeamStatusChangeModal
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import {
|
import {
|
||||||
FaTimes,
|
FaTimes,
|
||||||
FaEdit,
|
FaEdit,
|
||||||
|
|
@ -10,8 +10,8 @@ import {
|
||||||
FaFileAlt,
|
FaFileAlt,
|
||||||
FaTools,
|
FaTools,
|
||||||
FaCalendarAlt,
|
FaCalendarAlt,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import { PmFaultNotification } from "../../../types/pm";
|
import { PmFaultNotification } from '../../../types/pm'
|
||||||
import {
|
import {
|
||||||
getFaultTypeColor,
|
getFaultTypeColor,
|
||||||
getFaultTypeText,
|
getFaultTypeText,
|
||||||
|
|
@ -21,13 +21,13 @@ import {
|
||||||
getCriticalityLevelText,
|
getCriticalityLevelText,
|
||||||
getNotificationStatusColor,
|
getNotificationStatusColor,
|
||||||
getNotificationStatusText,
|
getNotificationStatusText,
|
||||||
} from "../../../utils/erp";
|
} from '../../../utils/erp'
|
||||||
|
|
||||||
interface ViewFaultNotificationModalProps {
|
interface ViewFaultNotificationModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onEdit: (notification: PmFaultNotification) => void;
|
onEdit: (notification: PmFaultNotification) => void
|
||||||
notification: PmFaultNotification | null;
|
notification: PmFaultNotification | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
||||||
|
|
@ -36,7 +36,7 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
||||||
onEdit,
|
onEdit,
|
||||||
notification,
|
notification,
|
||||||
}) => {
|
}) => {
|
||||||
if (!isOpen || !notification) return null;
|
if (!isOpen || !notification) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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 */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold text-gray-900">
|
<h2 className="text-lg font-semibold text-gray-900">{notification.notificationCode}</h2>
|
||||||
{notification.notificationCode}
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm text-gray-500 mt-1">{notification.title}</p>
|
<p className="text-sm text-gray-500 mt-1">{notification.title}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-3">
|
<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">
|
<div className="flex flex-wrap gap-1.5">
|
||||||
<span
|
<span
|
||||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getNotificationStatusColor(
|
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getNotificationStatusColor(
|
||||||
notification.status
|
notification.status,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getNotificationStatusText(notification.status)}
|
{getNotificationStatusText(notification.status)}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getPriorityColor(
|
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getPriorityColor(
|
||||||
notification.priority
|
notification.priority,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getPriorityText(notification.priority)}
|
{getPriorityText(notification.priority)}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getFaultTypeColor(
|
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getFaultTypeColor(
|
||||||
notification.faultType
|
notification.faultType,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getFaultTypeText(notification.faultType)}
|
{getFaultTypeText(notification.faultType)}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getCriticalityLevelColor(
|
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getCriticalityLevelColor(
|
||||||
notification.severity
|
notification.severity,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
Kritiklik: {getCriticalityLevelText(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="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Arıza Bilgileri</h3>
|
||||||
Arıza Bilgileri
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Açıklama</label>
|
||||||
Açıklama
|
<p className="text-sm text-gray-800 mt-1">{notification.description}</p>
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-800 mt-1">
|
|
||||||
{notification.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">İş Merkezi Kodu</label>
|
||||||
İş Merkezi Kodu
|
<p className="text-sm text-gray-800">{notification.workCenter.code}</p>
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-800">
|
|
||||||
{notification.workCenter.code}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">İş Merkezi Adı</label>
|
||||||
İş Merkezi Adı
|
<p className="text-sm text-gray-800">{notification.workCenter.name}</p>
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-800">
|
|
||||||
{notification.workCenter.name}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Konum</label>
|
||||||
Konum
|
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-800 flex items-center">
|
<p className="text-sm text-gray-800 flex items-center">
|
||||||
<FaMapMarkerAlt className="w-4 h-4 mr-2 text-gray-400" />
|
<FaMapMarkerAlt className="w-4 h-4 mr-2 text-gray-400" />
|
||||||
{notification.location}
|
{notification.location}
|
||||||
|
|
@ -175,14 +157,10 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Süreç Bilgileri</h3>
|
||||||
Süreç Bilgileri
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Bildiren Kişi</label>
|
||||||
Bildiren Kişi
|
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-800 flex items-center">
|
<p className="text-sm text-gray-800 flex items-center">
|
||||||
<FaUser className="w-4 h-4 mr-2 text-gray-400" />
|
<FaUser className="w-4 h-4 mr-2 text-gray-400" />
|
||||||
{notification.reportedBy}
|
{notification.reportedBy}
|
||||||
|
|
@ -190,21 +168,17 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Bildirim Tarihi</label>
|
||||||
Bildirim Tarihi
|
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-800 flex items-center">
|
<p className="text-sm text-gray-800 flex items-center">
|
||||||
<FaCalendarAlt className="w-4 h-4 mr-2 text-gray-400" />
|
<FaCalendarAlt className="w-4 h-4 mr-2 text-gray-400" />
|
||||||
{notification.reportedAt.toLocaleDateString("tr-TR")}{" "}
|
{notification.reportedAt.toLocaleDateString('tr-TR')}{' '}
|
||||||
{notification.reportedAt.toLocaleTimeString("tr-TR")}
|
{notification.reportedAt.toLocaleTimeString('tr-TR')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{notification.assignedTo && (
|
{notification.assignedTo && (
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Atanan Kişi</label>
|
||||||
Atanan Kişi
|
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-800 flex items-center">
|
<p className="text-sm text-gray-800 flex items-center">
|
||||||
<FaTools className="w-4 h-4 mr-2 text-gray-400" />
|
<FaTools className="w-4 h-4 mr-2 text-gray-400" />
|
||||||
{notification.assignedTo}
|
{notification.assignedTo}
|
||||||
|
|
@ -214,26 +188,18 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
||||||
|
|
||||||
{notification.workOrderId && (
|
{notification.workOrderId && (
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">İş Emri</label>
|
||||||
İş Emri
|
<p className="text-sm text-gray-800">{notification.workOrderId}</p>
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-800">
|
|
||||||
{notification.workOrderId}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{notification.closedBy && notification.closedAt && (
|
{notification.closedBy && notification.closedAt && (
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Kapatan</label>
|
||||||
Kapatan
|
<p className="text-sm text-gray-800">{notification.closedBy}</p>
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-800">
|
|
||||||
{notification.closedBy}
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">
|
||||||
{notification.closedAt.toLocaleDateString("tr-TR")}{" "}
|
{notification.closedAt.toLocaleDateString('tr-TR')}{' '}
|
||||||
{notification.closedAt.toLocaleTimeString("tr-TR")}
|
{notification.closedAt.toLocaleTimeString('tr-TR')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -257,12 +223,8 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
||||||
<div className="flex items-start space-x-2">
|
<div className="flex items-start space-x-2">
|
||||||
<FaFileAlt className="w-5 h-5 text-green-600 mt-0.5" />
|
<FaFileAlt className="w-5 h-5 text-green-600 mt-0.5" />
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h4 className="text-sm font-medium text-green-800 mb-1">
|
<h4 className="text-sm font-medium text-green-800 mb-1">Çözüm Notları</h4>
|
||||||
Çözüm Notları
|
<p className="text-sm text-green-700">{notification.resolutionNotes}</p>
|
||||||
</h4>
|
|
||||||
<p className="text-sm text-green-700">
|
|
||||||
{notification.resolutionNotes}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -279,16 +241,11 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-3">
|
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-3">
|
||||||
{notification.images.map((image, index) => (
|
{notification.images.map((image, index) => (
|
||||||
<div
|
<div key={index} className="border border-gray-200 rounded-lg p-2">
|
||||||
key={index}
|
|
||||||
className="border border-gray-200 rounded-lg p-2"
|
|
||||||
>
|
|
||||||
<div className="aspect-square bg-gray-100 rounded flex items-center justify-center">
|
<div className="aspect-square bg-gray-100 rounded flex items-center justify-center">
|
||||||
<FaCamera className="w-8 h-8 text-gray-400" />
|
<FaCamera className="w-8 h-8 text-gray-400" />
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-500 mt-1 truncate">
|
<p className="text-xs text-gray-500 mt-1 truncate">{image}</p>
|
||||||
{image}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -297,28 +254,22 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
||||||
|
|
||||||
{/* Timeline */}
|
{/* Timeline */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Zaman Çizelgesi</h3>
|
||||||
Zaman Çizelgesi
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center space-x-3 text-sm">
|
<div className="flex items-center space-x-3 text-sm">
|
||||||
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
|
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
|
||||||
<span className="text-gray-500">Oluşturuldu:</span>
|
<span className="text-gray-500">Oluşturuldu:</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{notification.creationTime.toLocaleDateString("tr-TR")}{" "}
|
{notification.creationTime.toLocaleDateString('tr-TR')}{' '}
|
||||||
{notification.creationTime.toLocaleTimeString("tr-TR")}
|
{notification.creationTime.toLocaleTimeString('tr-TR')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-3 text-sm">
|
<div className="flex items-center space-x-3 text-sm">
|
||||||
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
||||||
<span className="text-gray-500">Son Güncelleme:</span>
|
<span className="text-gray-500">Son Güncelleme:</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{notification.lastModificationTime.toLocaleDateString(
|
{notification.lastModificationTime.toLocaleDateString('tr-TR')}{' '}
|
||||||
"tr-TR"
|
{notification.lastModificationTime.toLocaleTimeString('tr-TR')}
|
||||||
)}{" "}
|
|
||||||
{notification.lastModificationTime.toLocaleTimeString(
|
|
||||||
"tr-TR"
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -336,7 +287,7 @@ const ViewFaultNotificationModal: React.FC<ViewFaultNotificationModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default ViewFaultNotificationModal;
|
export default ViewFaultNotificationModal
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,13 @@
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import {
|
import { FaTimes, FaTools, FaClock, FaUser, FaExclamationTriangle } from 'react-icons/fa'
|
||||||
FaTimes,
|
import { PmMaintenancePlan, MaintenancePlanTypeEnum, FrequencyUnitEnum } from '../../../types/pm'
|
||||||
FaTools,
|
import { mockMaterials } from '../../../mocks/mockMaterials'
|
||||||
FaClock,
|
import { PriorityEnum } from '../../../types/common'
|
||||||
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 {
|
interface ViewMaintenancePlanModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
plan: PmMaintenancePlan | null;
|
plan: PmMaintenancePlan | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
||||||
|
|
@ -25,93 +15,93 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
||||||
onClose,
|
onClose,
|
||||||
plan,
|
plan,
|
||||||
}) => {
|
}) => {
|
||||||
if (!isOpen || !plan) return null;
|
if (!isOpen || !plan) return null
|
||||||
|
|
||||||
const getPlanTypeDisplay = (type: MaintenancePlanTypeEnum) => {
|
const getPlanTypeDisplay = (type: MaintenancePlanTypeEnum) => {
|
||||||
const types = {
|
const types = {
|
||||||
[MaintenancePlanTypeEnum.Preventive]: {
|
[MaintenancePlanTypeEnum.Preventive]: {
|
||||||
label: "Önleyici Bakım",
|
label: 'Önleyici Bakım',
|
||||||
color: "bg-green-100 text-green-800",
|
color: 'bg-green-100 text-green-800',
|
||||||
},
|
},
|
||||||
[MaintenancePlanTypeEnum.Predictive]: {
|
[MaintenancePlanTypeEnum.Predictive]: {
|
||||||
label: "Kestirimci Bakım",
|
label: 'Kestirimci Bakım',
|
||||||
color: "bg-blue-100 text-blue-800",
|
color: 'bg-blue-100 text-blue-800',
|
||||||
},
|
},
|
||||||
[MaintenancePlanTypeEnum.Corrective]: {
|
[MaintenancePlanTypeEnum.Corrective]: {
|
||||||
label: "Düzeltici Bakım",
|
label: 'Düzeltici Bakım',
|
||||||
color: "bg-orange-100 text-orange-800",
|
color: 'bg-orange-100 text-orange-800',
|
||||||
},
|
},
|
||||||
[MaintenancePlanTypeEnum.Condition]: {
|
[MaintenancePlanTypeEnum.Condition]: {
|
||||||
label: "Durum Bazlı Bakım",
|
label: 'Durum Bazlı Bakım',
|
||||||
color: "bg-purple-100 text-purple-800",
|
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 getPriorityDisplay = (priority: PriorityEnum) => {
|
||||||
const priorities = {
|
const priorities = {
|
||||||
[PriorityEnum.Low]: {
|
[PriorityEnum.Low]: {
|
||||||
label: "Düşük",
|
label: 'Düşük',
|
||||||
color: "bg-gray-100 text-gray-800",
|
color: 'bg-gray-100 text-gray-800',
|
||||||
icon: null,
|
icon: null,
|
||||||
},
|
},
|
||||||
[PriorityEnum.Normal]: {
|
[PriorityEnum.Normal]: {
|
||||||
label: "Normal",
|
label: 'Normal',
|
||||||
color: "bg-blue-100 text-blue-800",
|
color: 'bg-blue-100 text-blue-800',
|
||||||
icon: null,
|
icon: null,
|
||||||
},
|
},
|
||||||
[PriorityEnum.High]: {
|
[PriorityEnum.High]: {
|
||||||
label: "Yüksek",
|
label: 'Yüksek',
|
||||||
color: "bg-yellow-100 text-yellow-800",
|
color: 'bg-yellow-100 text-yellow-800',
|
||||||
icon: FaExclamationTriangle,
|
icon: FaExclamationTriangle,
|
||||||
},
|
},
|
||||||
[PriorityEnum.Urgent]: {
|
[PriorityEnum.Urgent]: {
|
||||||
label: "Acil",
|
label: 'Acil',
|
||||||
color: "bg-red-100 text-red-800",
|
color: 'bg-red-100 text-red-800',
|
||||||
icon: FaExclamationTriangle,
|
icon: FaExclamationTriangle,
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
return (
|
return (
|
||||||
priorities[priority] || {
|
priorities[priority] || {
|
||||||
label: priority,
|
label: priority,
|
||||||
color: "bg-gray-100 text-gray-800",
|
color: 'bg-gray-100 text-gray-800',
|
||||||
icon: null,
|
icon: null,
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const getFrequencyDisplay = (frequency: number, unit: FrequencyUnitEnum) => {
|
const getFrequencyDisplay = (frequency: number, unit: FrequencyUnitEnum) => {
|
||||||
const units = {
|
const units = {
|
||||||
[FrequencyUnitEnum.Days]: frequency === 1 ? "gün" : "gün",
|
[FrequencyUnitEnum.Days]: frequency === 1 ? 'gün' : 'gün',
|
||||||
[FrequencyUnitEnum.Weeks]: frequency === 1 ? "hafta" : "hafta",
|
[FrequencyUnitEnum.Weeks]: frequency === 1 ? 'hafta' : 'hafta',
|
||||||
[FrequencyUnitEnum.Months]: frequency === 1 ? "ay" : "ay",
|
[FrequencyUnitEnum.Months]: frequency === 1 ? 'ay' : 'ay',
|
||||||
[FrequencyUnitEnum.Years]: frequency === 1 ? "yıl" : "yıl",
|
[FrequencyUnitEnum.Years]: frequency === 1 ? 'yıl' : 'yıl',
|
||||||
[FrequencyUnitEnum.Hours]: frequency === 1 ? "saat" : "saat",
|
[FrequencyUnitEnum.Hours]: frequency === 1 ? 'saat' : 'saat',
|
||||||
[FrequencyUnitEnum.Cycles]: frequency === 1 ? "çevrim" : "çevrim",
|
[FrequencyUnitEnum.Cycles]: frequency === 1 ? 'çevrim' : 'çevrim',
|
||||||
};
|
}
|
||||||
return `Her ${frequency} ${units[unit]}`;
|
return `Her ${frequency} ${units[unit]}`
|
||||||
};
|
}
|
||||||
|
|
||||||
const formatDate = (date: Date) => {
|
const formatDate = (date: Date) => {
|
||||||
return new Date(date).toLocaleDateString("tr-TR", {
|
return new Date(date).toLocaleDateString('tr-TR', {
|
||||||
year: "numeric",
|
year: 'numeric',
|
||||||
month: "long",
|
month: 'long',
|
||||||
day: "numeric",
|
day: 'numeric',
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const formatDuration = (minutes: number) => {
|
const formatDuration = (minutes: number) => {
|
||||||
if (minutes < 60) return `${minutes} dakika`;
|
if (minutes < 60) return `${minutes} dakika`
|
||||||
const hours = Math.floor(minutes / 60);
|
const hours = Math.floor(minutes / 60)
|
||||||
const remainingMinutes = minutes % 60;
|
const remainingMinutes = minutes % 60
|
||||||
if (remainingMinutes === 0) return `${hours} saat`;
|
if (remainingMinutes === 0) return `${hours} saat`
|
||||||
return `${hours} saat ${remainingMinutes} dakika`;
|
return `${hours} saat ${remainingMinutes} dakika`
|
||||||
};
|
}
|
||||||
|
|
||||||
const isOverdue = plan.nextDue && new Date(plan.nextDue) < new Date();
|
const isOverdue = plan.nextDue && new Date(plan.nextDue) < new Date()
|
||||||
const planTypeInfo = getPlanTypeDisplay(plan.planType);
|
const planTypeInfo = getPlanTypeDisplay(plan.planType)
|
||||||
const priorityInfo = getPriorityDisplay(plan.priority);
|
const priorityInfo = getPriorityDisplay(plan.priority)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<div className="flex items-center space-x-3">
|
||||||
<FaTools className="w-5 h-5 text-blue-600" />
|
<FaTools className="w-5 h-5 text-blue-600" />
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold text-gray-900">
|
<h2 className="text-lg font-semibold text-gray-900">{plan.planCode}</h2>
|
||||||
{plan.planCode}
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm text-gray-500">Bakım Planı Detayları</p>
|
<p className="text-sm text-gray-500">Bakım Planı Detayları</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||||
onClick={onClose}
|
|
||||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -147,19 +132,15 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
||||||
<span
|
<span
|
||||||
className={`inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium ${priorityInfo.color}`}
|
className={`inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium ${priorityInfo.color}`}
|
||||||
>
|
>
|
||||||
{priorityInfo.icon && (
|
{priorityInfo.icon && <priorityInfo.icon className="w-3 h-3 mr-1" />}
|
||||||
<priorityInfo.icon className="w-3 h-3 mr-1" />
|
|
||||||
)}
|
|
||||||
{priorityInfo.label}
|
{priorityInfo.label}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium ${
|
className={`inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium ${
|
||||||
plan.isActive
|
plan.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"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{plan.isActive ? "Aktif" : "Pasif"}
|
{plan.isActive ? 'Aktif' : 'Pasif'}
|
||||||
</span>
|
</span>
|
||||||
{isOverdue && (
|
{isOverdue && (
|
||||||
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-red-100 text-red-800">
|
<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="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Temel Bilgiler</h3>
|
||||||
Temel Bilgiler
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-500">
|
<label className="block text-sm font-medium text-gray-500">Plan Kodu</label>
|
||||||
Plan Kodu
|
<p className="text-sm text-gray-900 font-mono">{plan.planCode}</p>
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900 font-mono">
|
|
||||||
{plan.planCode}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-500">
|
<label className="block text-sm font-medium text-gray-500">İş Merkezi</label>
|
||||||
İş Merkezi
|
<p className="text-sm text-gray-900 font-mono">{plan.workCenter?.code}</p>
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900 font-mono">
|
|
||||||
{plan.workCenter?.code}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-500">
|
<label className="block text-sm font-medium text-gray-500">Açıklama</label>
|
||||||
Açıklama
|
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900">{plan.description}</p>
|
<p className="text-sm text-gray-900">{plan.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Sıklık ve Süre</h3>
|
||||||
Sıklık ve Süre
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<FaClock className="w-4 h-4 text-gray-400" />
|
<FaClock className="w-4 h-4 text-gray-400" />
|
||||||
|
|
@ -214,25 +181,19 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-500">
|
<label className="block text-sm font-medium text-gray-500">Tahmini Süre</label>
|
||||||
Tahmini Süre
|
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900">
|
<p className="text-sm text-gray-900">
|
||||||
{formatDuration(plan.estimatedDuration)}
|
{formatDuration(plan.estimatedDuration)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-500">
|
<label className="block text-sm font-medium text-gray-500">Sonraki Bakım</label>
|
||||||
Sonraki Bakım
|
|
||||||
</label>
|
|
||||||
<p
|
<p
|
||||||
className={`text-sm font-medium ${
|
className={`text-sm font-medium ${
|
||||||
isOverdue ? "text-red-600" : "text-gray-900"
|
isOverdue ? 'text-red-600' : 'text-gray-900'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{plan.nextDue
|
{plan.nextDue ? formatDate(plan.nextDue) : 'Belirtilmemiş'}
|
||||||
? formatDate(plan.nextDue)
|
|
||||||
: "Belirtilmemiş"}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -241,46 +202,32 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Tarih Bilgileri</h3>
|
||||||
Tarih Bilgileri
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-500">
|
<label className="block text-sm font-medium text-gray-500">Oluşturulma</label>
|
||||||
Oluşturulma
|
<p className="text-sm text-gray-900">{formatDate(plan.creationTime)}</p>
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900">
|
|
||||||
{formatDate(plan.creationTime)}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-500">
|
<label className="block text-sm font-medium text-gray-500">
|
||||||
Son Güncelleme
|
Son Güncelleme
|
||||||
</label>
|
</label>
|
||||||
<p className="text-sm text-gray-900">
|
<p className="text-sm text-gray-900">{formatDate(plan.lastModificationTime)}</p>
|
||||||
{formatDate(plan.lastModificationTime)}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-3">
|
<h3 className="text-lg font-medium text-gray-900 mb-3">İstatistikler</h3>
|
||||||
İstatistikler
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-500">
|
<label className="block text-sm font-medium text-gray-500">Durum</label>
|
||||||
Durum
|
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900">
|
<p className="text-sm text-gray-900">
|
||||||
{plan.isActive ? "Aktif plan" : "Pasif plan"}
|
{plan.isActive ? 'Aktif plan' : 'Pasif plan'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-500">
|
<label className="block text-sm font-medium text-gray-500">Tahmini Süre</label>
|
||||||
Tahmini Süre
|
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900">
|
<p className="text-sm text-gray-900">
|
||||||
{formatDuration(plan.estimatedDuration)}
|
{formatDuration(plan.estimatedDuration)}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -293,9 +240,7 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
||||||
{/* Instructions */}
|
{/* Instructions */}
|
||||||
{plan.instructions && (
|
{plan.instructions && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Bakım Talimatları</h3>
|
||||||
Bakım Talimatları
|
|
||||||
</h3>
|
|
||||||
<div className="bg-gray-50 rounded-lg p-3">
|
<div className="bg-gray-50 rounded-lg p-3">
|
||||||
<pre className="text-sm text-gray-700 whitespace-pre-wrap font-sans">
|
<pre className="text-sm text-gray-700 whitespace-pre-wrap font-sans">
|
||||||
{plan.instructions}
|
{plan.instructions}
|
||||||
|
|
@ -327,9 +272,7 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
||||||
{/* Required Materials */}
|
{/* Required Materials */}
|
||||||
{plan.requiredMaterials && plan.requiredMaterials.length > 0 && (
|
{plan.requiredMaterials && plan.requiredMaterials.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Gerekli Malzemeler</h3>
|
||||||
Gerekli Malzemeler
|
|
||||||
</h3>
|
|
||||||
<div className="bg-white border border-gray-200 rounded-lg overflow-hidden">
|
<div className="bg-white border border-gray-200 rounded-lg overflow-hidden">
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
<thead className="bg-gray-50">
|
<thead className="bg-gray-50">
|
||||||
|
|
@ -350,9 +293,7 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{plan.requiredMaterials.map((material, index) => {
|
{plan.requiredMaterials.map((material, index) => {
|
||||||
const materialInfo = mockMaterials.find(
|
const materialInfo = mockMaterials.find((m) => m.id === material.materialId)
|
||||||
(m) => m.id === material.materialId
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<tr key={index} className="hover:bg-gray-50">
|
<tr key={index} className="hover:bg-gray-50">
|
||||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
<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}
|
{materialInfo?.code || material.materialId}
|
||||||
</div>
|
</div>
|
||||||
{materialInfo && (
|
{materialInfo && (
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">{materialInfo.name}</div>
|
||||||
{materialInfo.name}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -377,15 +316,15 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
||||||
<span
|
<span
|
||||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||||
material.isRequired
|
material.isRequired
|
||||||
? "bg-red-100 text-red-800"
|
? 'bg-red-100 text-red-800'
|
||||||
: "bg-green-100 text-green-800"
|
: 'bg-green-100 text-green-800'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{material.isRequired ? "Zorunlu" : "Opsiyonel"}
|
{material.isRequired ? 'Zorunlu' : 'Opsiyonel'}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
@ -406,7 +345,7 @@ const ViewMaintenancePlanModal: React.FC<ViewMaintenancePlanModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default ViewMaintenancePlanModal;
|
export default ViewMaintenancePlanModal
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,20 @@
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import {
|
import { FaTimes, FaEdit, FaUser, FaAward, FaEnvelope, FaPhone, FaClock } from 'react-icons/fa'
|
||||||
FaTimes,
|
import { Team, TeamRoleEnum } from '../../../types/common'
|
||||||
FaEdit,
|
import { getTeamRoleColor, getTeamRoleIcon, getTeamRoleText } from '../../../utils/erp'
|
||||||
FaUser,
|
|
||||||
FaAward,
|
|
||||||
FaEnvelope,
|
|
||||||
FaPhone,
|
|
||||||
FaClock,
|
|
||||||
} from "react-icons/fa";
|
|
||||||
import { Team, TeamRoleEnum } from "../../../types/common";
|
|
||||||
import {
|
|
||||||
getTeamRoleColor,
|
|
||||||
getTeamRoleIcon,
|
|
||||||
getTeamRoleText,
|
|
||||||
} from "../../../utils/erp";
|
|
||||||
|
|
||||||
interface ViewTeamModalProps {
|
interface ViewTeamModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onEdit: (team: Team) => void;
|
onEdit: (team: Team) => void
|
||||||
team: Team | null;
|
team: Team | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
const ViewTeamModal: React.FC<ViewTeamModalProps> = ({ isOpen, onClose, onEdit, team }) => {
|
||||||
isOpen,
|
if (!team) return null
|
||||||
onClose,
|
|
||||||
onEdit,
|
|
||||||
team,
|
|
||||||
}) => {
|
|
||||||
if (!team) return null;
|
|
||||||
|
|
||||||
const leader = team.members.find(
|
const leader = team.members.find((member) => member.role === TeamRoleEnum.Lead)
|
||||||
(member) => member.role === TeamRoleEnum.Lead
|
const activeMembers = team.members.filter((member) => member.isActive)
|
||||||
);
|
|
||||||
const activeMembers = team.members.filter((member) => member.isActive);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isOpen &&
|
isOpen &&
|
||||||
|
|
@ -43,9 +24,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold text-gray-900">
|
<h2 className="text-lg font-semibold text-gray-900">{team.name}</h2>
|
||||||
{team.name}
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm text-gray-500 mt-1">{team.code}</p>
|
<p className="text-sm text-gray-500 mt-1">{team.code}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-3">
|
<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="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Ekip Bilgileri</h3>
|
||||||
Ekip Bilgileri
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Ekip Kodu</label>
|
||||||
Ekip Kodu
|
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900">{team.code}</p>
|
<p className="text-sm text-gray-900">{team.code}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Ekip Adı</label>
|
||||||
Ekip Adı
|
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900">{team.name}</p>
|
<p className="text-sm text-gray-900">{team.name}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Açıklama</label>
|
||||||
Açıklama
|
<p className="text-sm text-gray-900">{team.description}</p>
|
||||||
</label>
|
|
||||||
<p className="text-sm text-gray-900">
|
|
||||||
{team.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">Durum</label>
|
||||||
Durum
|
|
||||||
</label>
|
|
||||||
<p
|
<p
|
||||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||||
team.isActive
|
team.isActive
|
||||||
? "bg-green-100 text-green-800"
|
? 'bg-green-100 text-green-800'
|
||||||
: "bg-gray-100 text-gray-800"
|
: 'bg-gray-100 text-gray-800'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{team.isActive ? "Aktif" : "Pasif"}
|
{team.isActive ? 'Aktif' : 'Pasif'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -115,9 +82,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
||||||
{/* Specializations */}
|
{/* Specializations */}
|
||||||
{team.specializations && team.specializations.length > 0 && (
|
{team.specializations && team.specializations.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-sm font-medium text-gray-500 mb-1.5">
|
<h4 className="text-sm font-medium text-gray-500 mb-1.5">Uzmanlık Alanları</h4>
|
||||||
Uzmanlık Alanları
|
|
||||||
</h4>
|
|
||||||
<div className="flex flex-wrap gap-1.5">
|
<div className="flex flex-wrap gap-1.5">
|
||||||
{team.specializations.map((spec, index) => (
|
{team.specializations.map((spec, index) => (
|
||||||
<span
|
<span
|
||||||
|
|
@ -133,22 +98,16 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Zaman Bilgileri</h3>
|
||||||
Zaman Bilgileri
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center space-x-2 text-sm text-gray-600">
|
<div className="flex items-center space-x-2 text-sm text-gray-600">
|
||||||
<FaClock className="w-4 h-4" />
|
<FaClock className="w-4 h-4" />
|
||||||
<span>
|
<span>Oluşturuldu: {team.creationTime.toLocaleDateString('tr-TR')}</span>
|
||||||
Oluşturuldu:{" "}
|
|
||||||
{team.creationTime.toLocaleDateString("tr-TR")}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2 text-sm text-gray-600">
|
<div className="flex items-center space-x-2 text-sm text-gray-600">
|
||||||
<FaClock className="w-4 h-4" />
|
<FaClock className="w-4 h-4" />
|
||||||
<span>
|
<span>
|
||||||
Son Güncelleme:{" "}
|
Son Güncelleme: {team.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||||
{team.lastModificationTime.toLocaleDateString("tr-TR")}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -158,9 +117,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
||||||
{/* Team Leader */}
|
{/* Team Leader */}
|
||||||
{leader && (
|
{leader && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-3">
|
<h3 className="text-base font-medium text-gray-900 mb-3">Ekip Lideri</h3>
|
||||||
Ekip Lideri
|
|
||||||
</h3>
|
|
||||||
<div className="bg-purple-50 rounded-lg p-3">
|
<div className="bg-purple-50 rounded-lg p-3">
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<div className="w-10 h-10 bg-purple-600 rounded-full flex items-center justify-center">
|
<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-1">
|
||||||
<div className="flex items-center space-x-2 mb-1">
|
<div className="flex items-center space-x-2 mb-1">
|
||||||
<h4 className="font-medium text-gray-900">
|
<h4 className="font-medium text-gray-900">
|
||||||
{leader.employee?.firstName}{" "}
|
{leader.employee?.firstName} {leader.employee?.lastName}
|
||||||
{leader.employee?.lastName}
|
|
||||||
</h4>
|
</h4>
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium flex items-center space-x-1 ${getTeamRoleColor(
|
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)}
|
{getTeamRoleIcon(leader.role)}
|
||||||
|
|
@ -199,8 +155,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500 mt-2">
|
<div className="text-xs text-gray-500 mt-2">
|
||||||
Katılma Tarihi:{" "}
|
Katılma Tarihi: {leader.joinDate.toLocaleDateString('tr-TR')}
|
||||||
{leader.joinDate.toLocaleDateString("tr-TR")}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -230,12 +185,11 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center space-x-2 mb-1">
|
<div className="flex items-center space-x-2 mb-1">
|
||||||
<h4 className="font-medium text-gray-900">
|
<h4 className="font-medium text-gray-900">
|
||||||
{member.employee?.firstName}{" "}
|
{member.employee?.firstName} {member.employee?.lastName}
|
||||||
{member.employee?.lastName}
|
|
||||||
</h4>
|
</h4>
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded-full text-xs font-medium flex items-center space-x-1 ${getTeamRoleColor(
|
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)}
|
{getTeamRoleIcon(member.role)}
|
||||||
|
|
@ -259,8 +213,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">
|
||||||
Katılma Tarihi:{" "}
|
Katılma Tarihi: {member.joinDate.toLocaleDateString('tr-TR')}
|
||||||
{member.joinDate.toLocaleDateString("tr-TR")}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -283,7 +236,7 @@ const ViewTeamModal: React.FC<ViewTeamModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default ViewTeamModal;
|
export default ViewTeamModal
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import {
|
import {
|
||||||
FaTimes,
|
FaTimes,
|
||||||
FaCog,
|
FaCog,
|
||||||
|
|
@ -7,20 +7,20 @@ import {
|
||||||
FaUser,
|
FaUser,
|
||||||
FaWrench,
|
FaWrench,
|
||||||
FaExclamationTriangle,
|
FaExclamationTriangle,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import { PmWorkCenter, WorkOrderStatusEnum } from "../../../types/pm";
|
import { PmWorkCenter, WorkOrderStatusEnum } from '../../../types/pm'
|
||||||
import {
|
import {
|
||||||
getWorkCenterStatusColor,
|
getWorkCenterStatusColor,
|
||||||
getWorkCenterStatusIcon,
|
getWorkCenterStatusIcon,
|
||||||
getWorkCenterStatusText,
|
getWorkCenterStatusText,
|
||||||
getCriticalityLevelColor,
|
getCriticalityLevelColor,
|
||||||
getCriticalityLevelText,
|
getCriticalityLevelText,
|
||||||
} from "../../../utils/erp";
|
} from '../../../utils/erp'
|
||||||
|
|
||||||
interface ViewWorkCenterModalProps {
|
interface ViewWorkCenterModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
workCenter: PmWorkCenter;
|
workCenter: PmWorkCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
|
|
@ -28,28 +28,28 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
onClose,
|
onClose,
|
||||||
workCenter,
|
workCenter,
|
||||||
}) => {
|
}) => {
|
||||||
if (!isOpen || !workCenter) return null;
|
if (!isOpen || !workCenter) return null
|
||||||
|
|
||||||
const isWarrantyExpiring = (workCenter: PmWorkCenter) => {
|
const isWarrantyExpiring = (workCenter: PmWorkCenter) => {
|
||||||
if (!workCenter.warrantyExpiry) return false;
|
if (!workCenter.warrantyExpiry) return false
|
||||||
const today = new Date();
|
const today = new Date()
|
||||||
const diffTime = workCenter.warrantyExpiry.getTime() - today.getTime();
|
const diffTime = workCenter.warrantyExpiry.getTime() - today.getTime()
|
||||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
||||||
return diffDays <= 90 && diffDays > 0;
|
return diffDays <= 90 && diffDays > 0
|
||||||
};
|
}
|
||||||
|
|
||||||
const isWarrantyExpired = (workCenter: PmWorkCenter) => {
|
const isWarrantyExpired = (workCenter: PmWorkCenter) => {
|
||||||
if (!workCenter.warrantyExpiry) return false;
|
if (!workCenter.warrantyExpiry) return false
|
||||||
const today = new Date();
|
const today = new Date()
|
||||||
return workCenter.warrantyExpiry < today;
|
return workCenter.warrantyExpiry < today
|
||||||
};
|
}
|
||||||
|
|
||||||
const getWorkCenterAge = (installationDate: Date) => {
|
const getWorkCenterAge = (installationDate: Date) => {
|
||||||
const today = new Date();
|
const today = new Date()
|
||||||
const diffTime = today.getTime() - installationDate.getTime();
|
const diffTime = today.getTime() - installationDate.getTime()
|
||||||
const diffYears = Math.floor(diffTime / (1000 * 60 * 60 * 24 * 365));
|
const diffYears = Math.floor(diffTime / (1000 * 60 * 60 * 24 * 365))
|
||||||
return diffYears;
|
return diffYears
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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">
|
<div className="flex items-center space-x-3">
|
||||||
<FaCog className="w-5 h-5 text-blue-600" />
|
<FaCog className="w-5 h-5 text-blue-600" />
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold text-gray-900">
|
<h2 className="text-lg font-semibold text-gray-900">{workCenter.name}</h2>
|
||||||
{workCenter.name}
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm text-gray-500">{workCenter.code}</p>
|
<p className="text-sm text-gray-500">{workCenter.code}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded-lg transition-colors">
|
||||||
onClick={onClose}
|
|
||||||
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-4 h-4 text-gray-500" />
|
<FaTimes className="w-4 h-4 text-gray-500" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -79,15 +74,13 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Durum Bilgileri</h3>
|
||||||
Durum Bilgileri
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-gray-700">Operasyonel Durum:</span>
|
<span className="text-gray-700">Operasyonel Durum:</span>
|
||||||
<span
|
<span
|
||||||
className={`px-2.5 py-1 rounded-full text-xs font-medium flex items-center space-x-1.5 ${getWorkCenterStatusColor(
|
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)}
|
{getWorkCenterStatusIcon(workCenter.status)}
|
||||||
|
|
@ -98,7 +91,7 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
<span className="text-gray-700">Kritiklik Seviyesi:</span>
|
<span className="text-gray-700">Kritiklik Seviyesi:</span>
|
||||||
<span
|
<span
|
||||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getCriticalityLevelColor(
|
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getCriticalityLevelColor(
|
||||||
workCenter.criticality
|
workCenter.criticality,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getCriticalityLevelText(workCenter.criticality)}
|
{getCriticalityLevelText(workCenter.criticality)}
|
||||||
|
|
@ -109,20 +102,18 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
<span
|
<span
|
||||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${
|
className={`px-2.5 py-1 rounded-full text-xs font-medium ${
|
||||||
workCenter.isActive
|
workCenter.isActive
|
||||||
? "bg-green-100 text-green-800"
|
? 'bg-green-100 text-green-800'
|
||||||
: "bg-red-100 text-red-800"
|
: 'bg-red-100 text-red-800'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{workCenter.isActive ? "Aktif" : "Pasif"}
|
{workCenter.isActive ? 'Aktif' : 'Pasif'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Lokasyon Bilgileri</h3>
|
||||||
Lokasyon Bilgileri
|
|
||||||
</h3>
|
|
||||||
<div className="flex items-center space-x-2 text-gray-700">
|
<div className="flex items-center space-x-2 text-gray-700">
|
||||||
<FaMapMarkerAlt className="w-4 h-4 text-blue-600" />
|
<FaMapMarkerAlt className="w-4 h-4 text-blue-600" />
|
||||||
<span>{workCenter.location}</span>
|
<span>{workCenter.location}</span>
|
||||||
|
|
@ -132,38 +123,34 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Temel Bilgiler</h3>
|
||||||
Temel Bilgiler
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">Üretici:</span>
|
<span className="text-gray-600">Üretici:</span>
|
||||||
<span className="text-gray-900 font-medium">
|
<span className="text-gray-900 font-medium">
|
||||||
{workCenter.manufacturer || "-"}
|
{workCenter.manufacturer || '-'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">Model:</span>
|
<span className="text-gray-600">Model:</span>
|
||||||
<span className="text-gray-900 font-medium">
|
<span className="text-gray-900 font-medium">{workCenter.model || '-'}</span>
|
||||||
{workCenter.model || "-"}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">Seri No:</span>
|
<span className="text-gray-600">Seri No:</span>
|
||||||
<span className="text-gray-900 font-medium">
|
<span className="text-gray-900 font-medium">
|
||||||
{workCenter.serialNumber || "-"}
|
{workCenter.serialNumber || '-'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">İş Merkezi Tipi:</span>
|
<span className="text-gray-600">İş Merkezi Tipi:</span>
|
||||||
<span className="text-gray-900 font-medium">
|
<span className="text-gray-900 font-medium">
|
||||||
{workCenter.workCenterType?.name || "-"}
|
{workCenter.workCenterType?.name || '-'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-600">Kategori:</span>
|
<span className="text-gray-600">Kategori:</span>
|
||||||
<span className="text-gray-900 font-medium">
|
<span className="text-gray-900 font-medium">
|
||||||
{workCenter.workCenterType?.category || "-"}
|
{workCenter.workCenterType?.category || '-'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -174,9 +161,7 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{workCenter.description && (
|
{workCenter.description && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Açıklama</h3>
|
||||||
Açıklama
|
|
||||||
</h3>
|
|
||||||
<p className="text-sm text-gray-700 bg-gray-50 p-3 rounded-lg">
|
<p className="text-sm text-gray-700 bg-gray-50 p-3 rounded-lg">
|
||||||
{workCenter.description}
|
{workCenter.description}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -185,19 +170,15 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
|
|
||||||
{/* Date Information */}
|
{/* Date Information */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Tarih Bilgileri</h3>
|
||||||
Tarih Bilgileri
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
<div className="bg-gray-50 p-3 rounded-lg">
|
<div className="bg-gray-50 p-3 rounded-lg">
|
||||||
<div className="flex items-center space-x-2 mb-2">
|
<div className="flex items-center space-x-2 mb-2">
|
||||||
<FaCalendar className="w-4 h-4 text-blue-600" />
|
<FaCalendar className="w-4 h-4 text-blue-600" />
|
||||||
<span className="font-medium text-gray-900">
|
<span className="font-medium text-gray-900">Kurulum Tarihi</span>
|
||||||
Kurulum Tarihi
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-700">
|
<p className="text-gray-700">
|
||||||
{workCenter.installationDate.toLocaleDateString("tr-TR")}
|
{workCenter.installationDate.toLocaleDateString('tr-TR')}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-gray-500 mt-1">
|
<p className="text-sm text-gray-500 mt-1">
|
||||||
Yaş: {getWorkCenterAge(workCenter.installationDate)} yıl
|
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="bg-gray-50 p-3 rounded-lg">
|
||||||
<div className="flex items-center space-x-2 mb-2">
|
<div className="flex items-center space-x-2 mb-2">
|
||||||
<FaCalendar className="w-4 h-4 text-orange-600" />
|
<FaCalendar className="w-4 h-4 text-orange-600" />
|
||||||
<span className="font-medium text-gray-900">
|
<span className="font-medium text-gray-900">Garanti Bitiş Tarihi</span>
|
||||||
Garanti Bitiş Tarihi
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
className={`font-medium ${
|
className={`font-medium ${
|
||||||
isWarrantyExpired(workCenter)
|
isWarrantyExpired(workCenter)
|
||||||
? "text-red-600"
|
? 'text-red-600'
|
||||||
: isWarrantyExpiring(workCenter)
|
: isWarrantyExpiring(workCenter)
|
||||||
? "text-orange-600"
|
? 'text-orange-600'
|
||||||
: "text-gray-700"
|
: 'text-gray-700'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{workCenter.warrantyExpiry.toLocaleDateString("tr-TR")}
|
{workCenter.warrantyExpiry.toLocaleDateString('tr-TR')}
|
||||||
{isWarrantyExpiring(workCenter) &&
|
{isWarrantyExpiring(workCenter) && !isWarrantyExpired(workCenter) && (
|
||||||
!isWarrantyExpired(workCenter) && (
|
<span className="text-xs text-orange-600 ml-2">(Yakında sona eriyor)</span>
|
||||||
<span className="text-xs text-orange-600 ml-2">
|
)}
|
||||||
(Yakında sona eriyor)
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{isWarrantyExpired(workCenter) && (
|
{isWarrantyExpired(workCenter) && (
|
||||||
<span className="text-xs text-red-600 ml-2">
|
<span className="text-xs text-red-600 ml-2">(Garanti doldu)</span>
|
||||||
(Garanti doldu)
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -243,58 +217,49 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
<span className="font-medium text-gray-900">Oluşturan</span>
|
<span className="font-medium text-gray-900">Oluşturan</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-500 mt-1">
|
<p className="text-sm text-gray-500 mt-1">
|
||||||
{workCenter.creationTime.toLocaleDateString("tr-TR")}
|
{workCenter.creationTime.toLocaleDateString('tr-TR')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-gray-50 p-3 rounded-lg">
|
<div className="bg-gray-50 p-3 rounded-lg">
|
||||||
<div className="flex items-center space-x-2 mb-2">
|
<div className="flex items-center space-x-2 mb-2">
|
||||||
<FaCalendar className="w-4 h-4 text-gray-600" />
|
<FaCalendar className="w-4 h-4 text-gray-600" />
|
||||||
<span className="font-medium text-gray-900">
|
<span className="font-medium text-gray-900">Son Güncelleme</span>
|
||||||
Son Güncelleme
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-700">
|
<p className="text-gray-700">
|
||||||
{workCenter.lastModificationTime.toLocaleDateString("tr-TR")}
|
{workCenter.lastModificationTime.toLocaleDateString('tr-TR')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Technical Specifications */}
|
{/* Technical Specifications */}
|
||||||
{workCenter.specifications &&
|
{workCenter.specifications && workCenter.specifications.length > 0 && (
|
||||||
workCenter.specifications.length > 0 && (
|
<div>
|
||||||
<div>
|
<h3 className="text-base font-medium text-gray-900 mb-2">Teknik Özellikler</h3>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
Teknik Özellikler
|
{workCenter.specifications.map((spec) => (
|
||||||
</h3>
|
<div key={spec.id} className="bg-gray-50 p-3 rounded-lg">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
<div className="flex items-center justify-between mb-1">
|
||||||
{workCenter.specifications.map((spec) => (
|
<span className="font-medium text-gray-900">{spec.specificationName}</span>
|
||||||
<div key={spec.id} className="bg-gray-50 p-3 rounded-lg">
|
{spec.isRequired && (
|
||||||
<div className="flex items-center justify-between mb-1">
|
<span className="px-2 py-0.5 bg-blue-100 text-blue-800 text-xs rounded-full">
|
||||||
<span className="font-medium text-gray-900">
|
Zorunlu
|
||||||
{spec.specificationName}
|
|
||||||
</span>
|
</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">
|
||||||
</div>
|
{spec.specificationValue} {spec.unit}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Maintenance Plans Summary */}
|
{/* Maintenance Plans Summary */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">Bakım Planları</h3>
|
||||||
Bakım Planları
|
|
||||||
</h3>
|
|
||||||
<div className="bg-gray-50 p-3 rounded-lg">
|
<div className="bg-gray-50 p-3 rounded-lg">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -310,9 +275,7 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
|
|
||||||
{/* Work Orders Summary */}
|
{/* Work Orders Summary */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium text-gray-900 mb-2">
|
<h3 className="text-base font-medium text-gray-900 mb-2">İş Emirleri</h3>
|
||||||
İş Emirleri
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||||
<div className="bg-gray-50 p-3 rounded-lg">
|
<div className="bg-gray-50 p-3 rounded-lg">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|
@ -331,7 +294,7 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<p className="text-lg font-semibold text-gray-900">
|
<p className="text-lg font-semibold text-gray-900">
|
||||||
{workCenter.workOrders?.filter(
|
{workCenter.workOrders?.filter(
|
||||||
(wo) => wo.status === WorkOrderStatusEnum.InProgress
|
(wo) => wo.status === WorkOrderStatusEnum.InProgress,
|
||||||
).length || 0}
|
).length || 0}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-gray-600">Devam Eden</p>
|
<p className="text-sm text-gray-600">Devam Eden</p>
|
||||||
|
|
@ -367,7 +330,7 @@ const ViewWorkCenterModal: React.FC<ViewWorkCenterModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default ViewWorkCenterModal;
|
export default ViewWorkCenterModal
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import {
|
import {
|
||||||
FaTimes,
|
FaTimes,
|
||||||
FaEye,
|
FaEye,
|
||||||
|
|
@ -11,21 +11,21 @@ import {
|
||||||
FaDollarSign,
|
FaDollarSign,
|
||||||
FaCheck,
|
FaCheck,
|
||||||
FaExclamationTriangle,
|
FaExclamationTriangle,
|
||||||
} from "react-icons/fa";
|
} from 'react-icons/fa'
|
||||||
import { PmMaintenanceWorkOrder, WorkOrderStatusEnum } from "../../../types/pm";
|
import { PmMaintenanceWorkOrder, WorkOrderStatusEnum } from '../../../types/pm'
|
||||||
import {
|
import {
|
||||||
getPriorityColor,
|
getPriorityColor,
|
||||||
getPriorityText,
|
getPriorityText,
|
||||||
getWorkOrderStatusColor,
|
getWorkOrderStatusColor,
|
||||||
getWorkOrderStatusText,
|
getWorkOrderStatusText,
|
||||||
getWorkOrderTypeText,
|
getWorkOrderTypeText,
|
||||||
} from "../../../utils/erp";
|
} from '../../../utils/erp'
|
||||||
|
|
||||||
interface ViewWorkOrderModalProps {
|
interface ViewWorkOrderModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
onClose: () => void
|
||||||
onEdit: (workOrder: PmMaintenanceWorkOrder) => void;
|
onEdit: (workOrder: PmMaintenanceWorkOrder) => void
|
||||||
workOrder: PmMaintenanceWorkOrder;
|
workOrder: PmMaintenanceWorkOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
|
|
@ -36,36 +36,25 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const getCompletionPercentage = () => {
|
const getCompletionPercentage = () => {
|
||||||
const completedActivities = workOrder.activities.filter(
|
const completedActivities = workOrder.activities.filter(
|
||||||
(activity) => activity.completedAt
|
(activity) => activity.completedAt,
|
||||||
).length;
|
).length
|
||||||
const totalActivities = workOrder.activities.length;
|
const totalActivities = workOrder.activities.length
|
||||||
return totalActivities > 0
|
return totalActivities > 0 ? Math.round((completedActivities / totalActivities) * 100) : 0
|
||||||
? Math.round((completedActivities / totalActivities) * 100)
|
}
|
||||||
: 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTotalMaterialCost = () => {
|
const getTotalMaterialCost = () => {
|
||||||
return workOrder.materials.reduce(
|
return workOrder.materials.reduce((total, material) => total + material.totalCost, 0)
|
||||||
(total, material) => total + material.totalCost,
|
}
|
||||||
0
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTotalPlannedDuration = () => {
|
const getTotalPlannedDuration = () => {
|
||||||
return workOrder.activities.reduce(
|
return workOrder.activities.reduce((total, activity) => total + activity.plannedDuration, 0)
|
||||||
(total, activity) => total + activity.plannedDuration,
|
}
|
||||||
0
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTotalActualDuration = () => {
|
const getTotalActualDuration = () => {
|
||||||
return workOrder.activities.reduce(
|
return workOrder.activities.reduce((total, activity) => total + activity.actualDuration, 0)
|
||||||
(total, activity) => total + activity.actualDuration,
|
}
|
||||||
0
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<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-4">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<FaEye className="w-5 h-5 text-blue-600" />
|
<FaEye className="w-5 h-5 text-blue-600" />
|
||||||
<h3 className="text-lg font-semibold text-gray-900">
|
<h3 className="text-lg font-semibold text-gray-900">İş Emri Detayları</h3>
|
||||||
İş Emri Detayları
|
|
||||||
</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
className={`px-2.5 py-1 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
||||||
workOrder.status
|
workOrder.status,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getWorkOrderStatusText(workOrder.status)}
|
{getWorkOrderStatusText(workOrder.status)}
|
||||||
|
|
@ -94,10 +81,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
<FaEdit className="w-4 h-4" />
|
<FaEdit className="w-4 h-4" />
|
||||||
<span>Düzenle</span>
|
<span>Düzenle</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
||||||
onClick={onClose}
|
|
||||||
className="text-gray-400 hover:text-gray-600"
|
|
||||||
>
|
|
||||||
<FaTimes className="w-5 h-5" />
|
<FaTimes className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -113,14 +97,14 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<span
|
<span
|
||||||
className={`inline-block px-2 py-0.5 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
className={`inline-block px-2 py-0.5 rounded-full text-xs font-medium ${getWorkOrderStatusColor(
|
||||||
workOrder.status
|
workOrder.status,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getWorkOrderTypeText(workOrder.orderType)}
|
{getWorkOrderTypeText(workOrder.orderType)}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`inline-block px-2 py-0.5 rounded-full text-xs font-medium ml-2 ${getPriorityColor(
|
className={`inline-block px-2 py-0.5 rounded-full text-xs font-medium ml-2 ${getPriorityColor(
|
||||||
workOrder.priority
|
workOrder.priority,
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getPriorityText(workOrder.priority)}
|
{getPriorityText(workOrder.priority)}
|
||||||
|
|
@ -133,7 +117,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
{workOrder.workCenter?.code || workOrder.workCenterId}
|
{workOrder.workCenter?.code || workOrder.workCenterId}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-600">
|
<p className="text-xs text-gray-600">
|
||||||
{workOrder.workCenter?.name || "İş merkezi bilgisi bulunamadı"}
|
{workOrder.workCenter?.name || 'İş merkezi bilgisi bulunamadı'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -147,7 +131,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
<label className="text-xs text-gray-600">Atanan</label>
|
<label className="text-xs text-gray-600">Atanan</label>
|
||||||
<p className="font-medium text-sm text-gray-900 flex items-center">
|
<p className="font-medium text-sm text-gray-900 flex items-center">
|
||||||
<FaWrench className="w-4 h-4 mr-2 text-gray-500" />
|
<FaWrench className="w-4 h-4 mr-2 text-gray-500" />
|
||||||
{workOrder.assignedTo || "Atanmadı"}
|
{workOrder.assignedTo || 'Atanmadı'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -155,9 +139,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h4 className="text-base font-semibold text-gray-900 mb-2">
|
<h4 className="text-base font-semibold text-gray-900 mb-2">Açıklama</h4>
|
||||||
Açıklama
|
|
||||||
</h4>
|
|
||||||
<div className="bg-gray-50 rounded-lg p-3">
|
<div className="bg-gray-50 rounded-lg p-3">
|
||||||
<p className="text-sm text-gray-800">{workOrder.description}</p>
|
<p className="text-sm text-gray-800">{workOrder.description}</p>
|
||||||
</div>
|
</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">
|
<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="text-gray-600">Oluşturulma</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{workOrder.creationTime.toLocaleDateString("tr-TR")}
|
{workOrder.creationTime.toLocaleDateString('tr-TR')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{workOrder.scheduledStart && (
|
{workOrder.scheduledStart && (
|
||||||
<div className="flex justify-between items-center p-2 bg-blue-50 rounded-lg text-sm">
|
<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="text-gray-600">Planlanan Başlangıç</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{workOrder.scheduledStart.toLocaleDateString("tr-TR")}
|
{workOrder.scheduledStart.toLocaleDateString('tr-TR')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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">
|
<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="text-gray-600">Planlanan Bitiş</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{workOrder.scheduledEnd.toLocaleDateString("tr-TR")}
|
{workOrder.scheduledEnd.toLocaleDateString('tr-TR')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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">
|
<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="text-gray-600">Gerçek Başlangıç</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{workOrder.actualStart.toLocaleDateString("tr-TR")}
|
{workOrder.actualStart.toLocaleDateString('tr-TR')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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">
|
<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="text-gray-600">Gerçek Bitiş</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{workOrder.actualEnd.toLocaleDateString("tr-TR")}
|
{workOrder.actualEnd.toLocaleDateString('tr-TR')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -224,13 +206,13 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
<div className="flex justify-between items-center mb-2">
|
<div className="flex justify-between items-center mb-2">
|
||||||
<span className="text-gray-600">Tahmini Maliyet</span>
|
<span className="text-gray-600">Tahmini Maliyet</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
₺{workOrder.estimatedCost.toLocaleString("tr-TR")}
|
₺{workOrder.estimatedCost.toLocaleString('tr-TR')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<span className="text-gray-600">Gerçek Maliyet</span>
|
<span className="text-gray-600">Gerçek Maliyet</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
₺{workOrder.actualCost.toLocaleString("tr-TR")}
|
₺{workOrder.actualCost.toLocaleString('tr-TR')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -239,9 +221,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
<div className="p-2 bg-orange-50 rounded-lg text-sm">
|
<div className="p-2 bg-orange-50 rounded-lg text-sm">
|
||||||
<div className="flex justify-between items-center mb-2">
|
<div className="flex justify-between items-center mb-2">
|
||||||
<span className="text-gray-600">İlerleme</span>
|
<span className="text-gray-600">İlerleme</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">{getCompletionPercentage()}%</span>
|
||||||
{getCompletionPercentage()}%
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full bg-gray-200 rounded-full h-2">
|
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||||
<div
|
<div
|
||||||
|
|
@ -255,15 +235,11 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
<div className="p-2 bg-blue-50 rounded-lg text-sm">
|
<div className="p-2 bg-blue-50 rounded-lg text-sm">
|
||||||
<div className="flex justify-between items-center mb-2">
|
<div className="flex justify-between items-center mb-2">
|
||||||
<span className="text-gray-600">Planlanan Süre</span>
|
<span className="text-gray-600">Planlanan Süre</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">{getTotalPlannedDuration()} dk</span>
|
||||||
{getTotalPlannedDuration()} dk
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<span className="text-gray-600">Gerçek Süre</span>
|
<span className="text-gray-600">Gerçek Süre</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">{getTotalActualDuration()} dk</span>
|
||||||
{getTotalActualDuration()} dk
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -275,8 +251,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h4 className="text-base font-semibold text-gray-900 mb-2 flex items-center">
|
<h4 className="text-base font-semibold text-gray-900 mb-2 flex items-center">
|
||||||
<FaTools className="w-5 h-5 mr-2" />
|
<FaTools className="w-5 h-5 mr-2" />
|
||||||
Aktiviteler (
|
Aktiviteler ({workOrder.activities.filter((a) => a.completedAt).length}/
|
||||||
{workOrder.activities.filter((a) => a.completedAt).length}/
|
|
||||||
{workOrder.activities.length})
|
{workOrder.activities.length})
|
||||||
</h4>
|
</h4>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|
@ -285,8 +260,8 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
key={activity.id}
|
key={activity.id}
|
||||||
className={`p-3 rounded-lg border ${
|
className={`p-3 rounded-lg border ${
|
||||||
activity.completedAt
|
activity.completedAt
|
||||||
? "bg-green-50 border-green-200"
|
? 'bg-green-50 border-green-200'
|
||||||
: "bg-gray-50 border-gray-200"
|
: 'bg-gray-50 border-gray-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between">
|
<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 className="grid grid-cols-2 md:grid-cols-4 gap-3 text-xs">
|
||||||
<div>
|
<div>
|
||||||
<span className="text-gray-600">Planlanan Süre:</span>
|
<span className="text-gray-600">Planlanan Süre:</span>
|
||||||
<p className="font-medium">
|
<p className="font-medium">{activity.plannedDuration} dk</p>
|
||||||
{activity.plannedDuration} dk
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-gray-600">Gerçek Süre:</span>
|
<span className="text-gray-600">Gerçek Süre:</span>
|
||||||
<p className="font-medium">
|
<p className="font-medium">{activity.actualDuration} dk</p>
|
||||||
{activity.actualDuration} dk
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-gray-600">Yapan:</span>
|
<span className="text-gray-600">Yapan:</span>
|
||||||
<p className="font-medium">
|
<p className="font-medium">{activity.performedBy || 'Atanmadı'}</p>
|
||||||
{activity.performedBy || "Atanmadı"}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
{activity.completedAt && (
|
{activity.completedAt && (
|
||||||
<div>
|
<div>
|
||||||
<span className="text-gray-600">Tamamlanma:</span>
|
<span className="text-gray-600">Tamamlanma:</span>
|
||||||
<p className="font-medium">
|
<p className="font-medium">
|
||||||
{activity.completedAt.toLocaleDateString("tr-TR")}
|
{activity.completedAt.toLocaleDateString('tr-TR')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -376,25 +345,19 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
<tr key={material.id}>
|
<tr key={material.id}>
|
||||||
<td className="px-3 py-2 text-sm">
|
<td className="px-3 py-2 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-gray-900">
|
<p className="font-medium text-gray-900">{material.materialCode}</p>
|
||||||
{material.materialCode}
|
<p className="text-gray-600">{material.materialName}</p>
|
||||||
</p>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
{material.materialName}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 text-sm text-gray-900">
|
<td className="px-3 py-2 text-sm text-gray-900">
|
||||||
{material.plannedQuantity}
|
{material.plannedQuantity}
|
||||||
</td>
|
</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">
|
<td className="px-3 py-2 text-sm text-gray-900">
|
||||||
{material.actualQuantity}
|
₺{material.unitCost.toLocaleString('tr-TR')}
|
||||||
</td>
|
|
||||||
<td className="px-3 py-2 text-sm text-gray-900">
|
|
||||||
₺{material.unitCost.toLocaleString("tr-TR")}
|
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 text-sm font-medium text-gray-900">
|
<td className="px-3 py-2 text-sm font-medium text-gray-900">
|
||||||
₺{material.totalCost.toLocaleString("tr-TR")}
|
₺{material.totalCost.toLocaleString('tr-TR')}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
|
@ -408,7 +371,7 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
Toplam Malzeme Maliyeti
|
Toplam Malzeme Maliyeti
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 text-sm font-bold text-gray-900">
|
<td className="px-3 py-2 text-sm font-bold text-gray-900">
|
||||||
₺{getTotalMaterialCost().toLocaleString("tr-TR")}
|
₺{getTotalMaterialCost().toLocaleString('tr-TR')}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
|
|
@ -420,18 +383,14 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
{/* Notes */}
|
{/* Notes */}
|
||||||
{(workOrder.notes || workOrder.completionNotes) && (
|
{(workOrder.notes || workOrder.completionNotes) && (
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h4 className="text-base font-semibold text-gray-900 mb-2">
|
<h4 className="text-base font-semibold text-gray-900 mb-2">Notlar</h4>
|
||||||
Notlar
|
|
||||||
</h4>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{workOrder.notes && (
|
{workOrder.notes && (
|
||||||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3">
|
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3">
|
||||||
<div className="flex items-start space-x-2">
|
<div className="flex items-start space-x-2">
|
||||||
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
<FaExclamationTriangle className="w-5 h-5 text-yellow-600 mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<h5 className="font-medium text-yellow-800 mb-1">
|
<h5 className="font-medium text-yellow-800 mb-1">İş Emri Notları</h5>
|
||||||
İş Emri Notları
|
|
||||||
</h5>
|
|
||||||
<p className="text-yellow-700">{workOrder.notes}</p>
|
<p className="text-yellow-700">{workOrder.notes}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -442,12 +401,8 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
<div className="flex items-start space-x-2">
|
<div className="flex items-start space-x-2">
|
||||||
<FaCheck className="w-5 h-5 text-green-600 mt-0.5" />
|
<FaCheck className="w-5 h-5 text-green-600 mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<h5 className="font-medium text-green-800 mb-1">
|
<h5 className="font-medium text-green-800 mb-1">Tamamlanma Notları</h5>
|
||||||
Tamamlanma Notları
|
<p className="text-green-700">{workOrder.completionNotes}</p>
|
||||||
</h5>
|
|
||||||
<p className="text-green-700">
|
|
||||||
{workOrder.completionNotes}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -462,22 +417,22 @@ const ViewWorkOrderModal: React.FC<ViewWorkOrderModalProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<span>Oluşturulma:</span>
|
<span>Oluşturulma:</span>
|
||||||
<span className="ml-2 font-medium">
|
<span className="ml-2 font-medium">
|
||||||
{workOrder.creationTime.toLocaleDateString("tr-TR")}{" "}
|
{workOrder.creationTime.toLocaleDateString('tr-TR')}{' '}
|
||||||
{workOrder.creationTime.toLocaleTimeString("tr-TR")}
|
{workOrder.creationTime.toLocaleTimeString('tr-TR')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>Son Güncelleme:</span>
|
<span>Son Güncelleme:</span>
|
||||||
<span className="ml-2 font-medium">
|
<span className="ml-2 font-medium">
|
||||||
{workOrder.lastModificationTime.toLocaleDateString("tr-TR")}{" "}
|
{workOrder.lastModificationTime.toLocaleDateString('tr-TR')}{' '}
|
||||||
{workOrder.lastModificationTime.toLocaleTimeString("tr-TR")}
|
{workOrder.lastModificationTime.toLocaleTimeString('tr-TR')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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