import React, { useState, useMemo } from "react"; import { FaUser, FaChevronDown, FaChevronRight, FaChevronLeft, FaChevronRight as FaArrowRight, } from "react-icons/fa"; import { PsGanttTask } from "../../../types/ps"; import { mockEmployees } from "../../../mocks/mockEmployees"; import { mockProductionOrders } from "../../../mocks/mockProductionOrders"; import { mockWorkOrders } from "../../../mocks/mockWorkOrders"; import { mockWorkCenters } from "../../../mocks/mockWorkCenters"; import { PriorityEnum } from "../../../types/common"; import { getProductionOrderStatus, getWorkOrderStatus, } from "../../../utils/erp"; import { Container } from "@/components/shared"; interface PlanningGanttProps { workCenterId?: string; } const PlanningGantt: React.FC = ({ workCenterId, }) => { const getInitialExpandedItems = () => { const expandedItems = new Set(); const firstTwoOrders = mockProductionOrders.slice(0, 2); firstTwoOrders.forEach((order) => { expandedItems.add(`prod-${order.id}`); const orderWorkOrders = mockWorkOrders.filter( (wo) => wo.productionOrderId === order.id ); orderWorkOrders.forEach((wo) => { expandedItems.add(`work-${wo.id}`); }); }); return expandedItems; }; const [expandedItems, setExpandedItems] = useState>( getInitialExpandedItems() ); const [selectedWorkCenter, setSelectedWorkCenter] = useState( workCenterId || "" ); const [viewMode, setViewMode] = useState<"day" | "week" | "month" | "year">( "week" ); const [currentDate, setCurrentDate] = useState(new Date()); const generateDateRange = () => { const startDate = new Date(currentDate); if (viewMode === "day") { const hours = []; for (let i = 0; i < 24; i++) { const hour = new Date(startDate); hour.setHours(i, 0, 0, 0); hours.push(hour); } return hours; } else if (viewMode === "week") { const weekStart = new Date(startDate); const dayOfWeek = weekStart.getDay(); const daysToSubtract = dayOfWeek === 0 ? 6 : dayOfWeek - 1; weekStart.setDate(weekStart.getDate() - daysToSubtract); weekStart.setHours(0, 0, 0, 0); const dates = []; for (let i = 0; i < 7; i++) { const date = new Date(weekStart); date.setDate(weekStart.getDate() + i); dates.push(date); } return dates; } else if (viewMode === "month") { const monthStart = new Date( currentDate.getFullYear(), currentDate.getMonth(), 1 ); const daysInMonth = new Date( currentDate.getFullYear(), currentDate.getMonth() + 1, 0 ).getDate(); const dates = []; for (let i = 0; i < daysInMonth; i++) { const date = new Date(monthStart); date.setDate(monthStart.getDate() + i); dates.push(date); } return dates; } else { const yearStart = new Date(currentDate.getFullYear(), 0, 1); const months = []; for (let i = 0; i < 12; i++) { const month = new Date(yearStart); month.setMonth(i); months.push(month); } return months; } }; const dateRange = generateDateRange(); const filteredData = useMemo(() => { const createGanttData = (): PsGanttTask[] => { return mockProductionOrders.map((order) => { const workOrders = mockWorkOrders .filter((wo) => wo.productionOrderId === order.id) .map((wo) => ({ id: `work-${wo.id}`, name: wo.operation?.name || wo.workOrderNumber, type: "task" as const, startDate: wo.plannedStartDate, endDate: wo.plannedEndDate, progress: Math.round((wo.confirmedQuantity / wo.plannedQuantity) * 100) || 0, assignee: wo.assignedOperators[0] ? mockEmployees.find((e) => e.id === wo.assignedOperators[0]) : undefined, status: getWorkOrderStatus(wo.status), priority: order.priority, level: 1, children: [], estimatedHours: wo.processTime / 60, hoursWorked: wo.actualProcessTime ? wo.actualProcessTime / 60 : 0, })); return { id: `prod-${order.id}`, name: `${order.orderNumber} - ${order.materials .map((m) => m.material?.name) .join(", ")}`, type: "project" as const, startDate: order.plannedStartDate, endDate: order.plannedEndDate, progress: order.confirmedQuantity && order.plannedQuantity ? Math.round( (order.confirmedQuantity / order.plannedQuantity) * 100 ) : 0, status: getProductionOrderStatus(order.status), priority: order.priority, level: 0, children: workOrders, }; }); }; const filterByWorkCenter = (tasks: PsGanttTask[]): PsGanttTask[] => { if (!selectedWorkCenter) return tasks; return tasks .map((task) => { if ( task.type === "task" && task.assignee?.id !== selectedWorkCenter ) { return null; } if (task.children) { const filteredChildren = filterByWorkCenter(task.children).filter( Boolean ) as PsGanttTask[]; if (filteredChildren.length > 0) { return { ...task, children: filteredChildren }; } else if ( task.type === "task" && task.assignee?.id === selectedWorkCenter ) { return task; } return null; } return task; }) .filter(Boolean) as PsGanttTask[]; }; const allGanttTasks = createGanttData(); return filterByWorkCenter(allGanttTasks); }, [selectedWorkCenter]); const toggleExpand = (id: string) => { const newExpanded = new Set(expandedItems); if (newExpanded.has(id)) { newExpanded.delete(id); } else { newExpanded.add(id); } setExpandedItems(newExpanded); }; const getPriorityColor = (priority: PriorityEnum) => { switch (priority) { case PriorityEnum.High: return "text-red-600 bg-red-50 border-red-200"; case PriorityEnum.Normal: return "text-blue-600 bg-blue-50 border-blue-200"; case PriorityEnum.Low: return "text-green-600 bg-green-50 border-green-200"; default: return "text-gray-600 bg-gray-50 border-gray-200"; } }; const navigatePrevious = () => { const newDate = new Date(currentDate); if (viewMode === "day") newDate.setDate(newDate.getDate() - 1); else if (viewMode === "week") newDate.setDate(newDate.getDate() - 7); else if (viewMode === "month") newDate.setMonth(newDate.getMonth() - 1); else newDate.setFullYear(newDate.getFullYear() - 1); setCurrentDate(newDate); }; const navigateNext = () => { const newDate = new Date(currentDate); if (viewMode === "day") newDate.setDate(newDate.getDate() + 1); else if (viewMode === "week") newDate.setDate(newDate.getDate() + 7); else if (viewMode === "month") newDate.setMonth(newDate.getMonth() + 1); else newDate.setFullYear(newDate.getFullYear() + 1); setCurrentDate(newDate); }; const navigateToday = () => { setCurrentDate(new Date()); }; const getCurrentDateInfo = () => { if (viewMode === "day") { return currentDate.toLocaleDateString("tr-TR", { weekday: "long", year: "numeric", month: "long", day: "numeric", }); } else if (viewMode === "week") { const weekStart = new Date(currentDate); const dayOfWeek = weekStart.getDay(); const daysToSubtract = dayOfWeek === 0 ? 6 : dayOfWeek - 1; weekStart.setDate(weekStart.getDate() - daysToSubtract); const weekEnd = new Date(weekStart); weekEnd.setDate(weekStart.getDate() + 6); return `${weekStart.toLocaleDateString("tr-TR", { day: "numeric", month: "short", })} - ${weekEnd.toLocaleDateString("tr-TR", { day: "numeric", month: "short", year: "numeric", })}`; } else if (viewMode === "month") { return currentDate.toLocaleDateString("tr-TR", { year: "numeric", month: "long", }); } else { return currentDate.toLocaleDateString("tr-TR", { year: "numeric" }); } }; const calculateTaskPosition = (startDate: Date, endDate: Date) => { const chartStart = dateRange[0]; const chartEnd = dateRange[dateRange.length - 1]; const taskStart = new Date(startDate); const taskEnd = new Date(endDate); if (viewMode === "day") { const dayStart = new Date(chartStart); dayStart.setHours(0, 0, 0, 0); const dayEnd = new Date(chartStart); dayEnd.setHours(23, 59, 59, 999); if (taskEnd < dayStart || taskStart > dayEnd) return { left: "100%", width: "0%", isVisible: false }; const startHour = taskStart > dayStart ? taskStart.getHours() + taskStart.getMinutes() / 60 : 0; const endHour = taskEnd < dayEnd ? taskEnd.getHours() + taskEnd.getMinutes() / 60 : 24; const left = (startHour / 24) * 100; const width = Math.max(1, ((endHour - startHour) / 24) * 100); return { left: `${left}%`, width: `${width}%`, isVisible: true }; } else if (viewMode === "week") { const weekStart = new Date(chartStart); const weekEnd = new Date(chartEnd); weekEnd.setHours(23, 59, 59, 999); if (taskEnd < weekStart || taskStart > weekEnd) return { left: "100%", width: "0%", isVisible: false }; const startDay = taskStart > weekStart ? Math.floor( (taskStart.getTime() - weekStart.getTime()) / (1000 * 60 * 60 * 24) ) : 0; const endDay = taskEnd < weekEnd ? Math.ceil( (taskEnd.getTime() - weekStart.getTime()) / (1000 * 60 * 60 * 24) ) : 7; const left = (startDay / 7) * 100; const width = Math.max(1, ((endDay - startDay) / 7) * 100); return { left: `${left}%`, width: `${width}%`, isVisible: true }; } else if (viewMode === "month") { const monthStart = new Date(chartStart); const monthEnd = new Date(chartEnd); monthEnd.setHours(23, 59, 59, 999); if (taskEnd < monthStart || taskStart > monthEnd) return { left: "100%", width: "0%", isVisible: false }; const daysInMonth = dateRange.length; const startDay = taskStart > monthStart ? Math.floor( (taskStart.getTime() - monthStart.getTime()) / (1000 * 60 * 60 * 24) ) : 0; const endDay = taskEnd < monthEnd ? Math.ceil( (taskEnd.getTime() - monthStart.getTime()) / (1000 * 60 * 60 * 24) ) : daysInMonth; const left = (startDay / daysInMonth) * 100; const width = Math.max(1, ((endDay - startDay) / daysInMonth) * 100); return { left: `${left}%`, width: `${width}%`, isVisible: true }; } else { const yearStart = new Date(chartStart); const yearEnd = new Date(chartEnd); yearEnd.setMonth(11, 31); yearEnd.setHours(23, 59, 59, 999); if (taskEnd < yearStart || taskStart > yearEnd) return { left: "100%", width: "0%", isVisible: false }; const startMonth = taskStart > yearStart ? taskStart.getMonth() : 0; const endMonth = taskEnd < yearEnd ? taskEnd.getMonth() + 1 : 12; const left = (startMonth / 12) * 100; const width = Math.max(1, ((endMonth - startMonth) / 12) * 100); return { left: `${left}%`, width: `${width}%`, isVisible: true }; } }; const renderTask = (task: PsGanttTask): React.ReactNode => { const hasChildren = task.children && task.children.length > 0; const isExpanded = expandedItems.has(task.id); const indent = task.level * 20; return (
{/* Task Info */}
{hasChildren && ( )} {!hasChildren &&
}
{task.name}
{task.type === "task" && task.assignee && (
{task.assignee.firstName} {task.assignee.lastName} {task.hoursWorked && task.estimatedHours && ( {task.hoursWorked}h / {task.estimatedHours}h )}
)}
{task.startDate.toLocaleDateString("tr-TR")} -{" "} {task.endDate.toLocaleDateString("tr-TR")}
{/* Gantt Chart */}
{(() => { const position = calculateTaskPosition( task.startDate, task.endDate ); if (task.type === "task" && position.isVisible) { return (
{task.progress}%
{task.name}
İlerleme: {task.progress}% tamamlandı
Planlanan Başlangıç:{" "} {task.startDate.toLocaleDateString("tr-TR")}
Planlanan Bitiş:{" "} {task.endDate.toLocaleDateString("tr-TR")}
); } if (task.type !== "task" && position.isVisible) { return (
{task.name}
Planlanan Başlangıç:{" "} {task.startDate.toLocaleDateString("tr-TR")}
Planlanan Bitiş:{" "} {task.endDate.toLocaleDateString("tr-TR")}
); } return null; })()}
{hasChildren && isExpanded && task.children?.map((child) => renderTask(child))}
); }; return (

Planlama Gantt Şeması

Üretim ve iş emirlerinizi zaman çizelgesinde yönetin.

{getCurrentDateInfo()}
Üretim Emri / İş Emri
{dateRange.map((date) => (
{viewMode === "day" ? date.toLocaleTimeString("tr-TR", { hour: "2-digit", minute: "2-digit", }) : viewMode === "week" ? date.toLocaleDateString("tr-TR", { day: "2-digit" }) : viewMode === "year" ? date.toLocaleDateString("tr-TR", { month: "short" }) : date.toLocaleDateString("tr-TR", { day: "2-digit" })}
{viewMode !== "day" && viewMode !== "year" && (
{date.toLocaleDateString("tr-TR", { weekday: "short" })}
)} {viewMode === "year" && (
{date.getFullYear()}
)}
))}
{filteredData.map((task) => renderTask(task))}
); }; export default PlanningGantt;