import React, { useState, useEffect, useCallback } from 'react' import { useNavigate, useParams, Link } from 'react-router-dom' import { FaSave, FaTimes, FaArrowLeft, FaFolder, FaBullseye, FaFlag, FaTasks, FaFileAlt, FaExclamationCircle, FaChartLine, FaPlus, FaEdit, FaTrash, FaBuilding, FaPhone, FaEnvelope, FaClock, FaUser, FaDollarSign, FaEye, FaDownload, } from 'react-icons/fa' import LoadingSpinner from '../../../components/common/LoadingSpinner' import { mockEmployees } from '../../../mocks/mockEmployees' import { mockBusinessParties } from '../../../mocks/mockBusinessParties' import { PsProject, ProjectStatusEnum, ProjectTypeEnum, PsProjectPhase, PsProjectTask, PsProjectRisk, PsProjectDocument, TaskStatusEnum, PsDocumentTypeEnum, RiskCategoryEnum, RiskProbabilityEnum, RiskImpactEnum, RiskLevelEnum, RiskStatusEnum, } from '../../../types/ps' import { mockProjects } from '../../../mocks/mockProjects' import { mockProjectPhases } from '../../../mocks/mockProjectPhases' import { mockProjectTasks } from '../../../mocks/mockProjectTasks' import dayjs from 'dayjs' import classNames from 'classnames' import PhaseEditModal from './PhaseEditModal' import TaskModal, { TaskFormData } from './TaskEditModal' import DocumentUploadModal from './DocumentUploadModal' import DocumentViewerModal from './DocumentViewerModal' import RiskModal from './RiskModal' import { BusinessParty, PriorityEnum } from '../../../types/common' import { getProjectStatusColor, getProjectStatusIcon, getProjectStatusText, getProgressColor, getPriorityColor, getPriorityText, getProjectTypeColor, getProjectTypeText, } from '../../../utils/erp' import { Container } from '@/components/shared' import { ROUTES_ENUM } from '@/routes/route.constant' import { mockCurrencies } from '@/mocks/mockCurrencies' import { EmployeeDto } from '@/proxy/intranet/models' // Custom styles for the slider const sliderStyles = ` input[type="range"] { -webkit-appearance: none; appearance: none; height: 8px; border-radius: 4px; outline: none; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 20px; height: 20px; border-radius: 50%; background: #3b82f6; border: 2px solid #ffffff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); cursor: pointer; } input[type="range"]::-moz-range-thumb { width: 20px; height: 20px; border-radius: 50%; background: #3b82f6; border: 2px solid #ffffff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); cursor: pointer; } ` interface ValidationErrors { [key: string]: string } const ProjectForm: React.FC = () => { const navigate = useNavigate() const { id } = useParams<{ id: string }>() const isEdit = Boolean(id) const [loading, setLoading] = useState(false) const [saving, setSaving] = useState(false) const [errors, setErrors] = useState({}) const [customers, setCustomers] = useState([]) const [projectManagers, setProjectManagers] = useState([]) const [activeTab, setActiveTab] = useState('overview') // Additional states for the new features const [phases, setPhases] = useState([]) const [tasks, setTasks] = useState([]) const [risks, setRisks] = useState([]) const [documents, setDocuments] = useState([]) // Modal states const [isPhaseModalOpen, setIsPhaseModalOpen] = useState(false) const [editingPhase, setEditingPhase] = useState(null) const [isTaskModalOpen, setIsTaskModalOpen] = useState(false) const [editingTask, setEditingTask] = useState(null) // Document modal states const [isDocumentUploadModalOpen, setIsDocumentUploadModalOpen] = useState(false) const [isDocumentViewerModalOpen, setIsDocumentViewerModalOpen] = useState(false) const [selectedDocument, setSelectedDocument] = useState(null) // Risk modal states const [isRiskModalOpen, setIsRiskModalOpen] = useState(false) const [editingRisk, setEditingRisk] = useState(null) const [formData, setFormData] = useState({ id: '', code: '', name: '', description: '', projectType: ProjectTypeEnum.Internal, status: ProjectStatusEnum.Planning, priority: PriorityEnum.Low, customerId: '', customer: undefined, projectManagerId: '', projectManager: undefined, startDate: new Date(), endDate: new Date(), actualStartDate: undefined, actualEndDate: undefined, budget: 0, actualCost: 0, currency: 'TRY', progress: 0, phases: [], tasks: [], risks: [], documents: [], isActive: true, creationTime: new Date(), lastModificationTime: new Date(), }) const tabs = [ { id: 'overview', name: 'Genel Bakış', icon: FaChartLine }, { id: 'phases', name: 'Aşamalar', icon: FaFlag }, { id: 'tasks', name: 'Görevler', icon: FaTasks }, { id: 'documents', name: 'Belgeler', icon: FaFileAlt }, { id: 'risks', name: 'Riskler', icon: FaExclamationCircle }, { id: 'budget', name: 'Bütçe Bilgileri', icon: FaDollarSign }, ] const budgetUsagePercentage = formData.budget > 0 ? (formData.actualCost / formData.budget) * 100 : 0 const loadData = useCallback(async () => { try { setCustomers(mockBusinessParties) setProjectManagers(mockEmployees) if (isEdit && id) { // Load existing phases, tasks, risks, documents for the project const existingPhases = mockProjectPhases.filter((p) => p.projectId === id) const existingTasks = mockProjectTasks.filter((t) => t.projectId === id) setPhases(existingPhases) setTasks(existingTasks) // For now, using empty arrays for risks and documents setRisks([]) setDocuments([]) } } catch (error) { console.error('Error loading data:', error) } }, [isEdit, id]) const loadFormData = useCallback(async () => { setLoading(true) try { if (isEdit && id) { await new Promise((resolve) => setTimeout(resolve, 1000)) const mockProject = mockProjects.find((p) => p.id === id) if (mockProject) { setFormData(mockProject) } } } catch (error) { console.error('Error loading form data:', error) } finally { setLoading(false) } }, [isEdit, id]) useEffect(() => { loadData() loadFormData() }, [loadData, loadFormData]) // Add custom slider styles useEffect(() => { const style = document.createElement('style') style.textContent = sliderStyles document.head.appendChild(style) return () => { document.head.removeChild(style) } }, []) const validateForm = (): boolean => { const newErrors: ValidationErrors = {} if (!formData.code.trim()) { newErrors.projectCode = 'Proje kodu zorunludur' } if (!formData.name.trim()) { newErrors.name = 'Proje adı zorunludur' } if (!formData.projectType) { newErrors.projectType = 'Proje tipi seçilmelidir' } if (!formData.projectManagerId) { newErrors.projectManagerId = 'Proje yöneticisi seçilmelidir' } if (!formData.startDate) { newErrors.startDate = 'Başlangıç tarihi zorunludur' } if (!formData.endDate) { newErrors.endDate = 'Bitiş tarihi zorunludur' } if (formData.startDate && formData.endDate && formData.startDate >= formData.endDate) { newErrors.endDate = 'Bitiş tarihi başlangıç tarihinden sonra olmalıdır' } if (formData.budget <= 0) { newErrors.budget = "Bütçe 0'dan büyük olmalıdır" } setErrors(newErrors) return Object.keys(newErrors).length === 0 } const handleInputChange = (field: keyof PsProject, value: string | number | boolean | Date) => { setFormData((prev) => ({ ...prev, [field]: value, })) if (errors[field]) { setErrors((prev) => ({ ...prev, [field]: '', })) } } const handleDateChange = (field: keyof PsProject, value: string) => { const date = value ? new Date(value) : new Date() handleInputChange(field, date) } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!validateForm()) { return } setSaving(true) try { await new Promise((resolve) => setTimeout(resolve, 2000)) console.log('Project data:', { ...formData, id: isEdit ? id : undefined, }) alert(isEdit ? 'Proje başarıyla güncellendi!' : 'Proje başarıyla oluşturuldu!') navigate(ROUTES_ENUM.protected.projects.list) } catch (error) { console.error('Error saving project:', error) alert('Bir hata oluştu. Lütfen tekrar deneyin.') } finally { setSaving(false) } } const handleCancel = () => { navigate(ROUTES_ENUM.protected.projects.list) } // Phase modal handlers const openPhaseModal = () => { setEditingPhase(null) setIsPhaseModalOpen(true) } const openEditPhaseModal = (phase: PsProjectPhase) => { setEditingPhase(phase) setIsPhaseModalOpen(true) } const closePhaseModal = () => { setIsPhaseModalOpen(false) setEditingPhase(null) } const handlePhaseSubmit = (phaseData: PsProjectPhase) => { if (editingPhase) { // Edit existing phase const updatedPhases = phases.map((p) => p.id === editingPhase.id ? { ...editingPhase, name: phaseData.name, description: phaseData.description, projectId: phaseData.projectId, status: phaseData.status, startDate: new Date(phaseData.startDate), endDate: new Date(phaseData.endDate), budget: phaseData.budget, category: phaseData.category, assignedTeams: phaseData.assignedTeams, updatedAt: new Date(), } : p, ) setPhases(updatedPhases) alert('Aşama başarıyla güncellendi!') } else { // Create new phase const selectedProject = mockProjects.find((p) => p.id === phaseData.projectId) if (!selectedProject) { alert('Proje bulunamadı!') return } const newPhase: PsProjectPhase = { id: Date.now().toString(), code: `PH-${(phases.length + 1).toString().padStart(3, '0')}`, name: phaseData.name, description: phaseData.description, projectId: phaseData.projectId, project: selectedProject, status: phaseData.status, startDate: new Date(phaseData.startDate), endDate: new Date(phaseData.endDate), budget: phaseData.budget, actualCost: 0, progress: 0, category: phaseData.category, assignedTeams: phaseData.assignedTeams, deliverables: [], risks: [], milestones: 0, completedMilestones: 0, sequence: phases.length + 1, tasks: [], isActive: true, } setPhases([...phases, newPhase]) alert('Yeni aşama başarıyla oluşturuldu!') } closePhaseModal() } const handleDeletePhase = (phaseId: string) => { if (window.confirm('Bu aşamayı silmek istediğinizden emin misiniz?')) { setPhases(phases.filter((p) => p.id !== phaseId)) alert('Aşama başarıyla silindi!') } } // Task handlers const openTaskModal = () => { setEditingTask(null) setIsTaskModalOpen(true) } const openEditTaskModal = (task: PsProjectTask) => { setEditingTask(task) setIsTaskModalOpen(true) } const closeTaskModal = () => { setIsTaskModalOpen(false) setEditingTask(null) } const handleTaskSubmit = (taskData: TaskFormData) => { if (editingTask) { // Edit existing task const updatedTasks = tasks.map((t) => t.id === editingTask.id ? { ...editingTask, name: taskData.name, description: taskData.description, projectId: taskData.projectId, phaseId: taskData.phaseId, taskType: taskData.taskType, status: taskData.status, priority: taskData.priority, assignedTo: taskData.assignedTo, assignee: mockEmployees.find((emp) => emp.id === taskData.assignedTo), startDate: new Date(taskData.startDate), endDate: new Date(taskData.endDate), estimatedHours: taskData.estimatedHours, progress: taskData.progress, lastModificationTime: new Date(), } : t, ) setTasks(updatedTasks) alert('Görev başarıyla güncellendi!') } else { // Create new task const selectedProject = mockProjects.find((p) => p.id === taskData.projectId) const selectedPhase = mockProjectPhases.find((p) => p.id === taskData.phaseId) const selectedAssignee = mockEmployees.find((emp) => emp.id === taskData.assignedTo) if (!selectedProject) { alert('Proje bulunamadı!') return } const newTask: PsProjectTask = { id: Date.now().toString(), projectId: taskData.projectId, phaseId: taskData.phaseId, phase: selectedPhase, taskCode: `TSK-${(tasks.length + 1).toString().padStart(3, '0')}`, name: taskData.name, description: taskData.description, taskType: taskData.taskType, status: taskData.status, priority: taskData.priority, assignedTo: taskData.assignedTo, assignee: selectedAssignee, startDate: new Date(taskData.startDate), endDate: new Date(taskData.endDate), estimatedHours: taskData.estimatedHours, actualHours: 0, progress: 0, activities: [], comments: [], isActive: true, creationTime: new Date(), lastModificationTime: new Date(), } setTasks([...tasks, newTask]) alert('Yeni görev başarıyla oluşturuldu!') } closeTaskModal() } const handleDeleteTask = (taskId: string) => { if (window.confirm('Bu görevi silmek istediğinizden emin misiniz?')) { setTasks(tasks.filter((t) => t.id !== taskId)) alert('Görev başarıyla silindi!') } } // Document handlers const openDocumentUploadModal = () => { setIsDocumentUploadModalOpen(true) } const closeDocumentUploadModal = () => { setIsDocumentUploadModalOpen(false) } const handleDocumentUpload = (documentData: { documentName: string documentType: PsDocumentTypeEnum file: File }) => { // In a real application, you would upload the file to a server // For now, we'll create a mock URL and add it to the documents list const newDocument: PsProjectDocument = { id: Date.now().toString(), projectId: formData.id || 'temp-project-id', documentName: documentData.documentName, documentType: documentData.documentType, filePath: URL.createObjectURL(documentData.file), // Mock file path fileSize: Number((documentData.file.size / (1024 * 1024)).toFixed(2)), // Convert to MB uploadedBy: 'Current User', // In real app, get from auth context uploadedAt: new Date(), version: '1.0', isActive: true, } setDocuments([...documents, newDocument]) closeDocumentUploadModal() alert('Belge başarıyla yüklendi!') } const openDocumentViewer = (document: PsProjectDocument) => { setSelectedDocument(document) setIsDocumentViewerModalOpen(true) } const closeDocumentViewer = () => { setIsDocumentViewerModalOpen(false) setSelectedDocument(null) } const handleDocumentDownload = (document: PsProjectDocument) => { // In a real application, this would trigger a server download const link = window.document.createElement('a') link.href = document.filePath link.download = document.documentName link.click() } const handleDeleteDocument = (documentId: string) => { if (window.confirm('Bu belgeyi silmek istediğinizden emin misiniz?')) { setDocuments(documents.filter((d) => d.id !== documentId)) alert('Belge başarıyla silindi!') } } // Risk handlers const openRiskModal = () => { setEditingRisk(null) setIsRiskModalOpen(true) } const openEditRiskModal = (risk: PsProjectRisk) => { setEditingRisk(risk) setIsRiskModalOpen(true) } const closeRiskModal = () => { setIsRiskModalOpen(false) setEditingRisk(null) } const handleRiskSubmit = (riskData: Partial) => { if (editingRisk) { // Edit existing risk const updatedRisks = risks.map((r) => r.id === editingRisk.id ? { ...editingRisk, ...riskData, } : r, ) setRisks(updatedRisks) alert('Risk başarıyla güncellendi!') } else { // Create new risk const newRisk: PsProjectRisk = { id: Date.now().toString(), projectId: formData.id || 'temp-project-id', riskCode: `RISK-${(risks.length + 1).toString().padStart(3, '0')}`, title: riskData.title || '', description: riskData.description || '', category: riskData.category || RiskCategoryEnum.Technical, probability: riskData.probability || RiskProbabilityEnum.Medium, impact: riskData.impact || RiskImpactEnum.Medium, riskLevel: riskData.riskLevel || RiskLevelEnum.Medium, status: riskData.status || RiskStatusEnum.Identified, identifiedBy: 'Current User', // In real app, get from auth context identifiedDate: new Date(), mitigationPlan: riskData.mitigationPlan, contingencyPlan: riskData.contingencyPlan, ownerId: riskData.ownerId, reviewDate: riskData.reviewDate, isActive: true, } setRisks([...risks, newRisk]) alert('Yeni risk başarıyla eklendi!') } closeRiskModal() } const handleDeleteRisk = (riskId: string) => { if (window.confirm('Bu riski silmek istediğinizden emin misiniz?')) { setRisks(risks.filter((r) => r.id !== riskId)) alert('Risk başarıyla silindi!') } } if (loading) { return } return (
{/* Header */}
Geri

{isEdit ? formData.code : 'Yeni Proje'}

{isEdit ? formData.name : 'Proje bilgilerini girin'}

{isEdit && ( {getProjectStatusIcon(formData.status)} {getProjectStatusText(formData.status)} )}
{/* Summary Cards - Only show in edit mode */} {isEdit && (
{/* Progress Card */}
{formData.progress}%

İlerleme

{/* Budget Card */}
{budgetUsagePercentage.toFixed(0)}%

Bütçe Kullanımı

₺{formData.actualCost.toLocaleString()} / ₺{formData.budget.toLocaleString()}
{/* Duration Card */}
{dayjs(formData.endDate).diff(dayjs(formData.startDate), 'day')}

Süre (Gün)

{dayjs(formData.startDate).format('DD.MM.YYYY')} -{' '} {dayjs(formData.endDate).format('DD.MM.YYYY')}
{/* Priority Card */}
{getPriorityText(formData.priority)}

Öncelik

{getProjectTypeText(formData.projectType)}
)} {/* Tabs */}
{/* Tab Content */}
{activeTab === 'overview' && (
{/* Project Info Grid */}
{/* Project Details */}

Proje Detayları

handleInputChange('code', e.target.value)} className={`block w-full px-2.5 py-1.5 text-sm border rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 ${ errors.code ? 'border-red-300 focus:border-red-500' : 'border-gray-300 focus:border-blue-500' }`} placeholder="Örn: PRJ001" /> {errors.projectCode && (

{errors.projectCode}

)}
{errors.projectType && (

{errors.projectType}

)}
handleInputChange('name', e.target.value)} className={`block w-full px-2.5 py-1.5 text-sm border rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 ${ errors.name ? 'border-red-300 focus:border-red-500' : 'border-gray-300 focus:border-blue-500' }`} placeholder="Proje adı" /> {errors.name && (

{errors.name}

)}